Files
sics/site_ansto/ls340.c
Douglas Clowes eb41f23ee2 Reworked AsyncQueue sendCommand processing
Squashed commit of the following:

commit 42fb7d3cde591d40060cc740ccbc47f1ae7a5a50
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Tue Aug 26 13:31:11 2014 +1000

    Get the MODBUS_AP working

commit da785c1434a04c4186d4174eb2dfbaefc850c8e7
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Mon Aug 25 18:01:50 2014 +1000

    Bring Modbus protocol closer to Huber, Knauer and Omron

commit ef06ed7b6911cb49b35c19fe73e55f7c57cfd049
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Mon Aug 25 18:01:18 2014 +1000

    Make Huber, Knauer and Omron protocols more aligned (diffable)

commit 3ef1bb06b3f865502ad7dffc4bf5dba4814d9334
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Fri Aug 22 17:47:50 2014 +1000

    Get the Huber and Knauer protocols to be more alike

commit 2c9932e83f6735e894278648afdcadece654d43b
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Fri Aug 22 17:12:31 2014 +1000

    Clean up the Knauer dual-mode protocol and refactor

commit 333300b19b0e61916e261300ac6ae2b6bab5df09
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 15:38:39 2014 +1000

    Get the Knauer dual-mode protocol working(-ish)

commit b1f9d82f1b9eb8a1ff54694adc3482984b0d3d72
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 15:37:44 2014 +1000

    Make private functions static (and not duplicated)

commit 0b077414eef9e4351956a2b971d7751cced0d3cd
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 12:46:10 2014 +1000

    Knauer moving toward dual protocol

commit 13199bea38a1595ce06923e83474b738b10db94d
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 12:42:48 2014 +1000

    Restructure default sendCommand processing in asyncqueue

commit 99a8ea3174ca0636503b0ce0cdb6016790315558
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 09:48:50 2014 +1000

    Add a Modbus Protocol handler derived from sct_tcpmodbus

commit 3adf49fb7c8402c8260a0bb20729d551ac88537b
Author: Douglas Clowes <dcl@ansto.gov.au>
Date:   Thu Aug 21 09:43:54 2014 +1000

    Leave the free of private data to the asyncqueue mechanism
2014-08-26 14:30:19 +10:00

1298 lines
37 KiB
C

/*-------------------------------------------------------------------------
LS340 - LAKESHORE340
Support for the Lakeshore 340 Temperature Controller
The meaning and working of the functions defined is as desribed for a
general environment controller.
Adapted by Rodney Davies from orhvps code written by:
Douglas Clowes, December 2007
Copyright:
Labor fuer Neutronenstreuung
Paul Scherrer Institut
CH-5423 Villigen-PSI
The authors hereby grant permission to use, copy, modify, distribute,
and license this software and its documentation for any purpose, provided
that existing copyright notices are retained in all copies and that this
notice is included verbatim in any distributions. No written agreement,
license, or royalty fee is required for any of the authorized uses.
Modifications to this software may be copyrighted by their authors
and need not follow the licensing terms described here, provided that
the new terms are clearly indicated on the first page of each file where
they apply.
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
----------------------------------------------------------------------------
*/
#include "ls340.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 64
#define LS340_ERR_NONE (0)
#define LS340_ERR_LOCKED (-1)
#define LS340_ERR_RANGE (-2)
#define NUM_INPUT_SENSORS 10 /* Maximum Number of Input Sensors */
char *strcasestr(const char *hay, const char *needle);
/* Note: data structure supports Lakeshore 340 with 10101010101010101010 sensor inputs and 2 PID Loops */
typedef struct ls340_s {
pEVControl controller;
int iPidLoop; /* PID Loop number 1..2 */
int iCtrlSens; /* control sensor (this is the index in the list below, not the Lakeshore sensor number) 0..NUM_INPUT_SENSORS-1 */
char cCtrlSensName[3]; /* control sensor name eg: A1, B, C, etc */
char cValidSensors[NUM_INPUT_SENSORS][3]; /* list of valid sensors for this pid loop eg: A, A1, B4, C, D etc*/
float fSensorValues[NUM_INPUT_SENSORS]; /* each valid sensor value */
int iNumSensors; /* number of input sensors */
float fSetPoint; /* setpoint temperature of control loop */
int iError; /* error code */
float fSetPointLimit; /* Setpoint temperature limit */
float fPosSlope; /* Positive Slope value */
float fNegSlope; /* Negative Slope value */
int iMaxCurrent; /* Maximum Current on output */
int iMaxRange; /* Heater Range 0..5 */
int iRange; /* Heater Range 0..5 (0 being off) */
int iHeaterStatus; /* heater status - 1 - on, 0 - off */
float fValue; /* current temperature of control sensor in degrees K */
float fTarget; /* requested target temperature */
int iSettleTime; /* settling time (sec) */
float fTolerance; /* settline tolerance level 0..100 */
bool isLocked; /* changes no longer permitted */
unsigned long ulIdleDelay; /* idle timer timeout duration in milliseconds */
unsigned long ulIdlePollRate; /* idle timer timeout duration in milliseconds */
unsigned long ulMonitorPollRate; /* Monitoring sensor polling cycle duration in milliseconds */
unsigned long ulMonitorDelay; /* Monitoring timer timeout duration in milliseconds */
char* name;
pAsyncUnit asyncUnit;
StateMachine fsm;
pNWTimer state_timer; /**< state timer */
} LS340Driv, *pLS340Driv;
static int LS340GetValue( pEVDriver self, float* fPos);
static int LS340SetValue( pEVDriver self, float fPos);
static int LS340Send(pEVDriver self, char *pCommand, char *pReply, int iLen);
static int LS340Error(pEVDriver self, int *iCode, char *error, int iErrLen);
static int LS340Fix(pEVDriver self, int iError);
static int LS340Init(pEVDriver self);
static int LS340Close(pEVDriver self);
static void LS340KillPrivate(void *pData);
static void LS340Notify(void* context, int event);
/* get the index in the valid sensors list of a given sensor name */
static int getSensorIndex(pLS340Driv priv, char sname[]) {
int i;
for (i = 0; i < priv->iNumSensors; i++) {
if (strcmp(priv->cValidSensors[i], sname) == 0)
return i;
}
return -1;
}
static int LS340_SendCmd(pLS340Driv self,
char* command,
int cmd_len,
AsyncTxnHandler callback)
{
pStateMachine sm = &self->fsm;
return AsyncUnitSendTxn(self->asyncUnit,
command, cmd_len,
callback, sm, CMDLEN);
}
/**
* \brief Sends a command and waits for a response
*
* \param self motor data
* \param cmd command to send
* \param reply space to return response
* \return
*/
static int LS340_SendReceive(pLS340Driv self,
char *cmd,
int cmd_len,
char* reply,
int *rep_len) {
int status;
status = AsyncUnitTransact(self->asyncUnit, cmd, cmd_len, reply, rep_len);
if (status != 1) {
return FAILURE;
}
return OKOK;
}
static void LS340State_Unknown(pStateMachine sm, pEvtEvent event);
static void LS340State_Idle(pStateMachine sm, pEvtEvent event);
static void LS340State_Raising(pStateMachine sm, pEvtEvent event);
static void LS340State_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 == LS340State_Unknown) return "LS340State_Unknown";
if (func == LS340State_Idle) return "LS340State_Idle";
if (func == LS340State_Raising) return "LS340State_Raising";
if (func == LS340State_Lowering) return "LS340State_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");
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;
}
}
static void LS340State_Unknown(pStateMachine sm, pEvtEvent event) {
char cmd[CMDLEN];
pEVDriver driv = (pEVDriver) sm->context;
pLS340Driv priv = (pLS340Driv) driv->pPrivate;
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
priv->state_timer = NULL;
sprintf(cmd, "*IDN?");
LS340_SendCmd(priv, cmd, strlen(cmd), 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) {
/* IDN Request */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
sprintf(line, "IDN: %s", pCmd->inp_buf);
SICSLogWrite(line, eLog);
}
#if 0
sprintf(cmd, "RANGE 0"); /* ensure heater is off */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
#endif
sprintf(cmd, "CSET %d, %s, 1, %d, 1", priv->iPidLoop, priv->cValidSensors[priv->iCtrlSens], priv->iHeaterStatus);
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
sprintf(cmd, "CLIMIT? %d", priv->iPidLoop);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sm->mySubState = 2;
return;
}
if (sm->mySubState == 2) {
/* Handle CLIMIT data */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
sprintf(line, "CLIMIT (Unknown): %s", pCmd->inp_buf);
SICSLogWrite(line, eLog);
sscanf(pCmd->inp_buf, "%f,%f,%f,%d,%d", &priv->fSetPointLimit, &priv->fPosSlope, &priv->fNegSlope, &priv->iMaxCurrent, &priv->iMaxRange);
}
sprintf(cmd, "SETP? %d", priv->iPidLoop);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sm->mySubState = 3;
return;
}
if (sm->mySubState == 3) {
/* Handle SETP? data */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
sprintf(line, "SETP: %s", pCmd->inp_buf);
SICSLogWrite(line, eLog);
sscanf(pCmd->inp_buf, "%f", &priv->fSetPoint);
priv->fTarget = priv->fSetPoint; /* Set target and setpoints to current values from controller */
}
sprintf(cmd, "KRDG? %s", priv->cValidSensors[0]);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sm->mySubState = 4;
return;
}
if (sm->mySubState >= 4 && sm->mySubState <= priv->iNumSensors+3) {
/* KRDG? Requests */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
priv->fSensorValues[sm->mySubState-4] = atof(pCmd->inp_buf);
if (sm->mySubState-4 == priv->iCtrlSens) {
priv->fValue = priv->fSensorValues[sm->mySubState-4];
}
sprintf(line, "KRDG? (Unknown) sensor %s = %8.2f", priv->cValidSensors[sm->mySubState-4], priv->fSensorValues[sm->mySubState-4]);
SICSLogWrite(line, eLog);
}
if (sm->mySubState >= priv->iNumSensors+3) {
fsm_change_state(sm, LS340State_Idle);
return;
}
sm->mySubState++;
sprintf(cmd, "KRDG? %s", priv->cValidSensors[sm->mySubState-4]);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
return;
}
} while (0);
return;
case eCommandEvent:
return;
case eTimeoutEvent:
sprintf(cmd, "*IDN?");
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sm->mySubState = 1;
return;
}
return;
}
static void LS340State_Idle(pStateMachine sm, pEvtEvent event){
char cmd[CMDLEN];
pEVDriver driv = (pEVDriver) sm->context;
pLS340Driv priv = (pLS340Driv) driv->pPrivate;
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
NetWatchRegisterTimer(&priv->state_timer,
priv->ulIdleDelay,
fsm_tmr_callback, sm);
sprintf(cmd, "CLIMIT? %d", priv->iPidLoop);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sm->mySubState = 1;
return;
case eMessageEvent:
do {
pAsyncTxn pCmd = event->event.msg.cmd;
pCmd->inp_buf[pCmd->inp_idx] = '\0';
if (sm->mySubState == 1) { /* CLIMIT message */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
sprintf(line, "CLIMIT (Idle): %s", pCmd->inp_buf);
SICSLogWrite(line, eLog);
sscanf(pCmd->inp_buf, "%f,%f,%f,%d,%d", &priv->fSetPointLimit, &priv->fPosSlope, &priv->fNegSlope, &priv->iMaxCurrent, &priv->iMaxRange);
}
sm->mySubState++;
} else if (sm->mySubState >= 2 && sm->mySubState <= priv->iNumSensors+1) {
/* KRDG? Requests */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
priv->fSensorValues[sm->mySubState-2] = atof(pCmd->inp_buf);
if (sm->mySubState-2 == priv->iCtrlSens) priv->fValue = priv->fSensorValues[sm->mySubState-2];
sprintf(line, "KRDG? (Idle) sensor %s = %8.2f", priv->cValidSensors[sm->mySubState-2], priv->fSensorValues[sm->mySubState-2]);
SICSLogWrite(line, eLog);
}
if (sm->mySubState >= priv->iNumSensors+1) {
fsm_change_state(sm, LS340State_Idle);
return;
}
sm->mySubState++;
return;
}
} while (0);
return;
case eTimerEvent:
priv->state_timer = NULL;
if (priv->controller) {
char line[132];
sprintf(line, "LS340 eMode: = %d", priv->controller->eMode);
SICSLogWrite(line, eLog);
}
if (priv->fTarget > priv->fSetPoint) {
priv->fSetPoint = priv->fTarget; /* set the setpoint to be the target */
priv->iHeaterStatus = 1;
sprintf(cmd, "SETP %d, %8.2f", priv->iPidLoop, priv->fSetPoint); /* tell the Lakeshore to change the current setpoint to target */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
fsm_change_state(sm, LS340State_Raising);
return;
}
if (priv->fTarget < priv->fSetPoint) {
priv->fSetPoint = priv->fTarget; /* set the setpoint to be the target */
priv->iHeaterStatus = 1;
sprintf(cmd, "SETP %d, %8.2f", priv->iPidLoop, priv->fSetPoint); /* tell the Lakeshore to change the current setpoint to target */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
fsm_change_state(sm, LS340State_Lowering);
return;
}
if (sm->mySubState >= 2 && sm->mySubState <= priv->iNumSensors+1) {
char line[132];
sprintf(cmd, "KRDG? %s", priv->cValidSensors[sm->mySubState-2]);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sprintf(line, "eTimerEvent (Idle): Sent: %s", cmd);
SICSLogWrite(line, eLog);
}
/* restart timer */
NetWatchRegisterTimer(&priv->state_timer,
priv->ulIdleDelay,
fsm_tmr_callback, sm);
return;
case eCommandEvent:
return;
case eTimeoutEvent:
return;
}
return;
}
static void LS340State_Raising(pStateMachine sm, pEvtEvent event){
pEVDriver driv = (pEVDriver) sm->context;
pLS340Driv priv = (pLS340Driv) driv->pPrivate;
char cmd[CMDLEN];
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
NetWatchRegisterTimer(&priv->state_timer,
priv->ulMonitorDelay,
fsm_tmr_callback, sm);
sprintf(cmd, "RANGE %d", priv->iRange); /* set the Range value > 0 turns heater on */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
sprintf(cmd, "CLIMIT? %d", priv->iPidLoop);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sm->mySubState = 1;
return;
case eMessageEvent:
do {
pAsyncTxn pCmd = event->event.msg.cmd;
pCmd->inp_buf[pCmd->inp_idx] = '\0';
if (sm->mySubState == 1) { /* CLIMIT message */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
sprintf(line, "CLIMIT (Raising): %s", pCmd->inp_buf);
SICSLogWrite(line, eLog);
sscanf(pCmd->inp_buf, "%f,%f,%f,%d,%d", &priv->fSetPointLimit, &priv->fPosSlope, &priv->fNegSlope, &priv->iMaxCurrent, &priv->iMaxRange);
}
sm->mySubState++;
} else if (sm->mySubState >= 2 && sm->mySubState <= priv->iNumSensors+1) {
/* KRDG? Requests */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
priv->fSensorValues[sm->mySubState-2] = atof(pCmd->inp_buf);
if (sm->mySubState-2 == priv->iCtrlSens) priv->fValue = priv->fSensorValues[sm->mySubState-2];
sprintf(line, "KRDG? (Raising) sensor %s = %8.2f", priv->cValidSensors[sm->mySubState-2], priv->fSensorValues[sm->mySubState-2]);
SICSLogWrite(line, eLog);
}
sm->mySubState++;
}
/* restart timer */
NetWatchRegisterTimer(&priv->state_timer,
priv->ulMonitorDelay,
fsm_tmr_callback, sm);
} while (0);
return;
case eTimerEvent:
priv->state_timer = NULL;
if (priv->controller->eMode != EVDrive) {
fsm_change_state(sm, LS340State_Idle);
return;
}
if (priv->fTarget < priv->fSetPoint) {
priv->fSetPoint = priv->fTarget; /* set the setpoint to be the target */
sprintf(cmd, "SETP %d, %8.2f", priv->iPidLoop, priv->fSetPoint); /* tell the Lakeshore to change the current setpoint to target */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
fsm_change_state(sm, LS340State_Lowering);
return;
}
if (sm->mySubState > priv->iNumSensors+1) sm->mySubState = 2;
if (sm->mySubState >= 2 && sm->mySubState <= priv->iNumSensors+1) {
sprintf(cmd, "KRDG? %s", priv->cValidSensors[sm->mySubState-2]);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
}
return;
case eCommandEvent:
return;
case eTimeoutEvent:
return;
}
return;
}
static void LS340State_Lowering(pStateMachine sm, pEvtEvent event){
pEVDriver driv = (pEVDriver) sm->context;
pLS340Driv priv = (pLS340Driv) driv->pPrivate;
char cmd[CMDLEN];
switch (event->event_type) {
case eStateEvent:
if (priv->state_timer)
NetWatchRemoveTimer(priv->state_timer);
sprintf(cmd, "RANGE %d", priv->iRange); /* set the Range value > 0 turns heater on */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
sprintf(cmd, "CLIMIT? %d", priv->iPidLoop);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
sm->mySubState = 1;
return;
case eMessageEvent:
do {
pAsyncTxn pCmd = event->event.msg.cmd;
pCmd->inp_buf[pCmd->inp_idx] = '\0';
if (sm->mySubState == 1) { /* CLIMIT message */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
sprintf(line, "CLIMIT (lowering): %s", pCmd->inp_buf);
SICSLogWrite(line, eLog);
sscanf(pCmd->inp_buf, "%f,%f,%f,%d,%d", &priv->fSetPointLimit, &priv->fPosSlope, &priv->fNegSlope, &priv->iMaxCurrent, &priv->iMaxRange);
}
sm->mySubState++;
} else if (sm->mySubState >= 2 && sm->mySubState <= priv->iNumSensors+1) {
/* KRDG? Requests */
char* p = strchr(pCmd->inp_buf, '\r');
if (p) {
char line[132];
*p = '\0';
priv->fSensorValues[sm->mySubState-2] = atof(pCmd->inp_buf);
if (sm->mySubState-2 == priv->iCtrlSens) priv->fValue = priv->fSensorValues[sm->mySubState-2];
sprintf(line, "KRDG? (Lowering) sensor %s = %8.2f", priv->cValidSensors[sm->mySubState-2], priv->fSensorValues[sm->mySubState-2]);
SICSLogWrite(line, eLog);
}
sm->mySubState++;
}
/* restart timer */
NetWatchRegisterTimer(&priv->state_timer,
priv->ulMonitorDelay,
fsm_tmr_callback, sm);
} while (0);
return;
case eTimerEvent:
priv->state_timer = NULL;
if (priv->controller->eMode != EVDrive) { /* return to IDLE state when not driving */
fsm_change_state(sm, LS340State_Idle);
return;
}
if (priv->fTarget > priv->fSetPoint) {
priv->fSetPoint = priv->fTarget; /* set the setpoint to be the target */
sprintf(cmd, "SETP %d, %8.2f", priv->iPidLoop, priv->fSetPoint); /* tell the Lakeshore to change the current setpoint to target */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
fsm_change_state(sm, LS340State_Raising);
return;
}
if (sm->mySubState > priv->iNumSensors+1) sm->mySubState = 2;
if (sm->mySubState >= 2 && sm->mySubState <= priv->iNumSensors+1) {
sprintf(cmd, "KRDG? %s", priv->cValidSensors[sm->mySubState-2]);
LS340_SendCmd(priv, cmd, strlen(cmd), fsm_msg_callback);
}
return;
case eCommandEvent:
return;
case eTimeoutEvent:
return;
}
return;
}
static int LS340GetValue( pEVDriver self, float* fPos) {
pLS340Driv me = NULL;
assert(self);
assert(self->pPrivate);
me = (pLS340Driv) self->pPrivate;
*fPos = me->fValue;
return 1;
}
static int LS340SetValue( pEVDriver self, float fPos) {
pLS340Driv me = NULL;
assert(self);
assert(self->pPrivate);
me = (pLS340Driv) self->pPrivate;
if (me->isLocked) {
me->iError = LS340_ERR_LOCKED;
return 0;
}
if (fPos < 0.0 || fPos > me->fSetPointLimit) {
me->iError = LS340_ERR_RANGE;
return 0;
}
me->fTarget = fPos;
return 1;
}
static int LS340Send(pEVDriver self, char *pCommand, char *pReply, int iLen) {
int rsp_len;
rsp_len = iLen;
LS340_SendReceive(self->pPrivate, pCommand, strlen(pCommand), pReply, &rsp_len);
return 1;
}
static int LS340Error(pEVDriver self, int *iCode, char *error, int iErrLen) {
pLS340Driv priv = (pLS340Driv) self->pPrivate;
*iCode = priv->iError;
switch (priv->iError) {
case LS340_ERR_RANGE:
strncpy(error,"Value out of range",iErrLen);
break;
case LS340_ERR_LOCKED:
strncpy(error,"Object is locked",iErrLen);
break;
default:
strncpy(error,"TODO Error Messages",iErrLen);
break;
}
return 1;
}
static int LS340Fix(pEVDriver self, int iError) {
/* TODO */
return DEVFAULT;
}
static int LS340Init(pEVDriver self) {
/* TODO */
return 1;
}
static int LS340Close(pEVDriver self) {
/* TODO */
return -1;
}
static void LS340KillPrivate(void *pData) {
pLS340Driv pMe = (pLS340Driv) 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;
}
}
static void LS340Notify(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, eLog);
/* TODO: disconnect */
break;
case AQU_RECONNECT:
snprintf(line, 132, "Reconnect on Device '%s'", self->name);
SICSLogWrite(line, eLog);
/* 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;
}
static pAsyncProtocol LS340_Protocol = NULL;
static int LS340_Rx(pAsyncProtocol p, pAsyncTxn ctx, int rxchar) {
int iRet = 1;
pAsyncTxn myCmd = (pAsyncTxn) ctx;
switch (myCmd->txn_state) {
case 0: /* first character */
if (myCmd->inp_idx < myCmd->inp_len)
myCmd->inp_buf[myCmd->inp_idx++] = rxchar;
if (rxchar == '\r' || rxchar == '\n')
myCmd->txn_state = 99;
break;
}
if (myCmd->txn_state == 99) {
iRet = 0;
}
if (iRet == 0) { /* end of command */
myCmd->txn_status = ATX_COMPLETE;
return AQU_POP_CMD;
}
return iRet;
}
static int LS340_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 LS340_PrepareTxn(pAsyncProtocol p, pAsyncTxn txn, const char* cmd, int cmd_len, int rsp_len) {
txn->out_buf = (char*) malloc(cmd_len+2);
if (txn->out_buf == NULL) {
SICSLogWrite("ERROR: Out of memory in LS340_PrepareTxn", eError);
return 0;
}
memcpy(txn->out_buf, cmd, cmd_len);
txn->out_len = cmd_len;
if (txn->out_buf[txn->out_len-1] != '\r') {
txn->out_buf[txn->out_len++] = '\r';
}
/*
* If this message does not contain a Question Mark (?)
* Then it does not request a reply
*/
if (!strchr(cmd, '?'))
txn->inp_len = 0;
return 1;
}
void LS340InitProtocol(SicsInterp *pSics) {
if (LS340_Protocol == NULL) {
LS340_Protocol = AsyncProtocolCreate(pSics, "ls340", NULL, NULL);
LS340_Protocol->sendCommand = NULL;
LS340_Protocol->handleInput = LS340_Rx;
LS340_Protocol->handleEvent = LS340_Ev;
LS340_Protocol->prepareTxn = LS340_PrepareTxn;
LS340_Protocol->killPrivate = NULL;
}
}
pEVDriver CreateLS340Driver(int argc, char *argv[])
{
int i, d = 0;
int k, v;
char cname[3];
pEVDriver self = NULL;
pLS340Driv priv = NULL;
/* tcl script eg: EvFactory new tc1 ls340 sertemp1 1 B
* Argv[] entering CreateLS340Driver -
* 0 -
*/
if (argc < 1)
return NULL;
self = CreateEVDriver(argc, argv);
if (!self)
return NULL;
priv = (pLS340Driv)malloc(sizeof(LS340Driv));
if(!priv) {
DeleteEVDriver(self);
return NULL;
}
memset(priv,0,sizeof(LS340Driv));
priv->fValue = 0.0;
if (!AsyncUnitCreate(argv[4], &priv->asyncUnit)) {
char line[132];
snprintf(line, 132, "Error: did not find AsyncQueue %s for Device %s", argv[3], argv[4]);
DeleteEVDriver(self);
free(priv);
return NULL;
}
AsyncUnitSetNotify(priv->asyncUnit, self, LS340Notify);
AsyncUnitSetDelay(priv->asyncUnit, 50);
/* initialise function pointers */
self->SetValue = LS340SetValue;
self->GetValue = LS340GetValue;
self->Send = LS340Send;
self->GetError = LS340Error;
self->TryFixIt = LS340Fix;
self->Init = LS340Init;
self->Close = LS340Close;
self->pPrivate = priv;
self->KillPrivate = LS340KillPrivate;
priv->iHeaterStatus = 1; /* heater on by default */
priv->fsm.context = self;
priv->fsm.state_name = state_name;
priv->fsm.event_name = event_name;
priv->name = strdup(argv[3]);
priv->iPidLoop = atoi(argv[5]); /* PID Loop 1..2 */
if (argc < 7) return NULL;
strncpy(priv->cCtrlSensName, argv[6], strlen(argv[6])); /* control sensor name */
if (strlen(priv->cCtrlSensName) > 0) {
strncpy(priv->cValidSensors[0], priv->cCtrlSensName, strlen(priv->cCtrlSensName));
priv->iCtrlSens = 0;
d++;
}
/* parse out valid sensor names from argv[7] */
for (i = d; i < NUM_INPUT_SENSORS; i++) {
priv->cValidSensors[i][0] = 'X'; /* clear the list, first */
priv->cValidSensors[i][1] = '\0'; /* clear the list, first */
priv->cValidSensors[i][2] = '\0'; /* clear the list, first */
}
i = 0;
v = 0;
while (i < strlen(argv[7])) {
if (isalpha(argv[7][i]) > 0) {
cname[0] = argv[7][i];
cname[1] = '\0';
cname[2] = '\0';
if (isdigit(argv[7][i+1]) > 0) {
cname[1] = argv[7][i+1];
i++;
}
v = 0;
/* check to see if sensor is already in list */
for (k = 0; k < NUM_INPUT_SENSORS; k++) {
if (priv->cValidSensors[k][0] == cname[0]) {
if (strcmp(priv->cValidSensors[k], cname) == 0) {
v = 1;
break; /* already in list, skip */
}
/* C vs C2 */
if (priv->cValidSensors[k][1] == '\0' && isdigit(cname[1]) > 0) {
v = 1;
break;
}
/* C2 vs C */
if (isdigit(priv->cValidSensors[k][1]) > 0 && cname[1] == '\0') {
v = 1;
break;
}
}
}
if (v == 0) { /* not in list, so add it in! */
strncpy(priv->cValidSensors[d], cname, strlen(cname));
d++;
}
}
i++;
}
priv->iNumSensors = d; /* assign number of sensors found */
priv->ulIdlePollRate = 5000;
priv->ulIdleDelay = priv->ulIdlePollRate / priv->iNumSensors+1; /* +1 to include CLIMIT query */
priv->ulMonitorPollRate = 200;
priv->ulMonitorDelay = priv->ulMonitorPollRate / priv->iNumSensors+1; /* +1 to include CLIMIT query */
fsm_change_state(&priv->fsm, LS340State_Unknown);
return self;
}
void LS340Register(pEVControl self, pEVDriver driv)
{
pLS340Driv priv = (pLS340Driv) driv->pPrivate;
priv->controller = self;
if (self->pName) {
if (priv->name)
free(priv->name);
priv->name = strdup(self->pName);
}
}
int LS340Wrapper(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
pEVControl self = (pEVControl)pData;
pEVDriver driv = self->pDriv;
pLS340Driv priv = (pLS340Driv) 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;
/* Managers only */
if (!SCMatchRights(pCon, usMugger)) {
return 0;
}
if (argc < 2) return 0;
sprintf(cmd, "%s", argv[2]);
cmd_len = strlen(cmd);
rsp_len = CMDLEN;
LS340_SendReceive(priv, cmd, cmd_len, rsp, &rsp_len);
SCWrite(pCon, rsp, eValue);
return 1;
}
/* assign control sensor index */
if (strcasecmp("controlsensor", argv[1]) == 0) {
char cmd[64];
char cname[3];
char rsp[CMDLEN];
char line[132];
int i, v;
char *p = NULL;
v = -1;
if (argc > 2) {
p = (char *)strcasestr(argv[2], "sensor");
if (p) {
if (strlen(p) > 6) {
p+=6; /* point to next char after the word sensor */
}
} else {
snprintf(line, sizeof(line), "control sensor %s specified not in list", argv[2]);
SCWrite(pCon, line, eError);
return 0;
}
/* swap control sensor locations from [0] */
strncpy(cname, p, sizeof(cname));
/* algorithm:
* 1. check to see if new sensor given is in list
* IF YES, swap [0] with location found
*/
for (i = 0; i < priv->iNumSensors; i++) {
if (strcmp(priv->cValidSensors[i], cname) == 0) { /* Yes, given sensor is in list! */
v = i;
break;
}
}
if (v > -1) { /* in list, swap values */
float temp;
strcpy(priv->cValidSensors[0], priv->cValidSensors[v]);
temp = priv->fSensorValues[0];
priv->fSensorValues[0] = priv->fSensorValues[v];
priv->fSensorValues[v] = temp;
strcpy(priv->cValidSensors[v], priv->cCtrlSensName);
strcpy(priv->cCtrlSensName, cname);
sprintf(cmd, "CSET %d, %s, 1, %d, 1", priv->iPidLoop, priv->cValidSensors[priv->iCtrlSens], priv->iHeaterStatus);
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
} else {
snprintf(line, sizeof(line), "control sensor %s specified not in list", cname);
SCWrite(pCon, line, eError);
return 0;
}
}
snprintf(rsp, CMDLEN, "%s.controlsensor = sensor%s", priv->name, priv->cValidSensors[priv->iCtrlSens]);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("debug", argv[1]) == 0) {
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("setpoint", argv[1]) == 0) {
char rsp[CMDLEN];
snprintf(rsp, CMDLEN, "%s.setpoint = %8.2f", priv->name, priv->fSetPoint);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("numsensors", argv[1]) == 0) {
char rsp[CMDLEN];
snprintf(rsp, CMDLEN, "%s.numsensors = %d", priv->name, priv->iNumSensors);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("pollingrate", argv[1]) == 0) {
char rsp[CMDLEN];
if (argc > 2) {
priv->ulIdlePollRate = (unsigned)atol(argv[2]);
priv->ulIdleDelay = priv->ulIdlePollRate / priv->iNumSensors+1; /* +1 to include CLIMIT query */
fsm_change_state(&priv->fsm, LS340State_Unknown);
}
snprintf(rsp, CMDLEN, "%s.pollingrate = %lu", priv->name, priv->ulIdlePollRate);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("heateron", argv[1]) == 0) {
char rsp[CMDLEN];
char cmd[CMDLEN];
if (argc > 2) {
priv->iHeaterStatus = atoi(argv[2]);
if (priv->iHeaterStatus == 1) { /* turn heater on */
/* sprintf(cmd, "RANGE %d", priv->iRange);
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
*/
} else {
sprintf(cmd, "RANGE 0"); /* turn heater off otherwise */
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
}
}
snprintf(rsp, CMDLEN, "%s.heateron = %d", priv->name, priv->iHeaterStatus);
SCWrite(pCon, rsp, eValue);
if (argc > 2) fsm_change_state(&priv->fsm, LS340State_Idle); /* move back to Idle state? */
return 1;
}
if (strcasecmp("range", argv[1]) == 0) {
char rsp[CMDLEN];
char cmd[CMDLEN];
if (argc > 2) {
priv->iRange = atoi(argv[2]);
if (priv->iRange > 0) priv->iHeaterStatus = 1;
sprintf(cmd, "RANGE %d", priv->iRange);
LS340_SendCmd(priv, cmd, strlen(cmd), NULL);
}
snprintf(rsp, CMDLEN, "%s.range = %d", priv->name, priv->iRange);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("sensorlist", argv[1]) == 0) {
int i;
char rsp[CMDLEN];
char senslist[132];
char temp[132];
strcpy(senslist, ""); /* prepare string */
for (i = 0; i < priv->iNumSensors; i++) {
sprintf(temp, "sensor%s", priv->cValidSensors[i]);
strcat(senslist, temp);
if (i < priv->iNumSensors-1) strcat(senslist, ",");
}
snprintf(rsp, CMDLEN, "%s.sensorlist = %s", priv->name, senslist);
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strncasecmp("sensor", argv[1], 6) == 0) {
char rsp[CMDLEN];
int i;
char name[3];
name[0] = '\0';
name[1] = '\0';
name[2] = '\0';
if (strlen(argv[1]) == 7) { /* sensorA */
name[0] = argv[1][6];
}
if (strlen(argv[1]) == 8) { /* sensorC1 */
name[0] = argv[1][6];
name[1] = argv[1][7];
}
i = getSensorIndex(priv, name);
if (i >= 0) {
snprintf(rsp, CMDLEN, "%s.sensor%s = %8.2f", priv->name, priv->cValidSensors[i], priv->fSensorValues[i]);
} else {
snprintf(rsp, CMDLEN, "%s.sensor%s is not in list", priv->name, name);
}
SCWrite(pCon, rsp, eValue);
return 1;
}
if (strcasecmp("list", argv[1]) == 0) {
int i;
int iRet;
char rsp[CMDLEN];
char senslist[132];
char temp[132];
iRet = EVControlWrapper(pCon,pSics,pData,argc,argv);
if (iRet) {
snprintf(rsp, CMDLEN, "%s.pidloop = %d", priv->name, priv->iPidLoop);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.controlsensor = sensor%s", priv->name, priv->cValidSensors[priv->iCtrlSens]);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.numsensors = %d", priv->name, priv->iNumSensors);
SCWrite(pCon, rsp, eValue);
strcpy(senslist, ""); /* prepare string */
for (i = 0; i < priv->iNumSensors; i++) {
sprintf(temp, "sensor%s", priv->cValidSensors[i]);
strcat(senslist, temp);
if (i < priv->iNumSensors-1) strcat(senslist, ",");
}
snprintf(rsp, CMDLEN, "%s.sensorlist = %s", priv->name, senslist);
SCWrite(pCon, rsp, eValue);
for (i = 0; i < priv->iNumSensors; i++) {
snprintf(rsp, CMDLEN, "%s.sensor%s = %8.2f", priv->name, priv->cValidSensors[i], priv->fSensorValues[i]);
SCWrite(pCon, rsp, eValue);
}
snprintf(rsp, CMDLEN, "%s.pollingrate = %lu", priv->name, priv->ulIdlePollRate);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.setpoint = %8.2f", priv->name, priv->fSetPoint);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.target = %8.2f", priv->name, priv->fTarget);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.setpointlimit = %8.2f", priv->name, priv->fSetPointLimit);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.positiveslope = %8.2f", priv->name, priv->fPosSlope);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.negativeslope = %8.2f", priv->name, priv->fNegSlope);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.maxcurrent = %d", priv->name, priv->iMaxCurrent);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.maxrange = %d", priv->name, priv->iMaxRange);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.range = %d", priv->name, priv->iRange);
SCWrite(pCon, rsp, eValue);
snprintf(rsp, CMDLEN, "%s.heateron = %d", priv->name, priv->iHeaterStatus);
SCWrite(pCon, rsp, eValue);
}
return iRet;
}
return EVControlWrapper(pCon,pSics,pData,argc,argv);
}