EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: StreamDevice for an odd float format
From: "Davis, Mark via Tech-talk" <[email protected]>
To: "Brown, Garth" <[email protected]>, "[email protected]" <[email protected]>
Date: Wed, 5 Jun 2019 20:01:31 +0000
You can add your own format converters to StreamDevice.

John Priller ([email protected]) at our lab has written the code for several odd format like this (see attached code).  I believe the one you want is the %ZHF one.

Mark



Mark Davis NSCL/FRIB Control Systems Software Engineer [email protected]
On 6/5/2019 1:58 PM, Brown, Garth via Tech-talk wrote:
Hi All,
I'm trying to use StreamDevice to talk to a device that is almost, but not quite, giving me data that the format converters (as I understand them) can convert.
It’s an ASCII representation of the IEEE 32 bit floating point format, e.g. the raw data for a value of 28.0 is

0x34 0x31 0x45 0x30 0x30 0x30 0x30 0x30

Which is the ASCII code for

41E00000

Which is the IEEE-754 floating point representation of 28.0.
I think that if the raw data were 0x41E00000, %R would be able to convert it. And if the raw data were
0x32 0x38 0x2E 0x30 (ASCII for 28.0)
then %f would be able to convert it.
Before I start writing calc records to convert the 32 bits to a float value, I figured I'd ask if someone had a less brute-force approach. Maybe a StreamDevice format converter trick I don't know about?

Thanks,
Garth

 /*
  NSCL StreamDevice format converters

  Uses 'Z' stream format character.  The following are provided:

    %ZDC(s,n)

        decimal checksum (sum of bytes mod 256)
            s : starting position in read/write buffer, 0=beginning
            n : number of characters to calculate checksum for

        Needed by Pfeiffer vacuum protocol

        width and 0-pad format spec/flag are honored
        example: %03ZDC would be 3-character string, 0-padded

    %ZDS

        HA5/DS1820 thermocouple format (hex-coded "scratchpad" buffer)

    %ZHF

        Hex-coded 8-character float (4 bytes)

    %[-][size]ZHL

        Hex-coded long
            - : treat as signed (for < 8 chars, 8-char longs are always signed)
            size is number of chars to read, '0F1A' = 4 chars (2 hex-coded bytes)
            if size not specified goes to whichever encounters first
               8 characters read (4 bytes)
               end of input
               first non hex-coded char pair

    %[#][size]ZHS

        Hex-coded ascii text string
            # : trim leading/trailing whitespace (on reads only)
            size is number of chars to read, '6533' = 4 chars (2 hex-coded bytes)
            if size not specified goes to whichever encounters first
               end of input
               first non hex-coded char pair
               hex-coded NULL (00 pair) (consumed)

    %ZSR(a,d,m)

        SMDP (Sycon Multi-drop Prococol) send or receive string for reading values
            a : 2-hex-char address of slave device (10 .. FE)
            d : 6-hex-char code for dictionary entry desired
            m : 2-hex-char message id to send/expect
        always returns a signed 32-bit integer

    %ZC8(s,n,p)

        Generic 1-byte CRC
            s : starting position in read/write buffer, 0=beginning
            n : number of characters to calculate CRC for
            p : 2-char hex polynomial ('18' = 0x18)

  History:
    2015-02-26  J. Priller  added ZDC
    2012-11-16  J. Priller  added zFormatData so not parsing every call to scan/print
    2012-11-14  J. Priller  added ZC8
    2012-06-06  J. Priller  added ZSR
    2011-12-15  J. Priller  original version
*/

#include <ctype.h>
#include <stdlib.h>

#include "StreamError.h"
#include "StreamFormatConverter.h"
#include <nsclfribCommon.h>

#define IS_NAME_CHAR(cc) (((cc >= 'a') && (cc <= 'z')) || ((cc >= 'A') && (cc <= 'Z')) || ((cc >= '0') && (cc <= '9')))

// detect if Stream > 2.7, different types defined
#include "devStream.h"
#define STREAM_VERSION_LONG ( STREAM_MAJOR*10000 + STREAM_MINOR*100 + STREAM_PATCHLEVEL )
#if (STREAM_VERSION_LONG >= 20700)
  #define LONG_FORMAT  signed_format
#else
  #define LONG_FORMAT  long_format
#endif

//----------------------------------------------------------------------
// internal routines
//----------------------------------------------------------------------
static void trimString(char *str)
{
  // eliminate leading whitespace
  int jj = 0;
  while ((str[jj] == ' ') || (str[jj] == 0x09)) jj++;
  if (jj > 0) memmove(str,str+jj,strlen(str)-jj+1);
  // eliminate trailing whitespace
  int xx = strlen(str) - 1;
  while ((xx >= 0) && ((str[xx] == ' ') || (str[xx] == 0x09))) {
    str[xx] = 0;
    xx--;
  }
}
//----------------------------------------------------------------------
static void toHex(unsigned char c, char *str)
{
  int hsn = (c & 0x00F0) >> 4;
  int lsn = (c & 0x000F);
  str[0] = (hsn <= 9) ? (hsn + '0') : (hsn - 10 + 'A');
  str[1] = (lsn <= 9) ? (lsn + '0') : (lsn - 10 + 'A');
  str[2] = 0;
}
//----------------------------------------------------------------------
static int unhexChar(const char ch)
{
  if ((ch >= '0') && (ch <= '9')) return(ch - '0');
  if ((ch >= 'A') && (ch <= 'F')) return(ch - 'A' + 10);
  if ((ch >= 'a') && (ch <= 'f')) return(ch - 'a' + 10);
  return(-1);  // error
}
//----------------------------------------------------------------------
static bool unhexString(const char *pc, int count, unsigned long *value)
{
  (*value) = 0;
  unsigned long N = 0;
  for (int ii = 0; ii < count; ii++) {
    int cv = unhexChar(pc[ii]);
    if (cv < 0) {
      error("invalid hexcode char (%c) in input\n",pc[ii]);
      return(false);
    }
    N <<= 4;
    N |= cv;
  }
  (*value) = N;
  return(true);
}
//----------------------------------------------------------------------
static bool unhexPair(const char *pc, unsigned char *value)
{
  unsigned long V;
  if (! unhexString(pc,2,&V)) return(false);
  (*value) = (V & 0x00FF);
  return(true);
}
//----------------------------------------------------------------------
static bool isHexPair(const char *pc)
{
  if (unhexChar(*pc) < 0) return(false);
  pc++;
  if (unhexChar(*pc) < 0) return(false);
  return(true);
}
//----------------------------------------------------------------------
static void encodeHex(const char *pc, int count, StreamBuffer& info)
{
  for (int ii = 0; ii < count; ii++) {
    char tmp[16];
    toHex(pc[ii],tmp);
    info.append(tmp);
  }
}
//----------------------------------------------------------------------
static bool decodeHex(const StreamBuffer& info, int count, char *pc)
{
  int idx = 0;
  int kk = 0;
  while (idx < count) {
    char hvCh = info[idx++];
    char lvCh = info[idx++];
    int HV = unhexChar(hvCh);
    if (HV < 0) {
      error("invalid hex nibble '%c' [%d]\n",hvCh,hvCh);
      return(false);
    }
    int LV = unhexChar(lvCh);
    if (LV < 0) {
      error("invalid hex nibble '%c' [%d]\n",lvCh,lvCh);
      return(false);
    }
    pc[kk++] = (char) (((HV << 4) | (LV & 0x0F)) & 0x00FF);
  }
  return(true);
}
//----------------------------------------------------------------------
static bool unnumString(const char *pc, unsigned long *value)
{
  char *ei;
  (*value) = strtol(pc,&ei,0);
  return( ((*ei) == 0) ? true : false );  // okay if consumed all chars
}
//----------------------------------------------------------------------
static bool unnumStringDecimal(const char *pc, unsigned long *value)
{
  char *ei;
  (*value) = strtol(pc,&ei,10);
  return( ((*ei) == 0) ? true : false );  // okay if consumed all chars
}
//----------------------------------------------------------------------
static unsigned char genericCRC8(char *pc, int startpos, int count, int poly)
{
  unsigned char crc = 0;
  for (int ii = 0; ii < count; ii++) {
    unsigned char data = pc[startpos+ii];
    for (int bb = 0; bb < 8; bb++) {
      if ((crc ^ data) & 0x01) {
        crc = crc ^ poly;
        crc = crc >> 1;
        crc = crc | 0x80;
      } else {
        crc = crc >> 1;
        crc = crc & 0x7f;
      }
      data = data >> 1;
    }
  }
  return(crc);
}
//----------------------------------------------------------------------
static unsigned char checksum256(char *pc, int startpos, int count)
{
  unsigned int sum = 0;
  for (int ii = 0; ii < count; ii++) {
    unsigned char data = pc[startpos+ii];
    sum += data;
  }
  return( (unsigned char) (sum % 256) );
}
//----------------------------------------------------------------------
static void getFormatName(const char *str, char *name)
{
  int kk = 0;
  while (IS_NAME_CHAR(str[kk])) {
    name[kk] = str[kk];
    kk++;
  }
  name[kk] = 0;
}
//----------------------------------------------------------------------

#define ZFORMAT_FIRST  0x0aa00001

#define ZFORMAT_ZDS    0x0aa00001
#define ZFORMAT_ZHF    0x0aa00002
#define ZFORMAT_ZHL    0x0aa00003
#define ZFORMAT_ZHS    0x0aa00004
#define ZFORMAT_ZSR    0x0aa00005
#define ZFORMAT_ZC8    0x0aa00006
#define ZFORMAT_ZDC    0x0aa00007

#define ZFORMAT_LAST   0x0aa00007

typedef struct {
  unsigned char addr;
  unsigned long dict;
  unsigned char msgid;
} ZSRData;

typedef struct {
  int startpos;
  int count;
  int poly;
} ZC8Data;

typedef struct {
  int startpos;
  int count;
} ZDCData;

typedef struct {
  unsigned long zFormat;
  union {
    ZSRData zsr;
    ZC8Data zc8;
    ZDCData zdc;
  } U;
} zFormatData;

//----------------------------------------------------------------------
// NSCLConverter
//----------------------------------------------------------------------
class NSCLConverter : public StreamFormatConverter
{
  virtual int parse(const StreamFormat& fmt, StreamBuffer& info, const char*& source, bool scanFormat);
  virtual int scanLong(const StreamFormat& fmt, const char* input, long& value);
  virtual int scanDouble(const StreamFormat& fmt, const char* input, double& value);
  virtual int scanString(const StreamFormat& fmt, const char* input, char* value, size_t maxlen);
  virtual int scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor);

  virtual bool printLong(const StreamFormat& fmt, StreamBuffer& output, long value);
  virtual bool printDouble(const StreamFormat& fmt, StreamBuffer& output, double value);
  virtual bool printString(const StreamFormat& fmt, StreamBuffer& output, const char* value);
  virtual bool printPseudo(const StreamFormat& fmt, StreamBuffer& output);

  private:
    bool parse_ZSR(const char *source,
                   unsigned char *addr,
                   unsigned long *dict,
                   unsigned char *msgid,
                   int *slen);
    bool parse_ZC8(const char *source,
                   int *startpos,
                   int *count,
                   int *poly,
                   int *slen);
    bool parse_ZDC(const char *source,
                   int *startpos,
                   int *count,
                   int *slen);
    void putFormatData(StreamBuffer& info, zFormatData *zdata);
    bool getFormatData(const StreamBuffer& info, zFormatData *zdata);
};
//----------------------------------------------------------------------
void NSCLConverter::putFormatData(StreamBuffer& info, zFormatData *zdata)
{
  // clear info
  info.clear();

  // add data from structure, coding so all chars are "printable"
  encodeHex((char *)zdata,sizeof(zFormatData),info);
};
//----------------------------------------------------------------------
bool NSCLConverter::getFormatData(const StreamBuffer& info, zFormatData *zdata)
{
  // size must be correct
  if (info.length() != sizeof(zFormatData)*2) {
    error("info length %ld expecting %d\n",info.length(),(int)sizeof(zFormatData));
    nsclfrib_showBuffer((unsigned char *)info(0),info.length());
    return(false);
  }

  // decode structure bytes
  if (! decodeHex(info,info.length(),(char *)zdata)) {
    error("decode hex failed\n");
    nsclfrib_showBuffer((unsigned char *)info(0),info.length());
    return(false);
  }

  // format id must be valid
  if ((zdata->zFormat < ZFORMAT_FIRST) || (zdata->zFormat > ZFORMAT_LAST)) {
    error("invalid Z format id 0x%08lx\n",zdata->zFormat);
    return(false);
  }

  // othewise we're good
  return(true);
}
//----------------------------------------------------------------------
bool NSCLConverter::parse_ZC8
     (const char *source,
      int *startpos,
      int *count,
      int *poly,
      int *slen)
{
  unsigned long UL;
  char buff[256];
  strcpy(buff,source);

  (*slen) = 0;
  char *pc = buff;

  // must start with (
  if ((*pc) != '(') {
    error("invalid ZC8 format '%s' : want (s,n,p)\n",source);
    return(false);
  }
  pc++;

  // find ,
  char *idx = strchr(pc,',');
  if (! idx) {
    error("invalid ZC8 format '%s' : want (s,n,p)\n",source);
    return(false);
  }
  // get startpos
  (*idx) = 0;
  if (! unnumString(pc,&UL)) {
    error("invalid ZC8 format '%s' : want (s,n,p)\n",source);
    return(false);
  }
  (*startpos) = UL;
  pc = idx + 1;

  // find ,
  idx = strchr(pc,',');
  if (! idx) {
    error("invalid ZC8 format '%s' : want (s,n,p)\n",source);
    return(false);
  }
  // get count
  (*idx) = 0;
  if (! unnumString(pc,&UL)) {
    error("invalid ZC8 format '%s' : want (s,n,p)\n",source);
    return(false);
  }
  (*count) = UL;
  pc = idx + 1;

  // find ')'
  idx = strchr(pc,')');
  if (! idx) {
    error("invalid ZC8 format '%s' : want (s,n,p)\n",source);
    return(false);
  }
  // get polynomial
  (*idx) = 0;
  if (! unnumString(pc,&UL)) {
    error("invalid ZC8 format '%s' : want (s,n,p)\n",source);
    return(false);
  }
  (*poly) = UL;
  pc = idx + 1;

  // set slen
  (*slen) = (pc - buff);

  return(true);
}
//----------------------------------------------------------------------
bool NSCLConverter::parse_ZDC
     (const char *source,
      int *startpos,
      int *count,
      int *slen)
{
  unsigned long UL;
  char buff[256];
  strcpy(buff,source);

  (*startpos) = 0;
  (*count)    = 0;
  (*slen)     = 0;
  char *pc = buff;

  // if next char isn't '(', we're using just the defaults
  if ((*pc) != '(') return(true);

  // grab chars until we get a non-digit, this is startpos
  pc++;
  char *si = pc;
  while (isdigit(*pc)) pc++;
  char keep = *pc;
  (*pc) = 0;
  if (! unnumString(si,&UL)) {
    error("invalid ZDC format '%s' : want [(s[,n])]\n",source);
    return(false);
  }
  (*startpos) = UL;
  (*pc) = keep;

  // if char after number is a ')' we're done
  if ((*pc) == ')') {
    pc++;
    (*slen) = pc - buff;
    return(true);
  }

  // if char after number is NOT a comma is an error
  if ((*pc) != ',') {
    error("invalid ZDC format '%s' : want [(s[,n])]\n",source);
    return(false);
  }

  // grab chars until we get a non-digit, this is char count
  pc++;
  si = pc;
  while (isdigit(*pc)) pc++;
  keep = *pc;
  (*pc) = 0;
  if (! unnumString(si,&UL)) {
    error("invalid ZDC format '%s' : want [(s[,n])]\n",source);
    return(false);
  }
  (*count) = UL;
  (*pc) = keep;

  // if char after number is NOT a ')' is an error
  if ((*pc) != ')') {
    error("invalid ZDC format '%s' : want [(s[,n])]\n",source);
    return(false);
  }

  pc++;
  (*slen) = pc - buff;

  return(true);
}
//----------------------------------------------------------------------
bool NSCLConverter::parse_ZSR
     (const char *source,
      unsigned char *addr,
      unsigned long *dict,
      unsigned char *msgid,
      int *slen)

{
  (*slen) = 0;

  // input must:                  01234567890123
  //   be at least 14 chars long, (00,000000,00)
  //   start with (
  //   have ) at pos 13
  //   have commas at pos 3 and 10
  if ((strlen(source) != 14)
   || (source[0] != '(')
   || (source[13] != ')')
   || (source[3] != ',')
   || (source[10] != ',')) {
    error("invalid ZSR format '%s' : want (xx,xxxxxx,xx)\n",source);
    return(false);
  }

  // can now determine slen '(..)'
  (*slen) = 14;

  unsigned long UL;

  // get addr (2-char hex address
  if (! unhexString(source+1,2,&UL)) return(false);
  (*addr) = (UL & 0x000000FF);

  // get dict (6-char hex value)
  if (! unhexString(source+4,6,&UL)) return(false);
  (*dict) = (UL & 0x00FFFFFF);

  // get msgid (2-char hex value)
  if (! unhexString(source+11,2,&UL)) return(false);
  (*msgid) = (UL & 0x000000FF);

  return(true);
}
//----------------------------------------------------------------------
int NSCLConverter::parse(const StreamFormat& fmt, StreamBuffer& info, const char*& source, bool scanFormat)
{
#if 0
  error("NSCLConverter::parse():\n"
        "  fmt:\n"
        "    conv    : '%c'\n"
        "    type    : %u\n"
        "    flags   : %02X\n"
        "    prec    : %d\n"
        "    width   : %d\n"
        "    infolen : %d\n"
        "    info    : '%s'\n"
        "  source: '%s'\n",
        fmt.conv, fmt.type, fmt.flags, fmt.prec, fmt.width, fmt.infolen, fmt.info, source);
#endif
  // zero out format data structure
  zFormatData zdata;
  memset(&zdata,0,sizeof(zdata));

  // get format name (chars after 'Z')
  char name[128];
  getFormatName(source,name);

  if (strcmp(name,"DC") == 0) {
    // width must be specified
    if (fmt.width <= 0) {
      error("ZDC format requires width specifier\n");
      return(false);
    }
    int startpos;
    int count;
    int slen;
    if (! parse_ZDC(source+2,&startpos,&count,&slen)) return(false);
    zdata.zFormat = ZFORMAT_ZDC;
    zdata.U.zdc.startpos = startpos;
    zdata.U.zdc.count    = count;
    putFormatData(info,&zdata);
    source += slen + 2;
    return(pseudo_format);
  }

  if (strcmp(name,"DS") == 0) {
    if (! scanFormat) {
      // DS is read-only, no printing
      error("ZDS format is read-only, no output\n");
      return(false);
    }
    zdata.zFormat = ZFORMAT_ZDS;
    putFormatData(info,&zdata);
    source += 2;
    return(double_format);
  }

  if (strcmp(name,"HF") == 0) {
    zdata.zFormat = ZFORMAT_ZHF;
    putFormatData(info,&zdata);
    source += 2;
    return(double_format);
  }

  if (strcmp(name,"HL") == 0) {
    // if width specified, must be an even number
    if ((fmt.width) && (fmt.width & 0x01)) {
      error("%c%s format length must be an even number\n",fmt.conv,fmt.info);
      return(false);
    }
    zdata.zFormat = ZFORMAT_ZHL;
    putFormatData(info,&zdata);
    source += 2;
    return(LONG_FORMAT);
  }

  if (strcmp(name,"HS") == 0) {
    // if width specified, must be an even number
    if ((fmt.width) && (fmt.width & 0x01)) {
      error("%c%s format length must be an even number\n",fmt.conv,fmt.info);
      return(false);
    }
    zdata.zFormat = ZFORMAT_ZHS;
    putFormatData(info,&zdata);
    source += 2;
    return(string_format);
  }

  if (strcmp(name,"SR") == 0) {
    unsigned char addr;
    unsigned long dict;
    unsigned char msgid;
    int slen;
    if (! parse_ZSR(source+2,&addr,&dict,&msgid,&slen)) return(false);
    zdata.zFormat = ZFORMAT_ZSR;
    zdata.U.zsr.addr  = addr;
    zdata.U.zsr.dict  = dict;
    zdata.U.zsr.msgid = msgid;
    putFormatData(info,&zdata);
    source += slen + 2;
    return(LONG_FORMAT);
  }

  if (strcmp(name,"C8") == 0) {
    int startpos;
    int count;
    int poly;
    int slen;
    if (! parse_ZC8(source+2,&startpos,&count,&poly,&slen)) return(false);
    zdata.zFormat = ZFORMAT_ZC8;
    zdata.U.zc8.startpos = startpos;
    zdata.U.zc8.count    = count;
    zdata.U.zc8.poly     = poly;
    putFormatData(info,&zdata);
    source += slen + 2;
    return(pseudo_format);
  }

  // error if we get this far
  char infotxt[256];
  if (fmt.infolen == 0)
    infotxt[0] = 0;
  else {
    memcpy(infotxt,fmt.info,fmt.infolen);
    infotxt[fmt.infolen] = 0;
  }
  error("NSCLConverter::parse() error:\n"
        "unknown characters after '%c': '%s'\n"
        "  fmt:\n"
        "    conv    : '%c'\n"
        "    type    : %u\n"
        "    flags   : %02X\n"
        "    prec    : %d\n"
        "    width   : %d\n"
        "    infolen : %d\n"
        "    info    : '%s'\n"
        "  source: '%s'\n",
        fmt.conv, name,
        fmt.conv, fmt.type, fmt.flags, fmt.prec, fmt.width, fmt.infolen, infotxt, source);
  return(false);
}
//----------------------------------------------------------------------
int NSCLConverter::scanLong(const StreamFormat& fmt, const char* input, long& value)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZHL: {
      // determine how many bytes (hex-coded pairs) to read
      int ww = (fmt.width > 0) ? (fmt.width / 2) : 4;
      // if no width, stop when invalid hex pair occurs
      bool stopInvalid = (fmt.width > 0) ? false : true;
      // scan until error or done
      value = 0;
      const char *pc = input;
      for (int ii = 0; ii < ww; ii++) {
        if (! isHexPair(pc)) {
          // invalid hex pair, if stop on invalid we break
          if (stopInvalid) break;
          // if NOT stop on invalid, is an error
          error("invalid hex-coded character pair at '%s'\n",pc);
          value = 0;
          return(false);
        }
        // get value of hex-coded pair and add to value being built
        unsigned char d = 0;
        unhexPair(pc,&d);
        value <<= 8;
        value |= d;
        pc += 2;
      }
      // if '-' flag given, convert to signed
      if (fmt.flags & left_flag) {
        int nbytes = (pc - input) / 2;
        if (nbytes < 4) {
          unsigned long mask = 0x00000080;
          mask <<= (nbytes - 1) * 8;
          if (value & mask) {
            mask = 0xFF000000;
            for (int nn = 0; nn < 4-nbytes; nn++) {
              value |= mask;
              mask >>= 8;
            }
          }
        }
      }
      // return number of chars we consumed
      return( pc - input );
    } // ZHL
    break;

    case ZFORMAT_ZSR: {
      // must start with STX
      char *pc = (char *) input;
      if ((*pc) != 0x02) {
        error("ZSR reply starts with 0x%02x, not STX\n",(*pc));
        return(false);
      }
      pc++;

      // get remaining msg characters (13), un-converting escape-sequences
      //       addr + func + 'c' + hexcode + data + msgid + chksum
      // count  1      1      1       3       4       1       2     = 13
      // index  0      1      2     3,4,5   6,7,8,9   10     11,12
      unsigned char unesc[64];
      int unesc_len = 13;
      for (int kk = 0; kk < unesc_len; kk++) {
        if ((*pc) == 0x0d) {
          // hit CR prematurely, might be error reply, reset unesc_len
          unesc_len = kk;
          break;
        } else if ((*pc) != 0x07) {
          // regular char
          unesc[kk] = (*pc);
          pc++;
        } else {
          // 2-byte escape-sequence
          pc++;
          switch ((*pc)) {
            case 0x30:
                unesc[kk] = 0x02;
                pc++;
                break;
            case 0x31:
                unesc[kk] = 0x0d;
                pc++;
                break;
            case 0x32:
                unesc[kk] = 0x07;
                pc++;
                break;
            default:
                error("ZSR unknown escape-sequence 0x07+0x%02x\n",(*pc));
                return(false);
          }
        }
      }

      // calculate desired checksum
      int chksum = 0;
      for (int kk = 0; kk < unesc_len-2; kk++) chksum += unesc[kk];
      chksum &= 0x000000FF;

      // get checksum sent, must match
      int want_chksum = (unesc[unesc_len-2] & 0x0F);
      want_chksum <<= 4;
      want_chksum |= (unesc[unesc_len-1] & 0x0F);
      if (chksum != want_chksum) {
        error("ZSR checksum mismatch : 0x%02x, not 0x%02x\n",chksum,want_chksum);
        return(false);
      }

      // check for error reported
      unsigned char errval = unesc[1] & 0x07;
      if (errval != 0x01) {
        error("ZSR reply reports error : 0x%02x",errval);
        return(false);
      }

      // address must match
      if (unesc[0] != zdata.U.zsr.addr) {
        error("ZSR address mismatch : 0x%02x, not 0x%02x\n",unesc[0],zdata.U.zsr.addr);
        return(false);
      }

      // func nibble must match
      unsigned char func = unesc[1] & 0xF0;
      if (func != 0x80) {
        error("ZSR function mismatch : 0x%02x, not 0x80\n",func);
        return(false);
      }

      // get msgid, must match sent
      int reply_msgid = unesc[unesc_len-3];
      if (reply_msgid != zdata.U.zsr.msgid) {
        error("ZSR reply msgid is 0x%02x, not 0x%02x\n",reply_msgid,zdata.U.zsr.msgid);
        return(false);
      }

      // 'c'+hexcode must match
      unsigned char reply_cmd = unesc[2];
      unsigned long reply_dict = 0;
      for (int kk = 3; kk <= 5; kk++) {
        reply_dict <<= 8;
        reply_dict |= unesc[kk];
      }
      if ((reply_cmd != 'c') || (reply_dict != zdata.U.zsr.dict)) {
        error("ZSR reply code mismatch (0x%02x/%06lx != 0x%02x/%06lx)\n",
               reply_cmd,reply_dict,'c',zdata.U.zsr.dict);
        return(false);
      }

      // get data
      value = 0;
      for (int kk = 6; kk <= 9; kk++) {
        value <<= 8;
        value |= unesc[kk];
      }

      // return number of chars we consumed
      int consumed = (pc - input);
      return(consumed);
    } // ZSR
    break;
  } // switch

  // if we get this far is an error
  value = 0;
  return(false);
}
//----------------------------------------------------------------------
int NSCLConverter::scanDouble(const StreamFormat& fmt, const char* input, double& value)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZDS: {
      // length of string must be at least 18 bytes, size of DS1820 scratchpad
      int L = strlen(input);
      if (L < 18) {
        error("input string length (%d) shorter than DS1820 scratchpad length (18)\n",L);
        return(false);
      }

      // convert scratchpad string into data bytes
      // these are:
      //    0 : temp LSB
      //    1 : temp HSB
      //    2 : TH or User1      (ignored)
      //    3 : TL or User2      (ignored)
      //    4 : reserved         (ignored)
      //    5 : reserved         (ignored)
      //    6 : counts remaining
      //    7 : counts per degC
      //    8 : checksum         (ignored)
      unsigned char data[9] = {0,0,0,0,0,0,0,0,0};
      const char *pc = input;
      for (int ii = 0; ii < 9; ii++) {
        if (! unhexPair(pc,&data[ii])) return(false);
        pc += 2;
      }

      // get temperature data
      short tempD = data[1];
      tempD <<= 8;
      tempD |= data[0];
      tempD &= 0xFFFE;
      double count_rem = data[6];
      double count_per = data[7];

      // calculate temperature
      value = tempD * 0.5;
      if (count_per > 0) value = value - 0.25 + ((count_per - count_rem) / count_per);

      return(18);  // scratchpad is always 18 bytes
    } // ZDS
    break;

    case ZFORMAT_ZHF: {
      // decode hex bytes
      union {
        long L;
        float F;
      } U;
      U.L = 0;
      const char *pc = input;
      for (int ii = 0; ii < 4; ii++) {
        unsigned char d;
        if (! unhexPair(pc,&d)) return(false);
        U.L <<= 8;
        U.L |= d;
        pc += 2;
      }
      // set value and we're done
      value = U.F;
      return(8);  // always consumes 8 bytes
    }
    break;
  } // switch

  // if we get this far is an error
  value = 0;
  return(false);
}
//----------------------------------------------------------------------
int NSCLConverter::scanString(const StreamFormat& fmt, const char* input, char* value, size_t maxlen)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZHS: {
      // determine how many bytes (hex-coded pairs) to read
      int ww = (fmt.width > 0) ? (fmt.width / 2) : ((maxlen-1) & 0xFFFFFFFE);
      // if no width, we stop when an invalid pair occurs or a NULL (00 char pair) is read
      bool stopInvalid = (fmt.width > 0) ? false : true;
      bool stopNull    = (fmt.width > 0) ? false : true;
      // scan until error or done
      const char *pc = input;
      int idx = 0;
      for (int ii = 0; ii < ww; ii++) {
        if (! isHexPair(pc)) {
          // invalid hex pair, if stop on invalid we break
          if (stopInvalid) break;
          // if NOT stop on invalid, is an error
          error("invalid hex-coded character pair at '%s'\n",pc);
          value[0] = 0;
          return(false);
        }
        // get value of hex-coded pair, add to string
        unsigned char d = 0;
        unhexPair(pc,&d);
        value[idx] = (char) d;
        idx++;
        pc += 2;
        // if we've read a NULL (chars 00, byte 0) and should stop on NULL, do so
        if ((d == 0) && (stopNull)) break;
      }
      // always NULL-terminate string
      value[idx] = 0;
      // if # flag given, trim string
      if (fmt.flags & alt_flag) trimString(value);
      // return number of chars we consumed
      return( pc - input );
    } // ZHS
    break;
  } // switch

  // if we get this far is an error
  value[0] = 0;
  return(false);
}
//----------------------------------------------------------------------
int NSCLConverter::scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZDC: {
      // get checksum from reply
      char *pc = input(cursor);
      char str[256];
      strncpy(str,pc,fmt.width);
      str[fmt.width] = 0;
      unsigned long UL;
      if (! unnumStringDecimal(str,&UL)) {
        error("ZDC: '%s' in reply is not a number\n",str);
        return(false);
      }
      unsigned long replySum = UL;

      // calculate checksum from message
      pc = input(0);
      int count = zdata.U.zdc.count;
      if (count <= 0) count = cursor - zdata.U.zdc.startpos;
      if (count <= 0) {
        error("ZDC: byte count (%ld) < startpos (%d)\n",cursor,zdata.U.zdc.startpos);
        return(false);
      }
      unsigned long calcSum = checksum256(pc,zdata.U.zdc.startpos,count);

      // checksums must match
      if (replySum != calcSum) {
        error("ZDC reply checksum is %ld, calculated is %ld\n",replySum,calcSum);
        return(false);
      }

      // return number of chars we consumed
      int consumed = fmt.width;
      return(consumed);
    }

    case ZFORMAT_ZC8: {
/*
      printf("input:\n");
      nsclfrib_showBuffer((unsigned char *)&input[0],input.length());
*/
      // get CRC from reply
      unsigned char replyCRC = (*input(cursor));

      // calculate expected CRC
      char *pc = input(0);
      unsigned char calcCRC = genericCRC8(pc,
                                          zdata.U.zc8.startpos,
                                          zdata.U.zc8.count,
                                          zdata.U.zc8.poly);

      // CRCs must match
      if (replyCRC != calcCRC) {
        error("ZC8 reply crc is 0x%02X, calculated is 0x%02X\n",replyCRC,calcCRC);
        return(false);
      }

      // return number of chars we consumed
      int consumed = 2;
      return(consumed);
    } // ZC8
    break;
  } // switch

  // if we get this far is an error
  return(0);
}
//----------------------------------------------------------------------
bool NSCLConverter::printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZHL: {
      // determine how many bytes (hex-coded pairs) to write
      int ww = (fmt.width > 0) ? (fmt.width / 2) : 4;
      for (int ii = 0; ii < ww; ii++) {
         unsigned char c = (unsigned char) ((value >> 8*(ww-ii-1)) & 0x00FF);
         char str[3];
         toHex(c,str);
         output.append(str);
      }
      return(true);
    } // ZHL
    break;

    case ZFORMAT_ZSR: {
      // build part of message that is checksum-ed
      unsigned char body[64];
      body[0] = zdata.U.zsr.addr;  // address
      body[1] = 0x80;             // function code = dictionary lookup
      body[2] = 'c';              // c=read (set would be 'a')
      body[3] = (zdata.U.zsr.dict >> 16) & 0x0000FF;
      body[4] = (zdata.U.zsr.dict >>  8) & 0x0000FF;
      body[5] = (zdata.U.zsr.dict >>  0) & 0x0000FF;
      body[6] = zdata.U.zsr.msgid;
      int body_len = 7;

      // calculate checksum
      unsigned int chksum = 0;
      for (int kk = 0; kk < body_len; kk++) chksum += body[kk];
      chksum &= 0x000000FF;

      // build whole message, checking for needed escape-sequences
      unsigned char send[64];
      int idx = 0;
      send[idx++] = 0x02; // STX, start of message
      for (int kk = 0; kk < body_len; kk++) {
        switch (body[kk]) {
          case 0x02:
              send[idx++] = 0x07;
              send[idx++] = 0x30;
              break;
          case 0x07:
              send[idx++] = 0x07;
              send[idx++] = 0x32;
              break;
          case 0x0d:
              send[idx++] = 0x07;
              send[idx++] = 0x31;
              break;
          default:
              send[idx++] = body[kk];
              break;
        }
      }

      // add checksum + CR and we're done
      send[idx++] = 0x40 + ((chksum >> 4) & 0x00000F);
      send[idx++] = 0x40 + ((chksum >> 0) & 0x00000F);
      send[idx++] = 0x0d;
      output.append(send,idx);
      return(true);
    } // ZSR;
    break;
  } // switch

  // if we get this far is an error
  return(false);
}
//----------------------------------------------------------------------
bool NSCLConverter::printDouble(const StreamFormat& fmt, StreamBuffer& output, double value)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZHF: {
      // hex-coded float
      union {
        long L;
        float F;
      } U;
      U.F = (float) value;
      int ww = 4;
      for (int ii = 0; ii < ww; ii++) {
        unsigned char c = (unsigned char) ((U.L >> 8*(ww-ii-1)) & 0x00FF);
        char str[3];
        toHex(c,str);
        output.append(str);
      }
      return(true);
    } // ZHF
    break;
  } // switch

  // if we get this far is an error
  return(false);
}
//----------------------------------------------------------------------
bool NSCLConverter::printString(const StreamFormat& fmt, StreamBuffer& output, const char* value)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZHS: {
      int strchars = strlen(value);
      int usechars = strchars;
      int padchars = 0;
      if (fmt.width > 0) {
        usechars = fmt.width / 2;
        if (usechars > strchars) {
          padchars = (usechars - strchars);
          usechars = strchars;
        }
      }
      for (int ii = 0; ii < usechars; ii++) {
        char c[3];
        toHex(value[ii],c);
        output.append(c);
      }
      // pad w/spaces (if any needed)
      for (int ii = 0; ii < padchars; ii++) output.append("20");
      // if no specified width, append hexcoded null
      if (fmt.width <= 0) output.append("00");
      return(true);
    } // ZHS
    break;
  } // switch

  // if we get this far is an error
  return(false);
}
//----------------------------------------------------------------------
bool NSCLConverter::printPseudo(const StreamFormat& fmt, StreamBuffer& output)
{
  // get previously parsed format data
  zFormatData zdata;
  if (! getFormatData(fmt.info,&zdata)) return(false);

  switch (zdata.zFormat) {
    case ZFORMAT_ZDC: {
      // calculate expected checksum
      char *pc = output(0);
      int count = zdata.U.zdc.count;
      if (count <= 0) count = output.length();
      unsigned char calcSum = checksum256(pc,zdata.U.zdc.startpos,count);

      // format as a string
      char str[256];
      if (fmt.flags & zero_flag)
        sprintf(str,"%0*d",fmt.width,calcSum);
      else
        sprintf(str,"%*d",fmt.width,calcSum);

      // add to output
      output.append(str);
      return(true);
    }

    case ZFORMAT_ZC8: {
      // calculate expected CRC
      char *pc = output(0);
      unsigned char calcCRC = genericCRC8(pc,
                                          zdata.U.zc8.startpos,
                                          zdata.U.zc8.count,
                                          zdata.U.zc8.poly);

      // add to output
      output.append(calcCRC);
/*
      printf("output:\n");
      nsclfrib_showBuffer((unsigned char *)&output[0],output.length());
*/
      return(true);
    } // ZC8
    break;
  } // switch

  // if we get this far is an error
  return(false);
}
//----------------------------------------------------------------------

RegisterConverter(NSCLConverter, "Z");

References:
StreamDevice for an odd float format Brown, Garth via Tech-talk

Navigate by Date:
Prev: StreamDevice for an odd float format Brown, Garth via Tech-talk
Next: Re: StreamDevice for an odd float format Priller, John via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
Navigate by Thread:
Prev: StreamDevice for an odd float format Brown, Garth via Tech-talk
Next: Re: StreamDevice for an odd float format Priller, John via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
ANJ, 05 Jun 2019 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·