Reviewed-on: #1 Co-authored-by: Edward Wall <edward.wall@psi.ch> Co-committed-by: Edward Wall <edward.wall@psi.ch>
2537 lines
74 KiB
C
2537 lines
74 KiB
C
/* $Author: anicic $ */
|
|
/* $Date: 2012/03/06 10:53:08 $ */
|
|
/* $Id: devS7plcFW.c,v 1.2 2012/03/06 10:53:08 anicic Exp $ */
|
|
/* $Name: $ */
|
|
/* $Revision: 1.2 $ */
|
|
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <alarm.h>
|
|
#include <dbAccess.h>
|
|
#include <recGbl.h>
|
|
#include <devSup.h>
|
|
#include <devLib.h>
|
|
|
|
#include <epicsVersion.h>
|
|
#include <epicsPrint.h>
|
|
|
|
#include <drvS7plcFW.h>
|
|
|
|
#include <biRecord.h>
|
|
#include <boRecord.h>
|
|
#include <mbbiRecord.h>
|
|
#include <mbboRecord.h>
|
|
#include <mbbiDirectRecord.h>
|
|
#include <mbboDirectRecord.h>
|
|
#include <longinRecord.h>
|
|
#include <longoutRecord.h>
|
|
#include <aiRecord.h>
|
|
#include <aoRecord.h>
|
|
#include <stringinRecord.h>
|
|
#include <stringoutRecord.h>
|
|
#include <waveformRecord.h>
|
|
|
|
#ifdef BASE_VERSION
|
|
#define EPICS_3_13
|
|
#include "compat3_13.h"
|
|
#else
|
|
#include <postfix.h>
|
|
#include <calcoutRecord.h>
|
|
#include <cantProceed.h>
|
|
#include <epicsExport.h>
|
|
#endif
|
|
|
|
/* suppress compiler warning concerning long long with __extension__ */
|
|
#if (!defined __GNUC__) || (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
|
|
#define __extension__
|
|
#endif
|
|
|
|
#ifndef epicsUInt64
|
|
#if (LONG_MAX > 2147483647L)
|
|
#define epicsUInt64 unsigned long
|
|
#define CONV64 "%016lx"
|
|
#else
|
|
#define epicsUInt64 unsigned long long
|
|
#define CONV64 "%016llx"
|
|
#endif
|
|
#endif
|
|
|
|
#define S7MEM_TIME 100
|
|
|
|
typedef struct { /* Private structure to save IO arguments */
|
|
s7plcFWStation *station; /* Card id */
|
|
unsigned short offs; /* Offset (in bytes) within memory block */
|
|
unsigned short bit; /* Bit number (0-15) for bi/bo */
|
|
unsigned short dtype; /* Data type */
|
|
unsigned short dlen; /* Data length (in bytes) */
|
|
epicsInt32 hwLow; /* Hardware Low limit */
|
|
epicsInt32 hwHigh; /* Hardware High limit */
|
|
} S7memPrivate_t;
|
|
|
|
static char cvsid_devS7plcFW[] =
|
|
"$Id: devS7plcFW.c,v 1.2 2012/03/06 10:53:08 anicic Exp $";
|
|
|
|
STATIC long s7plcFWReport();
|
|
|
|
STATIC int s7plcFWIoParse(char* recordName, char *parameters, S7memPrivate_t *);
|
|
STATIC long s7plcFWGetInIntInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt);
|
|
STATIC long s7plcFWGetOutIntInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt);
|
|
|
|
struct devsup {
|
|
long number;
|
|
DEVSUPFUN report;
|
|
DEVSUPFUN init;
|
|
DEVSUPFUN init_record;
|
|
DEVSUPFUN get_ioint_info;
|
|
DEVSUPFUN io;
|
|
};
|
|
|
|
/* bi for status bit ************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordStat(biRecord *);
|
|
STATIC long s7plcFWReadStat(biRecord *);
|
|
STATIC long s7plcFWReadStat2(biRecord *);
|
|
|
|
struct devsup s7plcFWStat =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordStat,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadStat
|
|
};
|
|
epicsExportAddress(dset, s7plcFWStat);
|
|
|
|
struct devsup s7plcFWStat2 =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordStat,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadStat2
|
|
};
|
|
epicsExportAddress(dset, s7plcFWStat2);
|
|
|
|
|
|
/* bi ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordBi(biRecord *);
|
|
STATIC long s7plcFWReadBi(biRecord *);
|
|
|
|
struct devsup s7plcFWBi =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordBi,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadBi
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWBi);
|
|
|
|
/* bo ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordBo(boRecord *);
|
|
STATIC long s7plcFWWriteBo(boRecord *);
|
|
|
|
struct devsup s7plcFWBo =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordBo,
|
|
s7plcFWGetOutIntInfo,
|
|
s7plcFWWriteBo
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWBo);
|
|
|
|
/* mbbi *************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbbi(mbbiRecord *);
|
|
STATIC long s7plcFWReadMbbi(mbbiRecord *);
|
|
|
|
struct devsup s7plcFWMbbi =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordMbbi,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadMbbi
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWMbbi);
|
|
|
|
/* mbbo *************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbbo(mbboRecord *);
|
|
STATIC long s7plcFWWriteMbbo(mbboRecord *);
|
|
|
|
struct devsup s7plcFWMbbo =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordMbbo,
|
|
s7plcFWGetOutIntInfo,
|
|
s7plcFWWriteMbbo
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWMbbo);
|
|
|
|
/* mbbiDirect *******************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbbiDirect(mbbiDirectRecord *);
|
|
STATIC long s7plcFWReadMbbiDirect(mbbiDirectRecord *);
|
|
|
|
struct devsup s7plcFWMbbiDirect =
|
|
{
|
|
5,
|
|
s7plcFWReport,
|
|
NULL,
|
|
s7plcFWInitRecordMbbiDirect,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadMbbiDirect
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWMbbiDirect);
|
|
|
|
/* mbboDirect *******************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbboDirect(mbboDirectRecord *);
|
|
STATIC long s7plcFWWriteMbboDirect(mbboDirectRecord *);
|
|
|
|
struct devsup s7plcFWMbboDirect =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordMbboDirect,
|
|
s7plcFWGetOutIntInfo,
|
|
s7plcFWWriteMbboDirect
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWMbboDirect);
|
|
|
|
/* longin ***********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordLongin(longinRecord *);
|
|
STATIC long s7plcFWReadLongin(longinRecord *);
|
|
|
|
struct devsup s7plcFWLongin =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordLongin,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadLongin
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWLongin);
|
|
|
|
/* longout **********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordLongout(longoutRecord *);
|
|
STATIC long s7plcFWWriteLongout(longoutRecord *);
|
|
|
|
struct devsup s7plcFWLongout =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordLongout,
|
|
s7plcFWGetOutIntInfo,
|
|
s7plcFWWriteLongout
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWLongout);
|
|
|
|
/* ai ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordAi(aiRecord *);
|
|
STATIC long s7plcFWReadAi(aiRecord *);
|
|
STATIC long s7plcFWSpecialLinconvAi(aiRecord *, int after);
|
|
|
|
struct {
|
|
long number;
|
|
DEVSUPFUN report;
|
|
DEVSUPFUN init;
|
|
DEVSUPFUN init_record;
|
|
DEVSUPFUN get_ioint_info;
|
|
DEVSUPFUN read;
|
|
DEVSUPFUN special_linconv;
|
|
} s7plcFWAi =
|
|
{
|
|
6,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordAi,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadAi,
|
|
s7plcFWSpecialLinconvAi
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWAi);
|
|
|
|
/* ao ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordAo(aoRecord *);
|
|
STATIC long s7plcFWWriteAo(aoRecord *);
|
|
STATIC long s7plcFWSpecialLinconvAo(aoRecord *, int after);
|
|
|
|
struct {
|
|
long number;
|
|
DEVSUPFUN report;
|
|
DEVSUPFUN init;
|
|
DEVSUPFUN init_record;
|
|
DEVSUPFUN get_ioint_info;
|
|
DEVSUPFUN write;
|
|
DEVSUPFUN special_linconv;
|
|
} s7plcFWAo =
|
|
{
|
|
6,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordAo,
|
|
s7plcFWGetOutIntInfo,
|
|
s7plcFWWriteAo,
|
|
s7plcFWSpecialLinconvAo
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWAo);
|
|
|
|
/* stringin *********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordStringin(stringinRecord *);
|
|
STATIC long s7plcFWReadStringin(stringinRecord *);
|
|
|
|
struct devsup s7plcFWStringin =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordStringin,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadStringin
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWStringin);
|
|
|
|
/* stringout ********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordStringout(stringoutRecord *);
|
|
STATIC long s7plcFWWriteStringout(stringoutRecord *);
|
|
|
|
struct devsup s7plcFWStringout =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordStringout,
|
|
s7plcFWGetOutIntInfo,
|
|
s7plcFWWriteStringout
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWStringout);
|
|
|
|
/* waveform *********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordWaveform(waveformRecord *);
|
|
STATIC long s7plcFWReadWaveform(waveformRecord *);
|
|
|
|
struct devsup s7plcFWWaveform =
|
|
{
|
|
5,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordWaveform,
|
|
s7plcFWGetInIntInfo,
|
|
s7plcFWReadWaveform
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWWaveform);
|
|
|
|
/* calcout **********************************************************/
|
|
#ifndef EPICS_3_13
|
|
|
|
STATIC long s7plcFWInitRecordCalcout(calcoutRecord *);
|
|
STATIC long s7plcFWWriteCalcout(calcoutRecord *);
|
|
|
|
struct {
|
|
long number;
|
|
DEVSUPFUN report;
|
|
DEVSUPFUN init;
|
|
DEVSUPFUN init_record;
|
|
DEVSUPFUN get_ioint_info;
|
|
DEVSUPFUN write;
|
|
DEVSUPFUN special_linconv;
|
|
} s7plcFWCalcout = {
|
|
6,
|
|
NULL,
|
|
NULL,
|
|
s7plcFWInitRecordCalcout,
|
|
s7plcFWGetOutIntInfo,
|
|
s7plcFWWriteCalcout,
|
|
NULL
|
|
};
|
|
|
|
epicsExportAddress(dset, s7plcFWCalcout);
|
|
#endif
|
|
|
|
/********* Report routine ********************************************/
|
|
|
|
STATIC long s7plcFWReport()
|
|
{
|
|
printf("devS7mem version: %s\n", cvsid_devS7plcFW);
|
|
return 0;
|
|
}
|
|
|
|
/********* Support for "I/O Intr" for input records ******************/
|
|
|
|
STATIC long s7plcFWGetInIntInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt)
|
|
{
|
|
S7memPrivate_t* p = record->dpvt;
|
|
if (p == NULL)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWGetInIntInfo: uninitialized record");
|
|
return -1;
|
|
}
|
|
*ppvt = s7plcFWGetFetchScanPvt(p->station);
|
|
return 0;
|
|
}
|
|
|
|
/********* Support for "I/O Intr" for output records ****************/
|
|
|
|
STATIC long s7plcFWGetOutIntInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt)
|
|
{
|
|
S7memPrivate_t* p = record->dpvt;
|
|
if (p == NULL)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWGetInIntInfo: uninitialized record");
|
|
return -1;
|
|
}
|
|
*ppvt = s7plcFWGetWriteScanPvt(p->station);
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Routine to parse IO arguments
|
|
* IO address line format:
|
|
*
|
|
* <devName>/<a>[+<o>] [T=<datatype>] [B=<bitnumber>] [L=<hwLow|strLen>] [H=<hwHigh>]
|
|
*
|
|
* where: <devName> - symbolic device name
|
|
* <a+o> - address (byte number) within memory block
|
|
* <params> - parameters to be passed to a particular
|
|
* devSup parsering routine
|
|
* <datatype> - INT8, INT16, INT32,
|
|
* UINT16 (or UNSIGN16), UINT32 (or UNSIGN32),
|
|
* REAL32 (or FLOAT), REAL64 (or DOUBLE),
|
|
* STRING,TIME
|
|
* <bitnumber> - least significant bit is 0
|
|
* <hwLow> - raw value that mapps to EGUL
|
|
* <hwHigh> - raw value that mapps to EGUF
|
|
**********************************************************************/
|
|
|
|
STATIC int s7plcFWIoParse(char* recordName, char *par, S7memPrivate_t *priv)
|
|
{
|
|
char devName[255];
|
|
char *p = par, separator;
|
|
int nchar, i;
|
|
int status = 0;
|
|
|
|
struct {char* name; int dlen; epicsType type;} datatypes [] =
|
|
{
|
|
{ "INT8", 1, epicsInt8T },
|
|
|
|
{ "UINT8", 1, epicsUInt8T },
|
|
{ "UNSIGN8", 1, epicsUInt8T },
|
|
{ "BYTE", 1, epicsUInt8T },
|
|
{ "CHAR", 1, epicsUInt8T },
|
|
|
|
{ "INT16", 2, epicsInt16T },
|
|
{ "SHORT", 2, epicsInt16T },
|
|
|
|
{ "UINT16", 2, epicsUInt16T },
|
|
{ "UNSIGN16", 2, epicsUInt16T },
|
|
{ "WORD", 2, epicsUInt16T },
|
|
|
|
{ "INT32", 4, epicsInt32T },
|
|
{ "LONG", 4, epicsInt32T },
|
|
|
|
{ "UINT32", 4, epicsUInt32T },
|
|
{ "UNSIGN32", 4, epicsUInt32T },
|
|
{ "DWORD", 4, epicsUInt32T },
|
|
|
|
{ "REAL32", 4, epicsFloat32T },
|
|
{ "FLOAT32", 4, epicsFloat32T },
|
|
{ "FLOAT", 4, epicsFloat32T },
|
|
|
|
{ "REAL64", 8, epicsFloat64T },
|
|
{ "FLOAT64", 8, epicsFloat64T },
|
|
{ "DOUBLE", 8, epicsFloat64T },
|
|
|
|
{ "TIME", 1, S7MEM_TIME },
|
|
{ "BCD", 1, S7MEM_TIME }
|
|
};
|
|
|
|
/* Get rid of leading whitespace and non-alphanumeric chars */
|
|
while (!isalnum((unsigned char)*p))
|
|
if (*p++ == '\0') return S_drv_badParam;
|
|
|
|
/* Get device name */
|
|
nchar = strcspn(p, "/");
|
|
strncpy(devName, p, nchar);
|
|
devName[nchar] = '\0';
|
|
p += nchar;
|
|
separator = *p++;
|
|
s7plcFWDebugLog(1, "s7plcFWIoParse %s: station=%s\n", recordName, devName);
|
|
|
|
priv->station = s7plcFWOpen(devName);
|
|
if (!priv->station)
|
|
{
|
|
errlogSevPrintf(errlogFatal, "s7plcFWIoParse %s: device not found\n",
|
|
recordName);
|
|
return S_drv_noDevice;
|
|
}
|
|
|
|
/* Check station offset */
|
|
if (separator == '/')
|
|
{
|
|
priv->offs = strtol(p, &p, 0);
|
|
separator = *p++;
|
|
/* Handle any number of optional +o additions to the offs */
|
|
while (separator == '+')
|
|
{
|
|
priv->offs += strtol(p, &p, 0);
|
|
separator = *p++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
priv->offs = 0;
|
|
}
|
|
|
|
s7plcFWDebugLog(1,
|
|
"s7plcFWIoParse %s: offs=%d\n", recordName, priv->offs);
|
|
|
|
/* set default values for parameters */
|
|
if (!priv->dtype && !priv->dlen)
|
|
{
|
|
priv->dtype = epicsInt16T;
|
|
priv->dlen = 2;
|
|
}
|
|
priv->bit = 0;
|
|
priv->hwLow = 0;
|
|
priv->hwHigh = 0;
|
|
|
|
/* allow whitespaces before parameter for device support */
|
|
while ((separator == '\t') || (separator == ' '))
|
|
separator = *p++;
|
|
|
|
/* handle parameter for device support if present */
|
|
nchar = 0;
|
|
if (separator != '\'') p--; /* quote is optional*/
|
|
|
|
/* parse parameters */
|
|
while (p && *p)
|
|
{
|
|
switch (*p)
|
|
{
|
|
case ' ':
|
|
case '\t':
|
|
p++;
|
|
break;
|
|
case 'T': /* T=<datatype> */
|
|
p+=2;
|
|
|
|
if (strncmp(p,"STRING",6) == 0)
|
|
{
|
|
priv->dtype = epicsStringT;
|
|
p += 6;
|
|
}
|
|
else
|
|
{
|
|
static int maxtype =
|
|
sizeof(datatypes)/sizeof(*datatypes);
|
|
for (i = 0; i < maxtype; i++)
|
|
{
|
|
nchar = strlen(datatypes[i].name);
|
|
if (strncmp(p, datatypes[i].name, nchar) == 0)
|
|
{
|
|
priv->dtype = datatypes[i].type;
|
|
priv->dlen = datatypes[i].dlen;
|
|
p += nchar;
|
|
break;
|
|
}
|
|
}
|
|
if (i == maxtype)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWIoParse %s: invalid datatype %s\n",
|
|
recordName, p);
|
|
return S_drv_badParam;
|
|
}
|
|
}
|
|
break;
|
|
case 'B': /* B=<bitnumber> */
|
|
p += 2;
|
|
priv->bit = strtol(p,&p,0);
|
|
break;
|
|
case 'L': /* L=<low raw value> (converts to EGUL)*/
|
|
p += 2;
|
|
priv->hwLow = strtol(p,&p,0);
|
|
break;
|
|
case 'H': /* L=<high raw value> (converts to EGUF)*/
|
|
p += 2;
|
|
priv->hwHigh = strtol(p,&p,0);
|
|
break;
|
|
case '\'':
|
|
if (separator == '\'')
|
|
{
|
|
p = 0;
|
|
break;
|
|
}
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWIoParse %s: unknown parameter '%c'\n",
|
|
recordName, *p);
|
|
return S_drv_badParam;
|
|
}
|
|
}
|
|
|
|
/* for T=STRING L=... means length, not low */
|
|
if (priv->dtype == epicsStringT && priv->hwLow)
|
|
{
|
|
priv->dlen = priv->hwLow;
|
|
priv->hwLow = 0;
|
|
}
|
|
|
|
/* check if bit number is in range */
|
|
if (priv->bit && priv->bit >= priv->dlen*8)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWIoParse %s: invalid bit number %d (>%d)\n",
|
|
recordName, priv->bit, priv->dlen*8-1);
|
|
return S_drv_badParam;
|
|
}
|
|
|
|
/* get default values for L and H if user did'n define them */
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsUInt8T:
|
|
if (priv->hwHigh > 0xFF) status = S_drv_badParam;
|
|
if (!priv->hwHigh) priv->hwLow = 0x00;
|
|
if (!priv->hwHigh) priv->hwHigh = 0xFF;
|
|
break;
|
|
case epicsUInt16T:
|
|
if (priv->hwHigh > 0xFFFF) status = S_drv_badParam;
|
|
if (!priv->hwHigh) priv->hwLow = 0x0000;
|
|
if (!priv->hwHigh) priv->hwHigh = 0xFFFF;
|
|
break;
|
|
case epicsUInt32T:
|
|
if (!priv->hwHigh) priv->hwLow = 0x00000000;
|
|
if (!priv->hwHigh) priv->hwHigh = 0xFFFFFFFF;
|
|
break;
|
|
case epicsInt8T:
|
|
if (priv->hwHigh > 0x7F) status = S_drv_badParam;
|
|
if (!priv->hwHigh) priv->hwLow = 0xFFFFFF81;
|
|
if (!priv->hwHigh) priv->hwHigh = 0x0000007F;
|
|
break;
|
|
case epicsInt16T:
|
|
if (priv->hwHigh > 0x7FFF) status = S_drv_badParam;
|
|
if (!priv->hwHigh) priv->hwLow = 0xFFFF8001;
|
|
if (!priv->hwHigh) priv->hwHigh = 0x00007FFF;
|
|
break;
|
|
case epicsInt32T:
|
|
if (!priv->hwHigh) priv->hwLow = 0x80000001;
|
|
if (!priv->hwHigh) priv->hwHigh = 0x7FFFFFFF;
|
|
break;
|
|
default:
|
|
if (priv->hwHigh || priv->hwLow) {
|
|
errlogSevPrintf(errlogMinor,
|
|
"s7plcFWIoParse %s: L or H makes"
|
|
" no sense with this data type\n",
|
|
recordName);
|
|
}
|
|
break;
|
|
}
|
|
s7plcFWDebugLog(1, "s7plcFWIoParse %s: dlen=%d\n",recordName, priv->dlen);
|
|
s7plcFWDebugLog(1, "s7plcFWIoParse %s: B=%d\n", recordName, priv->bit);
|
|
s7plcFWDebugLog(1, "s7plcFWIoParse %s: L=%#x\n", recordName, priv->hwLow);
|
|
s7plcFWDebugLog(1, "s7plcFWIoParse %s: H=%#x\n", recordName, priv->hwHigh);
|
|
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogMinor,
|
|
"s7plcFWIoParse %s: L or H out of range for this data type\n",
|
|
recordName);
|
|
return status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* bi for status bit ************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordStat(biRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordStat: illegal INP field type");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1, sizeof(S7memPrivate_t),
|
|
"s7plcFWInitRecordStat");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordStat: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
|
|
STATIC long s7plcFWReadStat(biRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal, "%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
/* psudo-read (0 bytes, 0 data len, which 1=fetch-status 2 =write-status) just to get the connection status */
|
|
status = s7plcFWReadArray(priv->station, 1, 0, 0, NULL);
|
|
if (status == S_drv_noConn)
|
|
{
|
|
/* errlogSevPrintf(errlogFatal, "%s: read error - no connection\n", record->name); */
|
|
record->rval = 0;
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
return status;
|
|
}
|
|
record->rval = 1;
|
|
return 0;
|
|
}
|
|
STATIC long s7plcFWReadStat2(biRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal, "%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
/* psudo-read (0 bytes, 0 data len, which 0=fetch-status 1 =write-status) just to get the connection status */
|
|
status = s7plcFWReadArray(priv->station, 2, 0, 0, NULL);
|
|
if (status == S_drv_noConn)
|
|
{
|
|
/* errlogSevPrintf(errlogFatal, "%s: read error - no connection\n", record->name); */
|
|
record->rval = 0;
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
return status;
|
|
}
|
|
record->rval = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* bi ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordBi(biRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordBi: illegal INP field type");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordBi");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordBi: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordBi %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->mask = 1 << priv->bit;
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWReadBi(biRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8;
|
|
epicsUInt16 rval16;
|
|
epicsUInt32 rval32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
1, &rval8);
|
|
s7plcFWDebugLog(3, "bi %s: read 8bit %02x\n",
|
|
record->name, rval8);
|
|
rval32 = rval8;
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
2, &rval16);
|
|
s7plcFWDebugLog(3, "bi %s: read 16bit %04x\n",
|
|
record->name, rval16);
|
|
rval32 = rval16;
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &rval32);
|
|
s7plcFWDebugLog(3, "bi %s: read 32bit %04x\n",
|
|
record->name, rval32);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
record->rval = rval32 & record->mask;
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: read error\n", record->name);
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* bo ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordBo(boRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->out.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordBo: illegal OUT field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordBo");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->out.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordBo: bad OUT field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordBo %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->mask = 1 << priv->bit;
|
|
record->dpvt = priv;
|
|
return 2; /* preserve whatever is in the VAL field */
|
|
}
|
|
|
|
STATIC long s7plcFWWriteBo(boRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8, mask8;
|
|
epicsUInt16 rval16, mask16;
|
|
epicsUInt32 rval32, mask32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
rval8 = record->rval;
|
|
mask8 = record->mask;
|
|
s7plcFWDebugLog(2, "bo %s: write 8bit %02x mask %02x\n",
|
|
record->name, rval8, mask8);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
1, &rval8, &mask8);
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
rval16 = record->rval;
|
|
mask16 = record->mask;
|
|
s7plcFWDebugLog(2, "bo %s: write 16bit %04x mask %04x\n",
|
|
record->name, rval16, mask16);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
2, &rval16, &mask16);
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
rval32 = record->rval;
|
|
mask32 = record->mask;
|
|
s7plcFWDebugLog(2, "bo %s: write 32bit %08x mask %08x\n",
|
|
record->name, rval32, mask32);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
4, &rval32, &mask32);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, WRITE_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* mbbi *************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbbi(mbbiRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbbi: illegal INP field type");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordMbbi");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbbi: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordMbbi %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
if (record->shft > 0) record->mask <<= record->shft;
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWReadMbbi(mbbiRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8;
|
|
epicsUInt16 rval16;
|
|
epicsUInt32 rval32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
1, &rval8);
|
|
s7plcFWDebugLog(3, "mbbi %s: read 8bit %02x\n",
|
|
record->name, rval8);
|
|
rval32 = rval8;
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
2, &rval16);
|
|
s7plcFWDebugLog(3, "mbbi %s: read 16bit %04x\n",
|
|
record->name, rval16);
|
|
rval32 = rval16;
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &rval32);
|
|
s7plcFWDebugLog(3, "mbbi %s: read 32bit %04x\n",
|
|
record->name, rval32);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
record->rval = rval32 & record->mask;
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: read error\n", record->name);
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* mbbo *************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbbo(mbboRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->out.type != INST_IO) {
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbbo: illegal OUT field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordMbbo");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->out.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbbo: bad OUT field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
if (record->shft > 0) record->mask <<= record->shft;
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordMbbo %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
return 2; /* preserve whatever is in the VAL field */
|
|
}
|
|
|
|
STATIC long s7plcFWWriteMbbo(mbboRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8, mask8;
|
|
epicsUInt16 rval16, mask16;
|
|
epicsUInt32 rval32, mask32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
rval8 = record->rval;
|
|
mask8 = record->mask;
|
|
s7plcFWDebugLog(2, "mbbo %s: write 8bit %02x mask %02x\n",
|
|
record->name, rval8, mask8);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
1, &rval8, &mask8);
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
rval16 = record->rval;
|
|
mask16 = record->mask;
|
|
s7plcFWDebugLog(2, "mbbo %s: write 16bit %04x mask %04x\n",
|
|
record->name, rval16, mask16);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
2, &rval16, &mask16);
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
rval32 = record->rval;
|
|
mask32 = record->mask;
|
|
s7plcFWDebugLog(2, "mbbo %s: write 32bit %08x mask %08x\n",
|
|
record->name, rval32, mask32);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
4, &rval32, &mask32);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, WRITE_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* mbbiDirect *******************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbbiDirect(mbbiDirectRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbbiDirect: illegal INP field type");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordMbbiDirect");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbbiDirect: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
if (record->shft > 0) record->mask <<= record->shft;
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordMbbiDirect %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWReadMbbiDirect(mbbiDirectRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8;
|
|
epicsUInt16 rval16;
|
|
epicsUInt32 rval32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
1, &rval8);
|
|
s7plcFWDebugLog(3, "mbbiDirect %s: read 8bit %02x\n",
|
|
record->name, rval8);
|
|
rval32 = rval8;
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
2, &rval16);
|
|
s7plcFWDebugLog(3, "mbbiDirect %s: read 16bit %04x\n",
|
|
record->name, rval16);
|
|
rval32 = rval16;
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &rval32);
|
|
s7plcFWDebugLog(3, "mbbiDirect %s: read 32bit %08x\n",
|
|
record->name, rval32);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
record->rval = rval32 & record->mask;
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: read error\n", record->name);
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* mbboDirect *******************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordMbboDirect(mbboDirectRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->out.type != INST_IO) {
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbboDirect: illegal OUT field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordMbboDirect");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->out.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordMbboDirect: bad OUT field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
if (record->shft > 0) record->mask <<= record->shft;
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordMbboDirect %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
return 2; /* preserve whatever is in the VAL field */
|
|
}
|
|
|
|
STATIC long s7plcFWWriteMbboDirect(mbboDirectRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8, mask8;
|
|
epicsUInt16 rval16, mask16;
|
|
epicsUInt32 rval32, mask32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
rval8 = record->rval;
|
|
mask8 = record->mask;
|
|
s7plcFWDebugLog(2, "mbboDirect %s: write 8bit %02x mask %02x\n",
|
|
record->name, rval8, mask8);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
1, &rval8, &mask8);
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
rval16 = record->rval;
|
|
mask16 = record->mask;
|
|
s7plcFWDebugLog(2, "mbboDirect %s: write 16bit %04x mask %04x\n",
|
|
record->name, rval16, mask16);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
2, &rval16, &mask16);
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
rval32 = record->rval;
|
|
mask32 = record->mask;
|
|
s7plcFWDebugLog(2, "mbboDirect %s: write 32bit %08x mask %08x\n",
|
|
record->name, rval32, mask32);
|
|
status = s7plcFWWriteMasked(priv->station, priv->offs,
|
|
4, &rval32, &mask32);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n", record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, WRITE_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* longin ***********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordLongin(longinRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordLongin: illegal INP field type");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordLongin");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordLongin: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordLongin %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWReadLongin(longinRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
signed char sval8;
|
|
epicsUInt8 uval8;
|
|
epicsInt16 sval16;
|
|
epicsUInt16 uval16;
|
|
epicsInt32 sval32;
|
|
epicsUInt32 uval32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
1, &sval8);
|
|
s7plcFWDebugLog(3, "longin %s: read 8bit %02x\n",
|
|
record->name, sval8);
|
|
record->val = sval8;
|
|
break;
|
|
case epicsUInt8T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
1, &uval8);
|
|
s7plcFWDebugLog(3, "longin %s: read 8bit %02x\n",
|
|
record->name, uval8);
|
|
record->val = uval8;
|
|
break;
|
|
case epicsInt16T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
2, &sval16);
|
|
s7plcFWDebugLog(3, "longin %s: read 16bit %04x\n",
|
|
record->name, sval16);
|
|
record->val = sval16;
|
|
break;
|
|
case epicsUInt16T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
2, &uval16);
|
|
s7plcFWDebugLog(3, "longin %s: read 16bit %04x\n",
|
|
record->name, sval16);
|
|
record->val = uval16;
|
|
break;
|
|
case epicsInt32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &sval32);
|
|
s7plcFWDebugLog(3, "longin %s: read 32bit %04x\n",
|
|
record->name, sval32);
|
|
record->val = sval32;
|
|
break;
|
|
case epicsUInt32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &uval32);
|
|
s7plcFWDebugLog(3, "longin %s: read 32bit %04x\n",
|
|
record->name, uval32);
|
|
record->val = uval32;
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: read error\n", record->name);
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* longout **********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordLongout(longoutRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->out.type != INST_IO) {
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordLongout: illegal OUT field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordLongout");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->out.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordLongout: bad OUT field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordLongout %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWWriteLongout(longoutRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8;
|
|
epicsUInt16 rval16;
|
|
epicsUInt32 rval32;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
rval8 = record->val;
|
|
s7plcFWDebugLog(2, "longout %s: write 8bit %02x\n",
|
|
record->name, rval8);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
1, &rval8);
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
rval16 = record->val;
|
|
s7plcFWDebugLog(2, "longout %s: write 16bit %04x\n",
|
|
record->name, rval16);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
2, &rval16);
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
rval32 = record->val;
|
|
s7plcFWDebugLog(2, "longout %s: write 32bit %08x\n",
|
|
record->name, rval32);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
4, &rval32);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, WRITE_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* ai ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordAi(aiRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordAi: illegal INP field type");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1, sizeof(S7memPrivate_t),
|
|
"s7plcFWInitRecordAi");
|
|
status = s7plcFWIoParse(record->name, record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordAi: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
case epicsFloat32T:
|
|
case epicsFloat64T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordAi %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
s7plcFWSpecialLinconvAi(record, TRUE);
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWReadAi(aiRecord *record)
|
|
{
|
|
int status, floatval = FALSE;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
signed char sval8;
|
|
unsigned char uval8;
|
|
epicsInt16 sval16;
|
|
epicsUInt16 uval16;
|
|
epicsInt32 sval32;
|
|
epicsUInt32 uval32;
|
|
union {epicsFloat32 f; epicsUInt32 i; } val32;
|
|
__extension__ union {epicsFloat64 f; epicsUInt64 i; } val64;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal, "%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
1, &sval8);
|
|
s7plcFWDebugLog(3, "ai %s: read 8bit %02x\n",
|
|
record->name, sval8);
|
|
record->rval = sval8;
|
|
break;
|
|
case epicsUInt8T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
1, &uval8);
|
|
s7plcFWDebugLog(3, "ai %s: read 8bit %02x\n",
|
|
record->name, uval8);
|
|
record->rval = uval8;
|
|
break;
|
|
case epicsInt16T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
2, &sval16);
|
|
s7plcFWDebugLog(3, "ai %s: read 16bit %04x\n",
|
|
record->name, sval16);
|
|
record->rval = sval16;
|
|
break;
|
|
case epicsUInt16T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
2, &uval16);
|
|
s7plcFWDebugLog(3, "ai %s: read 16bit %04x\n",
|
|
record->name, uval16);
|
|
record->rval = uval16;
|
|
break;
|
|
case epicsInt32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &sval32);
|
|
s7plcFWDebugLog(3, "ai %s: read 32bit %04x\n",
|
|
record->name, sval32);
|
|
record->rval = sval32;
|
|
break;
|
|
case epicsUInt32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &uval32);
|
|
s7plcFWDebugLog(3, "ai %s: read 32bit %04x\n",
|
|
record->name, uval32);
|
|
record->rval = uval32;
|
|
break;
|
|
case epicsFloat32T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
4, &val32);
|
|
s7plcFWDebugLog(3, "ai %s: read 32bit %04x = %g\n",
|
|
record->name, val32.i, val32.f);
|
|
val64.f = val32.f;
|
|
floatval = TRUE;
|
|
break;
|
|
case epicsFloat64T:
|
|
status = s7plcFWRead(priv->station, priv->offs,
|
|
8, &val64);
|
|
__extension__ s7plcFWDebugLog(3, "ai %s: read 64bit " CONV64 " = %g\n",
|
|
record->name, val64.i, val64.f);
|
|
floatval = TRUE;
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: read error\n", record->name);
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
return status;
|
|
}
|
|
if (floatval)
|
|
{
|
|
/* emulate scaling */
|
|
if (record->aslo != 0.0) val64.f *= record->aslo;
|
|
val64.f += record->aoff;
|
|
if (record->udf)
|
|
record->val = val64.f;
|
|
else
|
|
/* emulate smoothing */
|
|
record->val = record->val * record->smoo +
|
|
val64.f * (1.0 - record->smoo);
|
|
record->udf = FALSE;
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWSpecialLinconvAi(aiRecord *record, int after)
|
|
{
|
|
epicsUInt32 hwSpan;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
|
|
if (after) {
|
|
hwSpan = priv->hwHigh - priv->hwLow;
|
|
record->eslo = (record->eguf - record->egul) / hwSpan;
|
|
record->eoff =
|
|
(priv->hwHigh*record->egul - priv->hwLow*record->eguf)
|
|
/ hwSpan;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ao ***************************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordAo(aoRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->out.type != INST_IO) {
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordAo: illegal OUT field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordAo");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->out.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordAo: bad OUT field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
case epicsFloat32T:
|
|
case epicsFloat64T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordAo %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
s7plcFWSpecialLinconvAo(record, TRUE);
|
|
return 2; /* preserve whatever is in the VAL field */
|
|
}
|
|
|
|
STATIC long s7plcFWWriteAo(aoRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 rval8;
|
|
epicsUInt16 rval16;
|
|
epicsUInt32 rval32;
|
|
union {epicsFloat32 f; epicsUInt32 i; } val32;
|
|
__extension__ union {epicsFloat64 f; epicsUInt64 i; } val64;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
rval32 = record->rval;
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
if (record->rval > priv->hwHigh) rval32 = priv->hwHigh;
|
|
if (record->rval < priv->hwLow) rval32 = priv->hwLow;
|
|
rval8 = rval32;
|
|
s7plcFWDebugLog(2, "ao %s: write 8bit %02x\n",
|
|
record->name, rval8 & 0xff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
1, &rval8);
|
|
break;
|
|
case epicsUInt8T:
|
|
if (rval32 > (epicsUInt32)priv->hwHigh) rval32 = priv->hwHigh;
|
|
if (rval32 < (epicsUInt32)priv->hwLow) rval32 = priv->hwLow;
|
|
rval8 = rval32;
|
|
s7plcFWDebugLog(2, "ao %s: write 8bit %02x\n",
|
|
record->name, rval8 & 0xff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
1, &rval8);
|
|
break;
|
|
case epicsInt16T:
|
|
if (record->rval > priv->hwHigh) rval32 = priv->hwHigh;
|
|
if (record->rval < priv->hwLow) rval32 = priv->hwLow;
|
|
rval16 = rval32;
|
|
s7plcFWDebugLog(2, "ao %s: write 16bit %04x\n",
|
|
record->name, rval16 & 0xffff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
2, &rval16);
|
|
break;
|
|
case epicsUInt16T:
|
|
if (rval32 > (epicsUInt32)priv->hwHigh) rval32 = priv->hwHigh;
|
|
if (rval32 < (epicsUInt32)priv->hwLow) rval32 = priv->hwLow;
|
|
rval16 = rval32;
|
|
s7plcFWDebugLog(2, "ao %s: write 16bit %04x\n",
|
|
record->name, rval16 & 0xffff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
2, &rval16);
|
|
break;
|
|
case epicsInt32T:
|
|
if (record->rval > priv->hwHigh) rval32 = priv->hwHigh;
|
|
if (record->rval < priv->hwLow) rval32 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "ao %s: write 32bit %08x\n",
|
|
record->name, rval32);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
4, &rval32);
|
|
break;
|
|
case epicsUInt32T:
|
|
if (rval32 > (epicsUInt32)priv->hwHigh) rval32 = priv->hwHigh;
|
|
if (rval32 < (epicsUInt32)priv->hwLow) rval32 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "ao %s: write 32bit %08x\n",
|
|
record->name, rval32);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
4, &rval32);
|
|
break;
|
|
case epicsFloat32T:
|
|
/* emulate scaling */
|
|
val32.f = record->oval - record->aoff;
|
|
if (record->aslo != 0) val32.f /= record->aslo;
|
|
|
|
s7plcFWDebugLog(2, "ao %s: write 32bit %08x = %g\n",
|
|
record->name, val32.i, val32.f);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
4, &val32);
|
|
break;
|
|
case epicsFloat64T:
|
|
/* emulate scaling */
|
|
val64.f = record->oval - record->aoff;
|
|
if (record->aslo != 0) val64.f /= record->aslo;
|
|
|
|
__extension__ s7plcFWDebugLog(2, "ao %s: write 64bit " CONV64 " = %g\n",
|
|
record->name, val64.i, val64.f);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
8, &val64);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, WRITE_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
STATIC long s7plcFWSpecialLinconvAo(aoRecord *record, int after)
|
|
{
|
|
epicsUInt32 hwSpan;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *) record->dpvt;
|
|
|
|
if (after) {
|
|
hwSpan = priv->hwHigh - priv->hwLow;
|
|
record->eslo = (record->eguf - record->egul) / hwSpan;
|
|
record->eoff =
|
|
(priv->hwHigh*record->egul -priv->hwLow*record->eguf)
|
|
/ hwSpan;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* stringin *********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordStringin(stringinRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordStringin: illegal INP field type");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordStringin");
|
|
priv->dtype = epicsStringT;
|
|
priv->dlen = sizeof(record->val);
|
|
status = s7plcFWIoParse(record->name,
|
|
record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordLongin: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
if (priv->dtype != epicsStringT)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordStringin %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
if (priv->dlen > sizeof(record->val))
|
|
{
|
|
errlogSevPrintf(errlogMinor,
|
|
"%s: string size reduced from %d to %d\n",
|
|
record->name, priv->dlen, (int)sizeof(record->val));
|
|
priv->dlen = sizeof(record->val);
|
|
}
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWReadStringin(stringinRecord *record)
|
|
{
|
|
// The SPS includes the max length of the string and the length of the
|
|
// current string, as the first two bytes in the register.
|
|
|
|
int status;
|
|
epicsUInt8 uval8;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
|
|
status = s7plcFWRead(priv->station, priv->offs+1,
|
|
1, &uval8);
|
|
s7plcFWDebugLog(3, "stringin %s: read 8bit %02x\n",
|
|
record->name, uval8);
|
|
uval8 = uval8 <= priv->dlen ? uval8 : priv->dlen;
|
|
|
|
memset(record->val, 0, priv->dlen);
|
|
status = s7plcFWReadArray(priv->station, priv->offs+2,
|
|
1, uval8, record->val);
|
|
s7plcFWDebugLog(3, "stringin %s: read array of %d 8bit values\n",
|
|
record->name, uval8);
|
|
if (record->val[priv->dlen] && !memchr(record->val, 0, priv->dlen))
|
|
{
|
|
/* truncate oversize string */
|
|
record->val[priv->dlen] = 0;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: read error\n", record->name);
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* stringout ********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordStringout(stringoutRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->out.type != INST_IO) {
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordStringout: illegal OUT field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordStringout");
|
|
priv->dtype = epicsStringT;
|
|
priv->dlen = sizeof(record->val);
|
|
status = s7plcFWIoParse(record->name,
|
|
record->out.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordStringout: bad OUT field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
if (priv->dtype != epicsStringT)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordStringout %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
if (priv->dlen > sizeof(record->val))
|
|
{
|
|
errlogSevPrintf(errlogMinor,
|
|
"%s: string size reduced from %d to %d\n",
|
|
record->name, priv->dlen, (int)sizeof(record->val));
|
|
priv->dlen = sizeof(record->val);
|
|
}
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
STATIC long s7plcFWWriteStringout(stringoutRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
s7plcFWDebugLog(2, "stringout %s: write %d 8bit values: \"%.*s\"\n",
|
|
record->name, priv->dlen, priv->dlen, record->val);
|
|
status = s7plcFWWriteArray(priv->station, priv->offs,
|
|
1, priv->dlen, record->val);
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, WRITE_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* waveform *********************************************************/
|
|
|
|
STATIC long s7plcFWInitRecordWaveform(waveformRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->inp.type != INST_IO) {
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordWaveform: illegal INP field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordWaveform");
|
|
switch (record->ftvl)
|
|
{
|
|
case DBF_CHAR:
|
|
priv->dtype = epicsInt8T;
|
|
priv->dlen = 1;
|
|
break;
|
|
case DBF_UCHAR:
|
|
priv->dtype = epicsUInt8T;
|
|
priv->dlen = 1;
|
|
break;
|
|
case DBF_SHORT:
|
|
priv->dtype = epicsInt16T;
|
|
priv->dlen = 2;
|
|
break;
|
|
case DBF_USHORT:
|
|
priv->dtype = epicsUInt16T;
|
|
priv->dlen = 2;
|
|
break;
|
|
case DBF_LONG:
|
|
priv->dtype = epicsInt32T;
|
|
priv->dlen = 4;
|
|
break;
|
|
case DBF_ULONG:
|
|
priv->dtype = epicsUInt32T;
|
|
priv->dlen = 4;
|
|
break;
|
|
case DBF_FLOAT:
|
|
priv->dtype = epicsFloat32T;
|
|
priv->dlen = 4;
|
|
break;
|
|
case DBF_DOUBLE:
|
|
priv->dtype = epicsFloat64T;
|
|
priv->dlen = 8;
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordWaveform %s: illegal FTVL value\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
status = s7plcFWIoParse(record->name,
|
|
record->inp.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordWaveform: bad INP field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
record->nord = record->nelm;
|
|
switch (priv->dtype)
|
|
{
|
|
case S7MEM_TIME:
|
|
if ((record->ftvl != DBF_CHAR) && (record->ftvl != DBF_UCHAR))
|
|
{
|
|
status = S_db_badField;
|
|
}
|
|
break;
|
|
case epicsFloat64T:
|
|
if (record->ftvl != DBF_DOUBLE)
|
|
{
|
|
status = S_db_badField;
|
|
}
|
|
break;
|
|
case epicsFloat32T:
|
|
if (record->ftvl != DBF_FLOAT)
|
|
{
|
|
status = S_db_badField;
|
|
}
|
|
break;
|
|
case epicsStringT:
|
|
if ((record->ftvl == DBF_CHAR) || (record->ftvl == DBF_UCHAR))
|
|
{
|
|
if (!priv->dlen) priv->dlen = record->nelm;
|
|
if (priv->dlen > record->nelm) priv->dlen = record->nelm;
|
|
break;
|
|
}
|
|
break;
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
if ((record->ftvl != DBF_CHAR) && (record->ftvl == DBF_UCHAR))
|
|
{
|
|
status = S_db_badField;
|
|
}
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
if ((record->ftvl != DBF_SHORT) && (record->ftvl == DBF_USHORT))
|
|
{
|
|
status = S_db_badField;
|
|
}
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
if ((record->ftvl != DBF_LONG) && (record->ftvl == DBF_ULONG))
|
|
{
|
|
status = S_db_badField;
|
|
}
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordWaveform %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordWaveform %s: "
|
|
"wrong FTVL field for this data type",
|
|
record->name);
|
|
return status;
|
|
}
|
|
record->dpvt = priv;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* bcd2d routine to convert byte from BCD to decimal format.
|
|
*/
|
|
static unsigned char bcd2d(unsigned char bcd)
|
|
{
|
|
unsigned char tmp;
|
|
|
|
tmp = bcd & 0xF;
|
|
tmp += ((bcd >> 4) & 0xF)*10;
|
|
|
|
return tmp;
|
|
}
|
|
|
|
STATIC long s7plcFWReadWaveform(waveformRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
char Time[8];
|
|
int i;
|
|
char *p;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsStringT:
|
|
status = s7plcFWReadArray(priv->station, priv->offs,
|
|
1, record->nelm, record->bptr);
|
|
s7plcFWDebugLog(3,
|
|
"waveform %s: read %ld values of 8bit to %p\n",
|
|
record->name, (long int) record->nelm, record->bptr);
|
|
break;
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
status = s7plcFWReadArray(priv->station, priv->offs,
|
|
2, record->nelm, record->bptr);
|
|
s7plcFWDebugLog(3,
|
|
"waveform %s: read %ld values of 16bit to %p\n",
|
|
record->name, (long int) record->nelm, record->bptr);
|
|
break;
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
case epicsFloat32T:
|
|
status = s7plcFWReadArray(priv->station, priv->offs,
|
|
4, record->nelm, record->bptr);
|
|
s7plcFWDebugLog(3,
|
|
"waveform %s: read %ld values of 32bit to %p\n",
|
|
record->name, (long int) record->nelm, record->bptr);
|
|
break;
|
|
case epicsFloat64T:
|
|
status = s7plcFWReadArray(priv->station, priv->offs,
|
|
8, record->nelm, record->bptr);
|
|
s7plcFWDebugLog(3,
|
|
"waveform %s: read %ld values of 64bit to %p\n",
|
|
record->name, (long int) record->nelm, record->bptr);
|
|
break;
|
|
case S7MEM_TIME:
|
|
status = s7plcFWReadArray(priv->station, priv->offs,
|
|
1, 8, Time);
|
|
s7plcFWDebugLog(3,
|
|
"waveform %s: read 8 values of 8bit to %p\n",
|
|
record->name, record->bptr);
|
|
if (status) break;
|
|
for (i = 0, p = record->bptr; i < record->nelm; i++)
|
|
*p++ = (i == 7)? Time[i] : bcd2d(Time[i]);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
record->nord = record->nelm;
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: read error\n", record->name);
|
|
recGblSetSevr(record, READ_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* calcout **********************************************************/
|
|
#ifndef EPICS_3_13
|
|
|
|
STATIC long s7plcFWInitRecordCalcout(calcoutRecord *record)
|
|
{
|
|
S7memPrivate_t *priv;
|
|
int status;
|
|
|
|
if (record->out.type != INST_IO) {
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordCalcout: illegal OUT field");
|
|
return S_db_badField;
|
|
}
|
|
priv = (S7memPrivate_t *)callocMustSucceed(1,
|
|
sizeof(S7memPrivate_t), "s7plcFWInitRecordCalcout");
|
|
status = s7plcFWIoParse(record->name,
|
|
record->out.value.instio.string, priv);
|
|
if (status)
|
|
{
|
|
recGblRecordError(S_db_badField, record,
|
|
"s7plcFWInitRecordCalcout: bad OUT field");
|
|
return S_db_badField;
|
|
}
|
|
assert(priv->station);
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
case epicsUInt8T:
|
|
case epicsInt16T:
|
|
case epicsUInt16T:
|
|
case epicsInt32T:
|
|
case epicsUInt32T:
|
|
case epicsFloat32T:
|
|
case epicsFloat64T:
|
|
break;
|
|
default:
|
|
errlogSevPrintf(errlogFatal,
|
|
"s7plcFWInitRecordCalcout %s: illegal data type\n",
|
|
record->name);
|
|
return S_db_badField;
|
|
}
|
|
record->dpvt = priv;
|
|
return 2; /* preserve whatever is in the VAL field */
|
|
}
|
|
|
|
STATIC long s7plcFWWriteCalcout(calcoutRecord *record)
|
|
{
|
|
int status;
|
|
S7memPrivate_t *priv = (S7memPrivate_t *)record->dpvt;
|
|
epicsUInt8 uval8;
|
|
epicsUInt16 uval16;
|
|
epicsUInt32 uval32;
|
|
epicsInt8 sval8;
|
|
epicsInt16 sval16;
|
|
epicsInt32 sval32;
|
|
union {epicsFloat32 f; epicsUInt32 i; } val32;
|
|
__extension__ union {epicsFloat64 f; epicsUInt64 i; } val64;
|
|
|
|
if (!priv)
|
|
{
|
|
recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: not initialized\n", record->name);
|
|
return -1;
|
|
}
|
|
assert(priv->station);
|
|
val64.f = record->oval;
|
|
switch (priv->dtype)
|
|
{
|
|
case epicsInt8T:
|
|
sval8 = val64.f;
|
|
if (val64.f > priv->hwHigh) sval8 = priv->hwHigh;
|
|
if (val64.f < priv->hwLow) sval8 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "calcout %s: write 8bit %02x\n",
|
|
record->name, sval8 & 0xff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
1, &sval8);
|
|
break;
|
|
case epicsUInt8T:
|
|
uval8 = val64.f;
|
|
if (val64.f > priv->hwHigh) uval8 = priv->hwHigh;
|
|
if (val64.f < priv->hwLow) uval8 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "calcout %s: write 8bit %02x\n",
|
|
record->name, uval8 & 0xff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
1, &uval8);
|
|
break;
|
|
case epicsInt16T:
|
|
sval16 = val64.f;
|
|
if (val64.f > priv->hwHigh) sval16 = priv->hwHigh;
|
|
if (val64.f < priv->hwLow) sval16 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "calcout %s: write 16bit %04x\n",
|
|
record->name, sval16 & 0xffff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
2, &sval16);
|
|
break;
|
|
case epicsUInt16T:
|
|
uval16 = val64.f;
|
|
if (val64.f > priv->hwHigh) uval16 = priv->hwHigh;
|
|
if (val64.f < priv->hwLow) uval16 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "calcout %s: write 16bit %04x\n",
|
|
record->name, uval16 & 0xffff);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
2, &uval16);
|
|
break;
|
|
case epicsInt32T:
|
|
sval32 = val64.f;
|
|
if (val64.f > priv->hwHigh) sval32 = priv->hwHigh;
|
|
if (val64.f < priv->hwLow) sval32 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "calcout %s: write 32bit %08x\n",
|
|
record->name, sval32);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
4, &sval32);
|
|
break;
|
|
case epicsUInt32T:
|
|
uval32 = val64.f;
|
|
if (val64.f > priv->hwHigh) uval32 = priv->hwHigh;
|
|
if (val64.f < priv->hwLow) uval32 = priv->hwLow;
|
|
s7plcFWDebugLog(2, "calcout %s: write 32bit %08x\n",
|
|
record->name, uval32);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
4, &uval32);
|
|
break;
|
|
case epicsFloat32T:
|
|
val32.f = val64.f;
|
|
s7plcFWDebugLog(2, "calcout %s: write 32bit %08x = %g\n",
|
|
record->name, val32.i, val32.f);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
4, &val32);
|
|
break;
|
|
case epicsFloat64T:
|
|
__extension__ s7plcFWDebugLog(2, "calcout %s: write 64bit " CONV64 " = %g\n",
|
|
record->name, val64.i, val64.f);
|
|
status = s7plcFWWrite(priv->station, priv->offs,
|
|
8, &val64);
|
|
break;
|
|
default:
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
errlogSevPrintf(errlogFatal,
|
|
"%s: unexpected data type requested\n",
|
|
record->name);
|
|
return -1;
|
|
}
|
|
if (status == S_drv_noConn)
|
|
{
|
|
recGblSetSevr(record, COMM_ALARM, INVALID_ALARM);
|
|
return 0;
|
|
}
|
|
if (status)
|
|
{
|
|
recGblSetSevr(record, WRITE_ALARM, INVALID_ALARM);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
#endif
|