Files
sics/site_ansto/hardsup/camera.c
Ferdi Franceschini 67a3a198ac Added command latch ready event and camera upload configuration event.
The camera driver will now upload the exposure time when it's called from a scan object.
The output function now returns events which are not targeted at the camera or SICS
so that they can be fed back into the state transition function.
The input event handler (camera.c:camdriv_input) now feeds output events back into the
transition function.
2014-07-24 13:53:34 +10:00

241 lines
7.2 KiB
C

/**
* \file camera.c
* \brief Defines the state transition table and implements the state transition function
*
* \Author Ferdi Franceschini February 2013
*
* Copyright: see file Copyright.txt
*/
#include <stdio.h>
#include <string.h>
#include "camera.h"
#define TR(a,b) #a,
#define TE(a,b) #a
char *event_names[] = { "0", CAMERA_CMDS_TABLE, CAMERA_MSGS_TABLE, CAM_MOD_EVENT_TABLE, DRIVER_EVENT_TABLE, CMD_EVENT_TABLE, CMD_LATCH_EVENT_TABLE};
#undef TR
#undef TE
/* The signature array defines the identifying characters at the start of a reply. */
#define TR(a,b) b,
#define TE(a,b) b
char *event_signatures[] = { "0", CAMERA_CMDS_TABLE, CAMERA_MSGS_TABLE, CAM_MOD_EVENT_TABLE, DRIVER_EVENT_TABLE, CMD_EVENT_TABLE };
#undef TR
#undef TE
#define TR(a,b) #a,
#define TE(a,b) #a
char *SCL_NAMES[] = {"0", COMMAND_LATCH_STATE_TABLE};
char *SCM_NAMES[] = {"0", CAMERA_MODEL_STATE_TABLE};
char *SDR_NAMES[] = {"0", DRIVER_STATE_TABLE};
#undef TR
#undef TE
#define TR(a,b) b,
#define TE(a,b) b
char *COMMAND_LATCH_STATE_DESCRIPTION[] = {"0", COMMAND_LATCH_STATE_TABLE};
char *CAMERA_MODEL_STATE_DESCRIPTION[] = {"0", CAMERA_MODEL_STATE_TABLE};
char *DRIVER_STATE_DESCRIPTION[] = {"0", DRIVER_STATE_TABLE};
#undef TR
#undef TE
char state_str[SSLEN+1];
char event_str[ESLEN+1];
/* Clear event channel */
void EVclr(event_t *E) {
E->ca=0;
E->cm=0;
E->cd=0;
E->dr=0;
E->cl=0;
}
/* Set event channel */
void EVset(event_t *E, event_t Ev) {
if (Ev.ca) E->ca = Ev.ca;
if (Ev.cm) E->cm = Ev.cm;
if (Ev.cd) E->cd = Ev.cd;
if (Ev.dr) E->dr = Ev.dr;
if (Ev.cl) E->cl = Ev.cl;
}
/* Set system state */
void STset(state_t *Sc, state_t St) {
if (St.cl) Sc->cl = St.cl;
if (St.cm) Sc->cm = St.cm;
if (St.dr) Sc->dr = St.dr;
}
enum event_codes camera_model(enum event_codes event) {
switch (event) {
case ECA_START:
case ECA_OPEN_SHUTR:
case ECA_ACQ_IMAGE:
return ECM_ACQ;
case ECA_CLOSE_SHUTR:
case ECA_READ_IMAGE:
case ECA_SEND_IMAGE:
return ECM_PROC;
case ECA_STOP:
return ECM_STOP;
case ECA_IDLE:
return ECM_IDLE;
case ECA_FAIL:
return ECM_FAIL;
default:
return ECM_UNKNOWN;
}
}
int cam_parse_status(char *msg, enum event_codes *ca_sym, int *time_rem, int *time_tot) {
char *delim=" ,";
char str[100], *tok;
int ret = 1;
strcpy(str,msg);
tok=strtok(str,delim);
ret = cam_rep2sym(tok);
if (ret != -1) {
*ca_sym = ret;
if (ECA_IDLE == *ca_sym) {
*time_rem = *time_tot = -1;
} else {
tok=strstr(msg, "Remaining");
sscanf(tok, " Remaining= %d ms, Total Time= %d ms, ", time_rem, time_tot);
}
}
return ret;
}
/* return -1 if message is unknown
* NOTE: 0 has meaning in the transition table
*/
enum event_codes cam_rep2sym(char *msg) {
int i;
for (i = ECA_START; i <= ECA_STOP; i++) {
if (0 == strcmp(msg, event_signatures[i])) {
return i;
}
}
return -1;
}
/* \brief Transition function. Given an input event it maps the current state
* to the next state and output.
*
* \param Sc current state.
* \param Ein input event.
* \param Sn will be set to the next state.
* \param Eo will be set as the output event.
* return 0 = no transition,
* return 1 = transition
*/
int cam_trans_fn(state_t Sc, enum event_codes Ein, state_t *Sn, event_t *Eo) {
trans_t tt;
int i, ret=0;
STset(Sn, Sc);
EVclr(Eo);
for (i=0; tt=TRANS_TABLE[i], tt.Sc.dr != END_TABLE; i++) {
if ( Ein == tt.Ei && INSYS(Sc,tt.Sc)) {
STset(Sn, tt.Sn);
EVset(Eo, tt.Eo);
ret = 1;
}
}
return ret;
}
/* TODO Do we need Eo in self, should camsm_t be refactored to remove it? */
/* \brief Takes an input event from the camera driver, runs it through the
* state machine and sets the current state equal to the next state.
*
* \param caller is a reference to the object (ie camera driver) sending the event.
* \param self is a reference to the state machine transfer object.
* \param event_sym is the input event symbol.
*/
void camdriv_input(void *caller, camsm_t *self, enum event_codes event_sym) {
int input_event;
state_t Sn;
input_event = event_sym;
while (input_event != -1) {
cam_trans_fn(self->Sc, input_event, &Sn, &self->Eo);
input_event = self->output_fn(caller, self->Eo);
self->Sc = Sn;
}
}
char *strstate(state_t s) {
char *cl, *cm, *dr;
cm = SCM_NAMES[s.cm];
cl = SCL_NAMES[s.cl];
dr = SDR_NAMES[s.dr];
snprintf(state_str, SSLEN, "%s,%s,%s", cm, cl, dr);
return state_str;
}
char *strevent(event_t E) {
char *ca, *cm, *cd, *dr, *cl;
ca = event_names[E.ca];
cm = event_names[E.cm];
cd = event_names[E.cd];
dr = event_names[E.dr];
cl = event_names[E.cl];
snprintf(event_str, ESLEN, "%s,%s,%s,%s,%s", ca, cm, cd, dr, cl);
return event_str;
}
/* The transition table has four columns,
* {current state}, input_event, {output events}, {next state}
*
* Each state is actually a 3-tuple (cl,cm,dr) where
* cl = command latch state
* cm = camera model state
* dr = driver state
*
* There is only one stream in the input event channel.
*
* The output event channel is a 4-tuple (ca,cm,cd,dr) where
* ca = camera event stream
* cm = cameram model event stream
* cd = command event stream
* dr = driver event stream
*
* The next state is a 3-tuple as above.
*
* 0 = wildcard for components of current state.
* 0 = no output for the output events.
* 0 = no change for components of next state.
*/
trans_t TRANS_TABLE[] = {
{{.cl=SCL_RDY}, ECD_TK_SHOT, {.dr=0}, {.cl=SCL_CFG_TK_SHOT}},
{{.cl=SCL_RDY}, ECD_MLTI_ON, {.dr=0}, {.cl=SCL_CFG_TK_MLTI}},
{{.cl=SCL_CFG_TK_SHOT}, ECM_IDLE, {.ca=ECA_CFG}, {.cl=SCL_TK_SHOT}},
{{.cl=SCL_TK_SHOT}, ECM_IDLE, {.ca=ECA_TK_SHOT}, {.cl=SCL_WT}},
{{.cl=SCL_CFG_TK_MLTI}, ECD_MLTI_OFF,{.cl=ECL_RDY}, {.cl=SCL_RDY}},
{{.cl=SCL_TK_MLTI}, ECD_MLTI_OFF,{.cl=ECL_RDY}, {.cl=SCL_RDY}},
{{.cl=SCL_CFG_TK_MLTI}, ECM_IDLE, {.ca=ECA_CFG}, {.cl=SCL_TK_MLTI}},
{{.cl=SCL_TK_MLTI}, ECM_IDLE, {.ca=ECA_MLTI_ON}, {.cl=SCL_MLTI_ON}},
{{.cl=SCL_MLTI_ON}, ECD_MLTI_OFF,{.ca=ECA_MLTI_OFF},{.cl=SCL_WT}},
{{.cl=SCL_WT}, ECM_PROC, {.cl=ECL_RDY}, {.cl=SCL_RDY}},
{{.cl=SCL_WT}, ECM_STOP, {.cl=ECL_RDY}, {.cl=SCL_RDY}},
{{.cl=SCL_WT}, ECM_IDLE, {.cl=ECL_RDY}, {.cl=SCL_RDY}},
{{.cm=SCM_IDLE}, ECM_ACQ, {.dr=0}, {.cm=SCM_ACQ}},
{{.cm=SCM_IDLE}, ECM_PROC, {.dr=0}, {.cm=SCM_PROC}},
{{.cm=SCM_ACQ}, ECM_PROC, {.dr=0}, {.cm=SCM_PROC}},
{{.cm=SCM_ACQ}, ECM_STOP, {.dr=0}, {.cm=SCM_IDLE}},
{{.cm=SCM_PROC}, ECM_STOP, {.dr=0}, {.cm=SCM_IDLE}},
{{.cm=SCM_ACQ}, ECM_IDLE, {.dr=0}, {.cm=SCM_IDLE}},
{{.cm=SCM_PROC}, ECM_IDLE, {.dr=0}, {.cm=SCM_IDLE}},
{{.dr=SDR_IDLE}, ECD_TK_SHOT, {.dr=EDR_BUSY}, {.dr=SDR_BUSY}},
{{.dr=SDR_IDLE}, ECD_MLTI_ON, {.dr=EDR_BUSY}, {.dr=SDR_BUSY}},
{{.dr=SDR_BUSY}, ECL_RDY, {.dr=EDR_IDLE}, {.dr=SDR_IDLE}},
{{.dr=END_TABLE}, END_TABLE, {.dr=END_TABLE}, {.dr=END_TABLE}}
};