Files
sics/site_ansto/orhvps.c
Ferdi Franceschini 02015668e2 Mark Lesha fixed some bugs associated with "send"
r2692 | ffr | 2008-09-17 08:39:33 +1000 (Wed, 17 Sep 2008) | 2 lines
2012-11-15 13:42:10 +11:00

1260 lines
34 KiB
C

/*-------------------------------------------------------------------------
O R H V P S
Support for Oak Ridge High Voltage Power Supply for SICS.
The meaning and working of the functions defined is as desribed for a
general environment controller.
Douglas Clowes, December 2007
Copyright: site_ansto/doc/Copyright.txt
----------------------------------------------------------------------------
*/
#include "orhvps.h"
#include "sics.h"
#include "asyncqueue.h"
#include "nwatch.h"
#include "fsm.h"
#include "anstoutil.h"
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#define CMDLEN 132
#define MY_ABSOLUTE_MAXIMUM (2600.0)
#define MY_MINIMUM_RATE (1.0)
#define MY_MAXIMUM_RATE (40.0)
/* ERROR CODES */
#define ORHVPS_ERR_NONE (0)
#define ORHVPS_ERR_LOCKED (-1)
#define ORHVPS_ERR_RANGE (-2)
/* Device Driver Control Structure */
struct orhvps_s {
pEVControl controller;
int iErrorCode; /* error code */
float fTarget; /* requested target voltage in volts */
float fValue; /* current voltage in volts */
float fMax; /* maximum voltage in volts */
float fRate; /* voltage slew rate in volts per second */
float fUpper; /* Normal Operating Voltage */
float fLower; /* Normal Moving Voltage */
bool isLocked; /* changes no longer permitted */
bool bRunFlag; /* set by the run command */
bool bInternal; /* Flags an internal run request */
char* name;
pAsyncUnit asyncUnit;
StateMachine fsm;
pNWTimer state_timer; /**< state timer */
int iValue; /* integer value from controller (0..63) */
int iPeriod; /* integer step-rate period in milliseconds */
};
typedef struct orhvps_s ORHVPSDriv, *pORHVPSDriv;
/* Functions */
static int ORHVPSGetValue( pEVDriver self, float* fPos);
static int ORHVPSSetValue( pEVDriver self, float fPos);
static int ORHVPSSend(pEVDriver self, char *pCommand, char *pReply, int iLen);
static int ORHVPSError(pEVDriver self, int *iCode, char *error, int iErrLen);
static int ORHVPSFix(pEVDriver self, int iError);
static int ORHVPSInit(pEVDriver self);
static int ORHVPSClose(pEVDriver self);
static void ORHVPSKillPrivate(void *pData);
static void ORHVPSNotify(void* context, int event);
static int get_period(float max, float rate) {
return (int) roundf(1000 * (max / 63.0) / rate);
}
/**
* \brief Sends a command and set up for a response event
*
* \param self motor data
* \param cmd command to send
* \param reply space to return response
* \return
*/
static int ORHV_SendCmd(pORHVPSDriv priv,
char* command,
int cmd_len,
AsyncTxnHandler callback)
{
pStateMachine sm = &priv->fsm;
return AsyncUnitSendTxn(priv->asyncUnit,
command, cmd_len,
callback, sm, CMDLEN);
}
/**
* \brief Sends a command and waits for a response
*
* \param priv motor data
* \param cmd command to send
* \param reply space to return response
* \return
*/
static int ORHV_SendReceive(pORHVPSDriv priv,
char *cmd,
int cmd_len,
char* reply,
int *rep_len) {
int status;
status = AsyncUnitTransact(priv->asyncUnit, cmd, cmd_len, reply, rep_len);
if (status != 1) {
return FAILURE;
}
return OKOK;
}
/* State Functions */
static void ORHVState_Unknown(pStateMachine sm, pEvtEvent event);
static void ORHVState_Idle(pStateMachine sm, pEvtEvent event);
static void ORHVState_Raising(pStateMachine sm, pEvtEvent event);
static void ORHVState_Lowering(pStateMachine sm, pEvtEvent event);
static void str_n_cat(char* s1, int len, const char* s2) {
int i = strlen(s1);
const char* p = s2;
while (i < len - 3 && *p) {
if (*p == '\r') {
s1[i++] = '\\';
s1[i++] = 'r';
++p;
}
else if (*p == '\n') {
s1[i++] = '\\';
s1[i++] = 'n';
++p;
}
else
s1[i++] = *p++;
}
s1[i] = '\0';
}
static const char* state_name(StateFunc func)
{
if (func == NULL) return "<null_state>";
if (func == ORHVState_Unknown) return "ORHVState_Unknown";
if (func == ORHVState_Idle) return "ORHVState_Idle";
if (func == ORHVState_Raising) return "ORHVState_Raising";
if (func == ORHVState_Lowering) return "ORHVState_Lowering";
return "<unknown_state>";
}
static const char* event_name(pEvtEvent event, char* text, int length)
{
char line[1024];
if (event == NULL)
return "<null_event>";
switch (event->event_type) {
case eStateEvent:
snprintf(text, length, "eStateEvent");
return text;
case eTimerEvent:
snprintf(text, length, "eTimerEvent (%d mSec)", event->event.tmr.timerValue);
return text;
case eMessageEvent:
snprintf(text, length, "eMessageEvent:");
fsm_textify(event->event.msg.cmd->out_buf,
event->event.msg.cmd->out_len,
line, sizeof(line));
str_n_cat(text, length, line);
str_n_cat(text, length, "|");
fsm_textify(event->event.msg.cmd->inp_buf,
event->event.msg.cmd->inp_idx,
line, sizeof(line));
str_n_cat(text, length, line);
return text;
case eCommandEvent:
/* TODO Command Events */
snprintf(text, length, "eCommandEvent:unknown");
return text;
case eTimeoutEvent:
snprintf(text, length, "eTimeoutEvent");
return text;
default:
snprintf(text, length, "<unknown_event>");
return text;
}
}
/**
* \brief parse the HV response for several consecutive states [bot, top]
*
* It captures the value in the first state and confirms the value in the last
* state. If any error occurs it returns -1 and resets the substate to bot.
* When the same value has been read for each substate from [bot, top] it
* returns 0 to confirm the value. As each value is confirmed it returns +1.
*
* \param sm pointer to the state machine
* \param pCmd pointer to the command and response
* \param bot first state (capture value)
* \param top last state confirm value
*
* \return -1 Error
* 0 sequence correct so far
* +1 sequence complete, can use the value
*/
static int getProcess(pStateMachine sm, pAsyncTxn pCmd, int bot, int top) {
pEVDriver driv = (pEVDriver) sm->context;
pORHVPSDriv priv = (pORHVPSDriv) driv->pPrivate;
int iRet = 1; /* Normal return value */
if (pCmd && pCmd->inp_idx == 3 && pCmd->inp_buf[0] == 'H' && pCmd->inp_buf[2] == 'z') {
int value = (pCmd->inp_buf[1]);
if (value >= 0 && value <= 63) {
if (sm->mySubState == bot) {
priv->iValue = value;
++sm->mySubState;
}
else if (value != priv->iValue) {
/* TODO log changed value */
sm->mySubState = bot;
iRet = -1; /* Error return value */
}
else if (sm->mySubState == top) {
/* final check passed, do calcs and transition */
priv->fValue = priv->iValue * (priv->fMax / 63.0);
++sm->mySubState;
return 0; /* Completed return value */
}
else {
/* recheck in next state */
++sm->mySubState;
}
}
else {
/* TODO log invalid value */
sm->mySubState = bot;
iRet = -1; /* Error return value */
}
}
else {
/* TODO log syntax error */
sm->mySubState = bot;
iRet = -1; /* Error return value */
}
return iRet;
}
/* State Functions */
/*
* Unknown State
*
* Handle initialisation and reset operations
*/
static void ORHVState_Unknown(pStateMachine sm, pEvtEvent event) {
pEVDriver driv = (pEVDriver) sm->context;
pORHVPSDriv priv = (pORHVPSDriv) driv->pPrivate;
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
priv->state_timer = NULL;
priv->bRunFlag = false;
ORHV_SendCmd(priv, "vz", 2, fsm_msg_callback);
sm->mySubState = 1;
return;
case eTimerEvent:
priv->state_timer = NULL;
return;
case eMessageEvent:
do {
pAsyncTxn pCmd = event->event.msg.cmd;
pCmd->inp_buf[pCmd->inp_idx] = '\0';
if (sm->mySubState == 1) {
/* Version Request */
char* p = strchr(pCmd->inp_buf, 'z');
if (p) {
char line[132];
*p = '\0';
sprintf(line, "Version: %s", pCmd->inp_buf);
SICSLogWrite(line, eStatus);
}
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
sm->mySubState = 2;
return;
}
if (sm->mySubState >= 2 && sm->mySubState <= 5) {
/* HV Get Request */
int iRet;
iRet = getProcess(sm, pCmd, 2, 5);
if (iRet == 0) {
priv->fTarget = priv->fValue;
fsm_change_state(sm, ORHVState_Idle);
return;
}
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
return;
}
} while (0);
return;
case eCommandEvent:
return;
case eTimeoutEvent:
ORHV_SendCmd(priv, "vz", 2, fsm_msg_callback);
sm->mySubState = 1;
return;
}
return;
}
/*
* Idle State
*
* Just monitoring what's going on
* and waiting to be told to run somewhere
*/
static void ORHVState_Idle(pStateMachine sm, pEvtEvent event){
pEVDriver driv = (pEVDriver) sm->context;
pORHVPSDriv priv = (pORHVPSDriv) driv->pPrivate;
int newVal;
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
NetWatchRegisterTimer(&priv->state_timer,
200,
fsm_tmr_callback, sm);
sm->mySubState = 1;
return;
case eTimerEvent:
priv->state_timer = NULL;
if (sm->mySubState == 6) {
if (priv->bRunFlag) {
priv->bRunFlag = false;
newVal = roundf(priv->fTarget / (priv->fMax / 63.0));
if (newVal > priv->iValue) {
fsm_change_state(sm, ORHVState_Raising);
return;
}
if (newVal < priv->iValue) {
fsm_change_state(sm, ORHVState_Lowering);
return;
}
}
/*
* There was either no commanded to move,
* on no need to move,
* so start another sequence
*/
sm->mySubState = 1;
}
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
return;
case eMessageEvent:
do {
pAsyncTxn pCmd = event->event.msg.cmd;
if (sm->mySubState >= 1 && sm->mySubState <= 5) {
/* HV Get Request */
int iRet;
iRet = getProcess(sm, pCmd, 1, 5);
if (iRet == 0) { /* final value OK */
sm->mySubState = 6;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
if (iRet < 0) { /* error, start again */
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
/* normal, just ask again for confirmation */
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
return;
}
} while (0);
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
case eCommandEvent:
return;
case eTimeoutEvent:
return;
}
return;
}
/* Raising State
*
* Increasing controlled value
*/
static void ORHVState_Raising(pStateMachine sm, pEvtEvent event){
pEVDriver driv = (pEVDriver) sm->context;
pORHVPSDriv priv = (pORHVPSDriv) driv->pPrivate;
int newVal;
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
NetWatchRegisterTimer(&priv->state_timer,
200,
fsm_tmr_callback, sm);
sm->mySubState = 1;
return;
case eTimerEvent:
priv->state_timer = NULL;
if (sm->mySubState == 1) {
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
return;
}
if (sm->mySubState == 6) {
char cmd[3];
cmd[0] = 'h';
cmd[1] = priv->iValue < 63 ? priv->iValue + 1 : priv->iValue;
cmd[2] = 'z';
ORHV_SendCmd(priv, cmd, 3, fsm_msg_callback);
return;
}
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
case eMessageEvent:
do {
pAsyncTxn pCmd = event->event.msg.cmd;
if (sm->mySubState >= 1 && sm->mySubState <= 5) {
/* HV Get Request */
int iRet;
iRet = getProcess(sm, pCmd, 1, 5);
if (iRet == 0) {
newVal = roundf(priv->fTarget / (priv->fMax / 63.0));
if (newVal == priv->iValue) {
fsm_change_state(sm, ORHVState_Idle);
return;
}
if (newVal < priv->iValue) {
fsm_change_state(sm, ORHVState_Lowering);
return;
}
sm->mySubState = 6;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
if (iRet < 0) {
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
return;
}
if (sm->mySubState == 6) {
/* HV Set Request */
if (*pCmd->inp_buf == 0x06) {
/* TODO: ACK */
}
else if (*pCmd->inp_buf == 0x15) {
/* TODO: NAK */
}
else {
/* TODO: ??? */
}
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
priv->iPeriod, /* TODO*/
fsm_tmr_callback, sm);
return;
}
} while (0);
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
case eCommandEvent:
return;
case eTimeoutEvent:
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
return;
}
/* Lowering State
*
* Decreasing controlled value
*/
static void ORHVState_Lowering(pStateMachine sm, pEvtEvent event){
pEVDriver driv = (pEVDriver) sm->context;
pORHVPSDriv priv = (pORHVPSDriv) driv->pPrivate;
char cmd[3];
int newVal;
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
NetWatchRegisterTimer(&priv->state_timer,
200,
fsm_tmr_callback, sm);
sm->mySubState = 1;
return;
case eTimerEvent:
priv->state_timer = NULL;
if (sm->mySubState == 1) {
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
return;
}
if (sm->mySubState == 6) {
cmd[0] = 'h';
cmd[1] = priv->iValue > 0 ? priv->iValue - 1 : priv->iValue;
cmd[2] = 'z';
ORHV_SendCmd(priv, cmd, 3, fsm_msg_callback);
return;
}
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
case eMessageEvent:
do {
pAsyncTxn pCmd = event->event.msg.cmd;
pCmd->inp_buf[pCmd->inp_idx] = '\0';
if (sm->mySubState >= 1 && sm->mySubState <= 5) {
/* HV Get Request */
int iRet;
iRet = getProcess(sm, pCmd, 1, 5);
if (iRet == 0) {
newVal = roundf(priv->fTarget / (priv->fMax / 63.0));
if (newVal == priv->iValue) {
fsm_change_state(sm, ORHVState_Idle);
return;
}
if (newVal > priv->iValue) {
fsm_change_state(sm, ORHVState_Raising);
return;
}
sm->mySubState = 6;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
if (iRet < 0) {
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
ORHV_SendCmd(priv, "Hz", 2, fsm_msg_callback);
return;
}
if (sm->mySubState == 6) { /* HV Set Request */
if (*pCmd->inp_buf == 0x06) {
/* TODO: ACK */
}
else if (*pCmd->inp_buf == 0x15) {
/* TODO: NAK */
}
else {
/* TODO: ??? */
}
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
priv->iPeriod, /* TODO*/
fsm_tmr_callback, sm);
return;
}
} while (0);
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
case eCommandEvent:
return;
case eTimeoutEvent:
sm->mySubState = 1;
NetWatchRegisterTimer(&priv->state_timer,
100, /* TODO*/
fsm_tmr_callback, sm);
return;
}
return;
}
/*
* Return the current value to SICS
*/
static int ORHVPSGetValue( pEVDriver self, float* fPos) {
pORHVPSDriv priv = NULL;
int newVal;
assert(self);
assert(self->pPrivate);
priv = (pORHVPSDriv) self->pPrivate;
newVal = roundf(priv->fTarget / (priv->fMax / 63.0));
if (newVal == priv->iValue)
*fPos = priv->fTarget;
else
*fPos = priv->fValue;
return 1;
}
/*
* Set the current value from SICS
*/
static int ORHVPSSetValue( pEVDriver self, float fPos) {
pORHVPSDriv priv = NULL;
assert(self);
assert(self->pPrivate);
priv = (pORHVPSDriv) self->pPrivate;
if (priv->isLocked && !priv->bInternal) {
priv->iErrorCode = ORHVPS_ERR_LOCKED;
return 0;
}
if (fPos < 0.0 || fPos > priv->fMax) {
priv->iErrorCode = ORHVPS_ERR_RANGE;
return 0;
}
priv->fTarget = fPos;
priv->bRunFlag = true;
return 1;
}
/*
* Send a command from SICS
*/
static int ORHVPSSend(pEVDriver self, char *pCommand, char *pReply, int iLen) {
char cmd[CMDLEN];
int cmd_len;
char rsp[CMDLEN];
int rsp_len;
int idx = 0;
int i;
cmd[0] = '\0';
if (pCommand && *pCommand && pReply && iLen > 0) {
for (i = 0; pCommand[i]; ++i) {
int k;
if (pCommand[i] == '\\') {
k = 0;
if (isxdigit(pCommand[i+1]) && isxdigit(pCommand[i+2])) {
if (pCommand[i+1] >= '0' && pCommand[i+1] <= '9')
k = (pCommand[i+1] - '0') << 4;
else if (pCommand[i+1] >= 'a' && pCommand[i+1] <= 'f')
k = (pCommand[i+1] - 'a' + 10) << 4;
else if (pCommand[i+1] >= 'A' && pCommand[i+1] <= 'F')
k = (pCommand[i+1] - 'A' + 10) << 4;
if (pCommand[i+2] >= '0' && pCommand[i+2] <= '9')
k += (pCommand[i+2] - '0');
else if (pCommand[i+2] >= 'a' && pCommand[i+2] <= 'f')
k += (pCommand[i+2] - 'a' + 10);
else if (pCommand[i+2] >= 'A' && pCommand[i+2] <= 'F')
k += (pCommand[i+2] - 'A' + 10);
i += 2;
}
}
else
k = pCommand[i];
if (idx < CMDLEN)
cmd[idx++] = k;
}
if (idx < CMDLEN)
cmd[idx] = '\0';
cmd_len = idx;
rsp_len = CMDLEN;
ORHV_SendReceive(self->pPrivate, cmd, cmd_len, rsp, &rsp_len);
idx = 0;
for (i = 0; i < rsp_len && idx < iLen - 1; ++i) {
if (rsp[i] < 32 || rsp[i] > 126) {
idx+=sprintf(&pReply[idx], "%02Xh", rsp[i]);
}
else if (idx < iLen)
pReply[idx++] = rsp[i];
}
if (idx < iLen)
pReply[idx++] = '\0';
return 1;
}
return -1;
}
/*
* SICS error handler
*/
static int ORHVPSError(pEVDriver self, int *iCode, char *error, int iErrLen) {
pORHVPSDriv priv = (pORHVPSDriv) self->pPrivate;
*iCode = priv->iErrorCode;
switch (priv->iErrorCode) {
case ORHVPS_ERR_RANGE:
strncpy(error,"Value out of range",iErrLen);
break;
case ORHVPS_ERR_LOCKED:
strncpy(error,"Object is locked",iErrLen);
break;
default:
strncpy(error,"TODO Error Messages",iErrLen);
break;
}
return 1;
}
/*
* SICS fix handler
*/
static int ORHVPSFix(pEVDriver self, int iError) {
/* TODO */
return DEVFAULT;
}
static int ORHVPSInit(pEVDriver self) {
/* TODO */
return 1;
}
static int ORHVPSClose(pEVDriver self) {
/* TODO */
return -1;
}
static void ORHVPSKillPrivate(void *pData) {
pORHVPSDriv pMe = (pORHVPSDriv) pData;
if (pMe) {
if (pMe->asyncUnit) {
AsyncUnitDestroy(pMe->asyncUnit);
pMe->asyncUnit = NULL;
}
if (pMe ->name) {
free(pMe ->name);
pMe ->name = NULL;
}
/* Not required as performed in caller
* free(pMe);
*/
return;
}
}
/*
* Protocol transmit function
* Called by AsyncQueue to transmit a line
*/
static int ORHVPS_Tx(pAsyncProtocol p, pAsyncTxn myCmd) {
int iRet = 1;
if (myCmd) {
myCmd->txn_status = ATX_ACTIVE;
iRet = AsyncUnitWrite(myCmd->unit, myCmd->out_buf, myCmd->out_len);
/* TODO handle errors */
if (iRet < 0) { /* TODO: EOF */
/*
iRet = AsyncUnitReconnect(myCmd->unit);
if (iRet == 0)
*/
return 0;
}
}
return 1;
}
/*
* Protocol receive character - characater by character
*/
static int ORHVPS_Rx(pAsyncProtocol p, pAsyncTxn ctx, int rxchar) {
int iRet = 1;
pAsyncTxn myCmd = (pAsyncTxn) ctx;
switch (myCmd->txn_state) {
case 0: /* first character */
if (rxchar == 0x06) { /* ACK */
/* normal prompt */
myCmd->txn_state = 99;
myCmd->txn_status = ATX_COMPLETE;
}
else if (rxchar == 0x15) { /* NAK */
myCmd->txn_state = 99;
myCmd->txn_status = ATX_COMPLETE;
}
else {
/* normal data */
myCmd->txn_state = 1;
}
/* note fallthrough */
case 1: /* receiving reply */
if (myCmd->inp_idx < myCmd->inp_len)
myCmd->inp_buf[myCmd->inp_idx++] = rxchar;
if (rxchar == 'z')
myCmd->txn_state = 99;
break;
}
if (myCmd->txn_state == 99) {
iRet = 0;
}
if (iRet == 0) { /* end of command */
return AQU_POP_CMD;
}
return iRet;
}
/*
* AsyncUnit Notify Callback
*/
static void ORHVPSNotify(void* context, int event)
{
/* TODO */
#if 0
pEVDriver self = (pEVDriver) context;
char line[132];
switch (event) {
case AQU_DISCONNECT:
snprintf(line, 132, "Disconnect on Device '%s'", self->name);
SICSLogWrite(line, eStatus);
/* TODO: disconnect */
break;
case AQU_RECONNECT:
snprintf(line, 132, "Reconnect on Device '%s'", self->name);
SICSLogWrite(line, eStatus);
/* TODO: reconnect */
if (self->has_fsm) {
/* Reset the state machine */
if (self->state_timer)
NetWatchRemoveTimer(self->state_timer);
self->state_timer = 0;
change_state(self, DMCState_Unknown);
/* Schedule a timer event as soon as possible */
NetWatchRegisterTimer(&self->state_timer,
0,
state_tmr_callback, self);
}
break;
}
#endif
return;
}
/*
* AsyncProtocol Event callback
*/
static int ORHVPS_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) {
if (event == AQU_TIMEOUT) {
/* handle command timeout */
pTxn->txn_status = ATX_TIMEOUT;
return AQU_POP_CMD;
}
return AQU_POP_CMD;
}
static int ORHVPS_PrepareTxn(pAsyncProtocol p, pAsyncTxn txn, const char* cmd, int cmd_len, int rsp_len) {
txn->out_buf = (char*) malloc(cmd_len);
if (txn->out_buf == NULL) {
SICSLogWrite("ERROR: Out of memory in ORHVPS_PrepareTxn", eError);
return 0;
}
memcpy(txn->out_buf, cmd, cmd_len);
txn->out_len = cmd_len;
return 1;
}
static pAsyncProtocol ORHVPS_Protocol = NULL;
/*
* Protocol Initialisation
*/
void ORHVPSInitProtocol(SicsInterp *pSics) {
if (ORHVPS_Protocol == NULL) {
ORHVPS_Protocol = AsyncProtocolCreate(pSics, "ORHVPS", NULL, NULL);
ORHVPS_Protocol->sendCommand = ORHVPS_Tx;
ORHVPS_Protocol->handleInput = ORHVPS_Rx;
ORHVPS_Protocol->handleEvent = ORHVPS_Ev;
ORHVPS_Protocol->prepareTxn = ORHVPS_PrepareTxn;
ORHVPS_Protocol->killPrivate = NULL;
}
}
/*
* Device Factory
*/
pEVDriver CreateORHVPSDriver(int argc, char *argv[])
{
pEVDriver self = NULL;
pORHVPSDriv priv = NULL;
if (argc < 1)
return NULL;
self = CreateEVDriver(argc, argv);
if (!self)
return NULL;
priv = (pORHVPSDriv)malloc(sizeof(ORHVPSDriv));
if(!priv) {
DeleteEVDriver(self);
return NULL;
}
memset(priv,0,sizeof(ORHVPSDriv));
priv->fValue = 0.0;
if (!AsyncUnitCreate(argv[0], &priv->asyncUnit)) {
char line[132];
snprintf(line, 132, "Error: did not find AsyncQueue %s for Device %s", argv[1], argv[0]);
SICSLogWrite(line, eError);
DeleteEVDriver(self);
free(priv);
return NULL;
}
AsyncUnitSetNotify(priv->asyncUnit, self, ORHVPSNotify);
/* initialise function pointers */
self->SetValue = ORHVPSSetValue;
self->GetValue = ORHVPSGetValue;
self->Send = ORHVPSSend;
self->GetError = ORHVPSError;
self->TryFixIt = ORHVPSFix;
self->Init = ORHVPSInit;
self->Close = ORHVPSClose;
self->pPrivate = priv;
self->KillPrivate = ORHVPSKillPrivate;
priv->fsm.context = self;
priv->fsm.state_name = state_name;
priv->fsm.event_name = event_name;
priv->name = strdup(argv[0]);
priv->fMax = 2400.0;
priv->fRate = 10.0;
priv->fLower = 0.0;
priv->fUpper = 1800.0;
priv->iPeriod = get_period(priv->fMax, priv->fRate);
priv->bRunFlag = false;
return self;
}
/*
* Register the controller with the driver
*/
void ORHVPSRegister(pEVControl self, pEVDriver driv)
{
pORHVPSDriv priv = (pORHVPSDriv) driv->pPrivate;
priv->controller = self;
if (self->pName) {
if (priv->name)
free(priv->name);
priv->name = strdup(self->pName);
priv->fsm.name = priv->name;
}
fsm_change_state(&priv->fsm, ORHVState_Unknown);
while (priv->fsm.myState == ORHVState_Unknown) {
pTaskMan pTasker = GetTasker();
TaskYield(pTasker);
}
}
/*
* Action Wrapper routine
*/
int ORHVPSWrapper(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
pEVControl self = (pEVControl)pData;
pEVDriver driv = self->pDriv;
pORHVPSDriv priv = (pORHVPSDriv) driv->pPrivate;
assert(self);
assert(pCon);
assert(pSics);
if(argc < 2)
{
return EVControlWrapper(pCon,pSics,pData,argc,argv);
}
if (strcasecmp("send", argv[1]) == 0) {
char cmd[CMDLEN];
int cmd_len;
char rsp[CMDLEN];
int rsp_len;
int idx = 0;
int i;
/* Managers only */
if (!SCMatchRights(pCon, usMugger))
return 0;
cmd[idx] = '\0';
for (i = 2; i < argc; ++i) {
int j, k;
if (i > 2 && idx < CMDLEN)
cmd[idx++] = ' ';
for (j = 0; argv[i][j]; ++j) {
if (argv[i][j] == '\\') {
k = 0;
if (isxdigit(argv[i][j+1]) && isxdigit(argv[i][j+2])) {
if (argv[i][j+1] >= '0' && argv[i][j+1] <= '9')
k = (argv[i][j+1] - '0') << 4;
else if (argv[i][j+1] >= 'a' && argv[i][j+1] <= 'f')
k = (argv[i][j+1] - 'a' + 10) << 4;
else if (argv[i][j+1] >= 'A' && argv[i][j+1] <= 'F')
k = (argv[i][j+1] - 'A' + 10) << 4;
if (argv[i][j+2] >= '0' && argv[i][j+2] <= '9')
k += (argv[i][j+2] - '0');
else if (argv[i][j+2] >= 'a' && argv[i][j+2] <= 'f')
k += (argv[i][j+2] - 'a' + 10);
else if (argv[i][j+2] >= 'A' && argv[i][j+2] <= 'F')
k += (argv[i][j+2] - 'A' + 10);
j += 2;
}
}
else
k = argv[i][j];
if (idx < CMDLEN)
cmd[idx++] = k;
}
if (idx < CMDLEN)
cmd[idx] = '\0';
cmd_len = idx;
}
rsp_len = CMDLEN;
ORHV_SendReceive(priv, cmd, cmd_len, rsp, &rsp_len);
idx = 0;
for (i = 0; i < rsp_len && idx < CMDLEN - 1; ++i) {
if (rsp[i] < 32 || rsp[i] > 126) {
idx+=sprintf(&cmd[idx], "%02Xh", rsp[i]);
}
else
cmd[idx++] = rsp[i];
}
if (idx < CMDLEN)
cmd[idx++] = '\0';
SCWrite(pCon, cmd, eValue);
return 1;
}
if (strcasecmp("up", argv[1]) == 0 || strcasecmp("down", argv[1]) == 0) {
int iRet;
if(!SCMatchRights(pCon,usUser))
return 0;
if (strcasecmp("up", argv[1]) == 0)
priv->fTarget = priv->fUpper;
else
priv->fTarget = priv->fLower;
if (priv->fTarget > priv->fMax)
priv->fTarget = priv->fMax;
if (priv->fTarget < 0)
priv->fTarget = 0.0;
priv->bInternal = true;
iRet = EVCDrive(priv->controller, pCon, priv->fTarget);
priv->bInternal = false;
if(iRet)
SCSendOK(pCon);
return iRet;
}
if (strcasecmp("off", argv[1]) == 0) {
int iRet;
if(!SCMatchRights(pCon,usUser))
return 0;
priv->fTarget = 0.0;
priv->bInternal = true;
iRet = EVCDrive(priv->controller, pCon, priv->fTarget);
priv->bInternal = false;
if(iRet)
SCSendOK(pCon);
return iRet;
}
if (strcasecmp("upper", argv[1]) == 0) {
char rsp[CMDLEN];
if (argc > 2) {
if (!SCMatchRights(pCon, usUser))
return 0;
if (priv->isLocked && !SCMatchRights(pCon, usMugger)) {
SCWrite(pCon, "object is locked", eError);
return 0;
}
else {
float value = atof(argv[2]);
if (value >= 0.0 && value <= priv->fMax) {
priv->fUpper = value;
}
else {
SCWrite(pCon, "upper value must be between 0.0 and <maximum>", eError);
return 0;
}
}
}
snprintf(rsp, CMDLEN, "%s.upper = %8.2f", priv->name, priv->fUpper);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("lower", argv[1]) == 0) {
char rsp[CMDLEN];
if (argc > 2) {
if (!SCMatchRights(pCon, usUser))
return 0;
if (priv->isLocked && !SCMatchRights(pCon, usMugger)) {
SCWrite(pCon, "object is locked", eError);
return 0;
}
else {
float value = atof(argv[2]);
if (value >= 0.0 && value <= priv->fMax) {
priv->fLower = value;
}
else {
SCWrite(pCon, "lower value must be between 0.0 and <maximum>", eError);
return 0;
}
}
}
snprintf(rsp, CMDLEN, "%s.lower = %8.2f", priv->name, priv->fLower);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("max", argv[1]) == 0) {
char rsp[CMDLEN];
if (argc > 2) {
if (!SCMatchRights(pCon, usUser))
return 0;
if (priv->isLocked) {
SCWrite(pCon, "object is locked", eError);
return 0;
}
else {
float value = atof(argv[2]);
if (value >= 0.0 && value <= MY_ABSOLUTE_MAXIMUM) {
priv->fMax = value;
priv->iPeriod = get_period(priv->fMax, priv->fRate);
}
else {
char line[CMDLEN];
snprintf(line, CMDLEN, "max must be between 0.0 and %.0f", MY_ABSOLUTE_MAXIMUM);
SCWrite(pCon, line, eError);
return 0;
}
}
}
snprintf(rsp, CMDLEN, "%s.max = %8.2f", priv->name, priv->fMax);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("debug", argv[1]) == 0 && SCMatchRights(pCon, usMugger)) {
char rsp[CMDLEN];
if (argc > 2) {
int debug = atoi(argv[2]);
if (debug != 0)
priv->fsm.debug = true;
else
priv->fsm.debug = false;
}
snprintf(rsp, CMDLEN, "%s.debug = %d", priv->name, priv->fsm.debug ? 1 : 0);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("rate", argv[1]) == 0) {
char rsp[CMDLEN];
if (argc > 2) {
if (!SCMatchRights(pCon, usUser))
return 0;
if (priv->isLocked && !SCMatchRights(pCon, usMugger)) {
SCWrite(pCon, "object is locked", eError);
return 0;
}
else {
float value = atof(argv[2]);
if (value >= MY_MINIMUM_RATE && value <= MY_MAXIMUM_RATE) {
priv->fRate = value;
priv->iPeriod = get_period(priv->fMax, priv->fRate);
}
else {
char line[CMDLEN];
snprintf(line, CMDLEN, "rate must be between %.0f and %.0f", MY_MINIMUM_RATE, MY_MAXIMUM_RATE);
SCWrite(pCon, line, eError);
return 0;
}
}
}
snprintf(rsp, CMDLEN, "%s.rate = %8.2f", priv->name, priv->fRate);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("reset", argv[1]) == 0) {
char line[CMDLEN];
if (!SCMatchRights(pCon, usUser))
return 0;
/* restart state machine */
fsm_change_state(&priv->fsm, ORHVState_Unknown);
while (priv->fsm.myState == ORHVState_Unknown) {
pTaskMan pTasker = GetTasker();
TaskYield(pTasker);
}
/* give feedback */
snprintf(line, CMDLEN, "%s.reset = 1", priv->name);
SCWrite(pCon, line, eValue);
return 1;
}
if (strcasecmp("lock", argv[1]) == 0) {
char rsp[CMDLEN];
if (!SCMatchRights(pCon, usUser))
return 0;
priv->isLocked = 1;
snprintf(rsp, CMDLEN, "%s.lock = %d", priv->name, priv->isLocked);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("unlock", argv[1]) == 0
&& SCMatchRights(pCon, usMugger)
&& priv->fsm.debug) {
char rsp[CMDLEN];
priv->isLocked = 0;
snprintf(rsp, CMDLEN, "%s.lock = %d", priv->name, priv->isLocked);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("list", argv[1]) == 0) {
int iRet;
iRet = EVControlWrapper(pCon,pSics,pData,argc,argv);
if (iRet) {
char rsp[CMDLEN];
snprintf(rsp, CMDLEN, "%s.lower = %8.2f", priv->name, priv->fLower);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.upper = %8.2f", priv->name, priv->fUpper);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.max = %8.2f", priv->name, priv->fMax);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.rate = %8.2f", priv->name, priv->fRate);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.period = %6d", priv->name, priv->iPeriod);
SCWrite(pCon, rsp, eValue);
}
return iRet;
}
if (strcasecmp("period", argv[1]) == 0) {
char line[CMDLEN];
if (argc > 2) {
if (!SCMatchRights(pCon, usUser))
return 0;
SCWrite(pCon, "cannot set period, set rate instead", eError);
return 0;
}
snprintf(line, CMDLEN, "%s.period = %6d", priv->name, priv->iPeriod);
SCWrite(pCon, line, eValue);
return 1;
}
return EVControlWrapper(pCon,pSics,pData,argc,argv);
}