/** * \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 #include #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 }; #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; } /* 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; } /* 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; } /* 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? */ void camdriv_input(void *caller, camsm_t *self, enum event_codes event_sym) { state_t Sn; cam_trans_fn(self->Sc, event_sym, &Sn, &self->Eo); 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; ca = event_names[E.ca]; cm = event_names[E.cm]; cd = event_names[E.cd]; dr = event_names[E.dr]; snprintf(event_str, ESLEN, "%s,%s,%s,%s", ca, cm, cd, dr); 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, .dr=SDR_IDLE}, ECD_TK_SHOT, {.dr=0}, {.cl=SCL_TK_SHOT}}, {{.cl=SCL_RDY, .dr=SDR_IDLE}, ECD_MLTI_ON, {.dr=0}, {.cl=SCL_TK_MLTI}}, {{.cl=SCL_TK_SHOT}, ECM_IDLE, {.ca=ECA_TK_SHOT}, {.cl=SCL_WT}}, {{.cl=SCL_TK_MLTI}, ECD_MLTI_OFF,{.dr=0}, {.cl=SCL_RDY}}, {{.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_ACQ, {.dr=0}, {.cl=SCL_RDY}}, {{.cl=SCL_WT}, ECM_PROC, {.dr=0}, {.cl=SCL_RDY}}, {{.cl=SCL_WT}, ECM_STOP, {.dr=0}, {.cl=SCL_RDY}}, {{.cl=SCL_WT}, ECM_IDLE, {.dr=0}, {.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, .cl=SCL_RDY}, ECM_PROC, {.dr=EDR_IDLE}, {.dr=SDR_IDLE}}, {{.dr=SDR_BUSY, .cl=SCL_RDY}, ECM_STOP, {.dr=EDR_IDLE}, {.dr=SDR_IDLE}}, {{.dr=SDR_BUSY, .cl=SCL_RDY}, ECM_IDLE, {.dr=EDR_IDLE}, {.dr=SDR_IDLE}}, {{.dr=END_TABLE}, END_TABLE, {.dr=END_TABLE}, {.dr=END_TABLE}} };