diff --git a/site_ansto/TESTS/dingo_camera/Makefile b/site_ansto/TESTS/dingo_camera/Makefile new file mode 100644 index 00000000..cb7a53b8 --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/Makefile @@ -0,0 +1,8 @@ +# vim: ft=make: ts=8: sw=8: noet: cindent: + +all: camera_test.c ../../hardsup/camera.c ../../hardsup/camera.h + gcc -g -Wall -Wextra -std=c99 -pedantic -I../../hardsup -o camera_test camera_test.c ../../hardsup/camera.c + +clean: + rm camera_test + diff --git a/site_ansto/TESTS/dingo_camera/camdriver_event_io_expect.txt b/site_ansto/TESTS/dingo_camera/camdriver_event_io_expect.txt new file mode 100644 index 00000000..6b0f7ffd --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/camdriver_event_io_expect.txt @@ -0,0 +1,2 @@ +input: ECD_TK_SHOT :SCM_IDLE,SCL_RDY,SDR_IDLEcamdriv_out: symbol=EDR_BUSY, output=HWBUSY +input: ECM_IDLE :SCM_IDLE,SCL_TK_SHOT,SDR_BUSYcamdriv_out: symbol=ECA_TK_SHOT, output=take shot diff --git a/site_ansto/TESTS/dingo_camera/camera_test.c b/site_ansto/TESTS/dingo_camera/camera_test.c new file mode 100644 index 00000000..ae7ffc64 --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/camera_test.c @@ -0,0 +1,194 @@ +/** + * \file camera_test.c + * \brief Test camera.[ch] state machine. + * + * \Author Ferdi Franceschini February 2013 + */ +#include +#include +#include +#include "camera.h" + + +void print_state(state_t s) { + char *cl, *cm, *dr; + cm = SCM_NAMES[s.cm]; + cl = SCL_NAMES[s.cl]; + dr = SDR_NAMES[s.dr]; + printf("%s,%s,%s", cm, cl, dr); +} + +void print_event(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]; + printf("%s,%s,%s,%s", ca, cm, cd, dr); +} + +event_t output(state_t Sc, enum event_codes Ei) { + int i; + trans_t tr; + event_t Eo; + + for (i=0; tr=TRANS_TABLE[i], tr.Sc.dr!= END_TABLE; i++) { + if (Ei==tr.Ei && INSYS(Sc,tr.Sc)) { + EVset(&Eo,tr.Eo); + } + } + return Eo; +} + +int test_camrep2sym(void) { + int i, cm_sym, time_rem, time_tot; + enum event_codes ca_sym; + char *camrep[] = { + "StartTime Remaining= 3907 ms, Total Time=3686 ms,", + "Open ShutrTime Remaining= 539454 ms, Total Time=3686 ms,", + "Acq ImageTime Remaining= 109 ms, Total Time=3686 ms,", + "Close ShutrTime Remaining= 218 ms, Total Time=3686 ms,", + "Read ImageTime Remaining= 328 ms, Total Time=3686 ms,", + "Read ImageTime Remaining= 328 ms, Total Time=3686 ms,", + "Read ImageTime Remaining= 328 ms, Total Time=3686 ms,", + "Idle", + NULL + }; + + fprintf(stderr, "TEST_CAMREP2SYM:DESCRIPTION: Parse camera status messages and convert them to camera events and camera model events\n"); + fprintf(stderr, "TEST_CAMREP2SYM:DESCRIPTION: Output format = 'status message' => camera_symbol => camera_model_symbol\n"); + fprintf(stderr, "TEST_CAMREP2SYM:OUTPUT:START\n"); + for (i=0; camrep[i] != NULL; i++) { + if ( cam_parse_status(camrep[i], &ca_sym, &time_rem, &time_tot) == -1) + exit(1); + else { + cm_sym = camera_model(ca_sym); + if (ECA_IDLE==ca_sym) + printf("'%s' => %s => %s\n", + camrep[i], event_names[ca_sym], event_names[cm_sym]); + else + printf("'%s' => %s => %s: time remaining = %d, total time = %d\n", + camrep[i], event_names[ca_sym], event_names[cm_sym], + time_rem, time_tot); + } + } + fprintf(stderr, "TEST_CAMREP2SYM:OUTPUT:END\n\n"); + return 1; +} + +int test_trans_fn(void) { + int i; + event_t Eo={0,0,0,0}; + + + state_t Sc = {.cl=SCL_RDY, .cm=SCM_IDLE, .dr=SDR_IDLE}; + int input[] = { + ECM_IDLE, ECD_TK_SHOT, ECM_IDLE, ECM_IDLE, ECM_ACQ, ECD_TK_SHOT, + ECM_PROC, ECD_TK_SHOT, ECM_PROC, ECM_IDLE, ECM_ACQ, + ECM_PROC, ECM_IDLE, 0 + }; + + fprintf(stderr, "TEST_TRANS_FN:DESCRIPTION: Test the state transitions. Each line of ouput has three fields.\n"); + fprintf(stderr, "TEST_TRANS_FN:DESCRIPTION: Output pattern ~= Ein Scurr Eout\n"); + fprintf(stderr, "TEST_TRANS_FN:DESCRIPTION: You can follow the transitions by reading down the Scurr column\n"); + fprintf(stderr, "TEST_TRANS_FN:OUTPUT:START\n"); + printf("INPUT,\tCURR SCM,CURR SCL,CURR SDR,\tCA OUT,CM OUT,CD OUT,DR OUT\n"); + for (i=0; input[i]; i++) { + EVclr(&Eo); + printf("%s,\t",event_names[input[i]]); + print_state(Sc); + cam_trans_fn(Sc, input[i], &Sc, &Eo); + printf(",\t"); + print_event(Eo); + printf("\n"); + } + fprintf(stderr, "TEST_TRANS_FN:OUTPUT:END\n"); + return 1; +} + +int camdriv_out(void *me, event_t Eo) { + int *self = me; *self=0; /* shudduppa your warnings */ + if (Eo.ca) { + printf("camdriv_out: symbol=%s, output=%s\n", event_names[Eo.ca], event_signatures[Eo.ca]); + } + if (Eo.cm) { + printf("camdriv_out: symbol=%s, output=%s\n", event_names[Eo.cm], event_signatures[Eo.cm]); + } + if (Eo.cd) { + printf("camdriv_out: symbol=%s, output=%s\n", event_names[Eo.cd], event_signatures[Eo.cd]); + } + if (Eo.dr) { + printf("camdriv_out: symbol=%s, output=%s\n", event_names[Eo.dr], event_signatures[Eo.dr]); + } + return 1; +} + +int test_camdriv_event_io(void) { + int Ein=ECD_TK_SHOT; + int self; + camsm_t cdinfo = { + .Sc = {.cl=SCL_RDY, .cm=SCM_IDLE, .dr=SDR_IDLE}, + .Eo = {.ca=0}, + .multi = 0, + .output_fn = camdriv_out, + }; + + fprintf(stderr, "TEST_CAMDRIVER_EVENT_IO:DESCRIPTION: Test State machine output_fn callback\n"); + fprintf(stderr, "TEST_CAMDRIVER_EVENT_IO:DESCRIPTION: Output pattern ~= Ein:Scurr:Eo:output message\n"); + fprintf(stderr, "TEST_CAMDRIVER_EVENT_IO:OUPUT:START\n"); + + printf("input: %s :", event_names[Ein]); + print_state(cdinfo.Sc); + camdriv_input(&self, &cdinfo, Ein); + + Ein = ECM_IDLE; + printf("input: %s :", event_names[Ein]); + cdinfo.Sc.cl=SCL_TK_SHOT; + print_state(cdinfo.Sc); + camdriv_input(&self, &cdinfo, Ein); + + fprintf(stderr, "TEST_CAMDRIVER_EVENT_IO:OUPUT:END\n"); + return 1; +} + +char *tests[] = {"camrep2sym", "trans_fn", "camdriv_input"}; +void usage(char *name) { + int i; + printf("Usage: %s [n]\n",name); + printf("default: run all tests\n"); + printf("n is an int whose bits indicate which tests will be executed as follows\n"); + for (i=0; i<3; i++) + printf("n=%d; %s\n",1 << i, tests[i]); + printf("\n"); +} + +int main(int argc, char *argv[]) { + int ret, test=0, tflag=7; + + if (argc == 1) + test = tflag; + else if (strstr(argv[1], "help")) { + usage(argv[0]); + return 0; + } else + test = atoi(argv[1]); + + if ((test < 1) || (test > tflag)) { + printf("ERROR: Argument must be a positive integer between 1 and %d inclusive\n", tflag); + usage(argv[0]); + return 1; + } + + + if (test & 1) + ret = test_camrep2sym(); + if (test & 2) + ret = test_trans_fn(); + if (test & 4) + ret = test_camdriv_event_io(); + + if (ret) + return 0; + else + return 1; +} diff --git a/site_ansto/TESTS/dingo_camera/camrep2sym_expect.txt b/site_ansto/TESTS/dingo_camera/camrep2sym_expect.txt new file mode 100644 index 00000000..4361863f --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/camrep2sym_expect.txt @@ -0,0 +1,8 @@ +'StartTime Remaining= 3907 ms, Total Time=3686 ms,' => ECA_START => ECM_ACQ: time remaining = 3907, total time = 3686 +'Open ShutrTime Remaining= 539454 ms, Total Time=3686 ms,' => ECA_OPEN_SHUTR => ECM_ACQ: time remaining = 539454, total time = 3686 +'Acq ImageTime Remaining= 109 ms, Total Time=3686 ms,' => ECA_ACQ_IMAGE => ECM_ACQ: time remaining = 109, total time = 3686 +'Close ShutrTime Remaining= 218 ms, Total Time=3686 ms,' => ECA_CLOSE_SHUTR => ECM_PROC: time remaining = 218, total time = 3686 +'Read ImageTime Remaining= 328 ms, Total Time=3686 ms,' => ECA_READ_IMAGE => ECM_PROC: time remaining = 328, total time = 3686 +'Read ImageTime Remaining= 328 ms, Total Time=3686 ms,' => ECA_READ_IMAGE => ECM_PROC: time remaining = 328, total time = 3686 +'Read ImageTime Remaining= 328 ms, Total Time=3686 ms,' => ECA_READ_IMAGE => ECM_PROC: time remaining = 328, total time = 3686 +'Idle' => ECA_IDLE => ECM_IDLE diff --git a/site_ansto/TESTS/dingo_camera/trans_fn_expect.txt b/site_ansto/TESTS/dingo_camera/trans_fn_expect.txt new file mode 100644 index 00000000..efc5294d --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/trans_fn_expect.txt @@ -0,0 +1,14 @@ +INPUT, CURR SCM,CURR SCL,CURR SDR, CA OUT,CM OUT,CD OUT,DR OUT +ECM_IDLE, SCM_IDLE,SCL_RDY,SDR_IDLE, 0,0,0,0 +ECD_TK_SHOT, SCM_IDLE,SCL_RDY,SDR_IDLE, 0,0,0,EDR_BUSY +ECM_IDLE, SCM_IDLE,SCL_TK_SHOT,SDR_BUSY, ECA_TK_SHOT,0,0,0 +ECM_IDLE, SCM_IDLE,SCL_WT,SDR_BUSY, 0,0,0,0 +ECM_ACQ, SCM_IDLE,SCL_RDY,SDR_BUSY, 0,0,0,0 +ECD_TK_SHOT, SCM_ACQ,SCL_RDY,SDR_BUSY, 0,0,0,0 +ECM_PROC, SCM_ACQ,SCL_RDY,SDR_BUSY, 0,0,0,EDR_IDLE +ECD_TK_SHOT, SCM_PROC,SCL_RDY,SDR_IDLE, 0,0,0,EDR_BUSY +ECM_PROC, SCM_PROC,SCL_TK_SHOT,SDR_BUSY, 0,0,0,0 +ECM_IDLE, SCM_PROC,SCL_TK_SHOT,SDR_BUSY, ECA_TK_SHOT,0,0,0 +ECM_ACQ, SCM_IDLE,SCL_WT,SDR_BUSY, 0,0,0,0 +ECM_PROC, SCM_ACQ,SCL_RDY,SDR_BUSY, 0,0,0,EDR_IDLE +ECM_IDLE, SCM_PROC,SCL_RDY,SDR_IDLE, 0,0,0,0 diff --git a/site_ansto/hardsup/camera.c b/site_ansto/hardsup/camera.c new file mode 100644 index 00000000..24e22cda --- /dev/null +++ b/site_ansto/hardsup/camera.c @@ -0,0 +1,218 @@ +/** + * \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}} +}; diff --git a/site_ansto/hardsup/camera.h b/site_ansto/hardsup/camera.h new file mode 100644 index 00000000..abb9f5e7 --- /dev/null +++ b/site_ansto/hardsup/camera.h @@ -0,0 +1,247 @@ +/** + * \file camera.h + * \brief Enumerates event codes and states, and defines the abstract + * types used in the state machine. + * + * \Author Ferdi Franceschini February 2013 + * + * Copyright: see file Copyright.txt + * + * Deterministic Finite State machine transducer (Mealy) + * The idea is to synthesize a state machine which handles the union of input + * events for a given set state machines in a system. + * + * The system is made up of three components, a command latch (CL), a camera + * model (CM) and the driver (DR). The system state machine is considered to be + * made up of the component state machines running in parallel and with some of + * the component state machine transitions being restricted by system state + * transition rules. + * The sets of states are, + * SCL:Command Latch states, SCM:Camera Model states, SCDR:Driver states. + * Q is the set of system states. + * The sets of event symbols are defined as, + * ECA:Camera events, ECM:Camera model events, ECD:User command events, + * EDR:Driver events. + * + * The system state machine for the camera driver is defined as follows. + * In the following, '<' means subset + * Q < SCL X SCM X SCDR + * Ein = ECA U ECM U ECD U EDR + * Eo < ECA X ECM X ECD X EDR + * trans fn: QXEin -> Q + * out fn : QXEin -> Eo + * + * In the implementation the transition and output functions are combined in + * TRANS_TABLE. + */ +#ifndef CAMERA_H +#define CAMERA_H + +/* END_TABLE marks the last row in the transition table */ +#define END_TABLE -1 +/* EVENT CODES */ +/* All event enum constants start with 'EXX_', XX identifies the source + * ECA_ : Camera event stream. Messages destined for or received from the camera. + * ECM_ : Events from the camera model. + * ECD_ : Command input, either directly from a user or via the scan command + * EDR_ : Driver events + */ +#define CAMERA_CMDS_TABLE \ + TR(ECA_SET_CAM, "set camera") \ + TR(ECA_TK_SHOT, "take shot") \ + TR(ECA_MLTI_ON, "take multi on") \ + TR(ECA_MLTI_OFF, "take multi off") \ + TR(ECA_GET_STATUS, "get status") \ + TE(ECA_GET_STATE, "get state") + +#define CAMERA_MSGS_TABLE \ + TR(ECA_START, "StartTime") \ + TR(ECA_OK, "OK") \ + TR(ECA_FAIL, "Fail") \ + TR(ECA_IDLE, "Idle") \ + TR(ECA_OPEN_SHUTR, "Open") \ + TR(ECA_CLOSE_SHUTR, "Close") \ + TR(ECA_ACQ_IMAGE, "Acq") \ + TR(ECA_READ_IMAGE, "Read") \ + TR(ECA_SEND_IMAGE, "Send") \ + TE(ECA_STOP, "Stop") + +#define CAM_MOD_EVENT_TABLE \ + TR(ECM_UNKNOWN, "Stop message received from camera") \ + TR(ECM_STOP, "Stop message received from camera") \ + TR(ECM_IDLE, "Camera in idle state") \ + TR(ECM_FAIL, "Command rejected") \ + TR(ECM_ACQ, "catch-all for start of acq phase") \ + TE(ECM_PROC, "catch-all for end of acq phase") + +/* COMMAND EVENTS + * These are events triggered by user input. + * The events can be direct via the 'send' subcommand or + * indirect via the scan command through the counter interface. + */ +#define CMD_EVENT_TABLE \ + TR(ECD_CLEAR, "Clear latched command") \ + TR(ECD_TK_SHOT, "take shot") \ + TR(ECD_MLTI_ON, "take multi on") \ + TR(ECD_MLTI_OFF, "take multi off") \ + TR(ECD_GET_STATE, "get state") \ + TE(ECD_GET_STATUS, "get status") + +/* DRIVER EVENT TABLE + * These events are of interest to SICS. + */ +#define DRIVER_EVENT_TABLE \ + TR(EDR_IDLE, "HWIDLE") \ + TR(EDR_BUSY, "HWBUSY") \ + TE(EDR_FAULT, "HWFAULT") + +#define ESLEN 32 + +/* STATE CODES + * SXX_ State code enum constants, XX identifies a component of the system. + * SCL_ Command latch states + * SCM_ Camera model states + * SDR_ Driver states + */ +#define COMMAND_LATCH_STATE_TABLE \ + TR(SCL_RDY, "command latch ready to accept") \ + TR(SCL_TK_SHOT, "latched a take shot command") \ + TR(SCL_TK_MLTI, "latched a multi-shot command") \ + TR(SCL_MLTI_ON, "multi-shot is running") \ + TE(SCL_WT, "waiting for camera to start acquisition") + +#define CAMERA_MODEL_STATE_TABLE \ + TR(SCM_IDLE, "Camera is idle") \ + TR(SCM_ACQ, "Camera is acquiring an image") \ + TE(SCM_PROC, "Camera is processing an image") + +#define DRIVER_STATE_TABLE \ + TR(SDR_IDLE, "HWIdle") \ + TR(SDR_BUSY, "HWBusy") \ + TE(SDR_FAULT, "HWFault") + +#define SSLEN 32 + +/* Enumerate event and state symbols */ +#define TR(a,b) a, +#define TE(a,b) a +enum event_codes {ECA_UNKNOWN, CAMERA_CMDS_TABLE, CAMERA_MSGS_TABLE, CAM_MOD_EVENT_TABLE, DRIVER_EVENT_TABLE, CMD_EVENT_TABLE}; +#undef TR +#undef TE + +#define TR(n,d) n, +#define TE(n,d) n +enum command_latch_states {CL_Z,COMMAND_LATCH_STATE_TABLE}; +enum camera_model_states {CM_Z,CAMERA_MODEL_STATE_TABLE}; +enum driver_states {DR_Z,DRIVER_STATE_TABLE}; +#undef TR +#undef TE + +extern char *SCL_NAMES[]; +extern char *SCM_NAMES[]; +extern char *SDR_NAMES[]; +/* List of names generated from the event_codes. */ +extern char *event_names[]; + +/* The signature array includes the identifying characters at the start of a reply. + * as well as camera commands or just describes the corresponding symbols. + */ +extern char *event_signatures[]; + + + + +/* Output event channel + * A tuple (Eca,Ecm,Ecd,Edr) in ECA X ECM X ECD X EDR + */ +typedef struct { + enum event_codes ca; + enum event_codes cm; + enum event_codes cd; + enum event_codes dr; +} event_t; + +/* A tuple (Scl,Scm,Sdr) in SCL X SCM X SDR */ +typedef struct { + int cl; + int cm; + int dr; +} state_t; + +/* Defines a row in the transition table */ +typedef struct { + state_t Sc; + enum event_codes Ei; + event_t Eo; + state_t Sn; +} trans_t; + +/* Transfer object for state machine IO and transitions + * output_fn: This is the output function callback. It must be defined by the + * IO layer (ie cameradriver.c). + */ +typedef struct { + state_t Sc; + event_t Eo; + int multi; + int debug; + int (*output_fn)(void *caller, event_t Eo); +} camsm_t; + +/* Are we in the SCL_XX, SCM_ or SDR_ state? + * NOTE: 0 is a wildcard so any Sc.cl value matches St.cl==0 + */ +#define INCL(Sc,St) (!St.cl || Sc.cl==St.cl) +#define INCM(Sc,St) (!St.cm || Sc.cm==St.cm) +#define INDR(Sc,St) (!St.dr || Sc.dr==St.dr) +/* INSYS is True if system state Sc is in St. + * St is normally the current state in the transition table + * and describes a range of possible states. + */ +#define INSYS(Sc,St) ( INCL(Sc,St) && INCM(Sc,St) && INDR(Sc,St) ) + + +/* Clear the given event */ +void EVclr(event_t *E); +/* Set E to Ev */ +void EVset(event_t *E, event_t Ev); +/* Set Sc to St */ +void STset(state_t *Sc, state_t St); + +/* \brief Translates a camera status message to a camera (ECA_) event */ +enum event_codes cam_rep2sym(char *msg); + +/* \brief Converts a camera (ECA_) event to a camera modle (ECM_) event */ +enum event_codes camera_model(enum event_codes event); + +/* \brief Determines camera state from camera status message and reads acquisition + * time if camera is running. + * \param msg camera status message from the "get status" command. + * \param ca_sym symbol for the camera state in the status message, one of ECA_ enums. + * \param time_rem time remaining during while the camera is running. + * \param time_tot total time while the camera is running. + */ +int cam_parse_status(char *msg, enum event_codes *ca_sym, int *time_rem, int *time_tot); + +/* \brief Transition function for the camera system state machine. + * \param Sc the current system state. + * \param Ein input event. + * \param Sn the next system state. + * \param Eo the output events. + */ +int cam_trans_fn(state_t Sc, enum event_codes Ein, state_t *Sn, event_t *Eo); + +/* \brief This is the state machine input function called by the IO layer (ie cameradriver.c) + * + * \param caller, Provides context info for the ouput function callback. + * \param self, Provides current state on input and next state and output event on return. + */ +void camdriv_input(void *caller, camsm_t *self, enum event_codes event_sym); + +/* Convenience functions to convert a state or event to a descriptive string */ +char *strstate(state_t s); +char *strevent(event_t E); + +/* The transition table */ +extern trans_t TRANS_TABLE[]; +#endif diff --git a/site_ansto/hardsup/cameradriver.c b/site_ansto/hardsup/cameradriver.c new file mode 100644 index 00000000..0981ca25 --- /dev/null +++ b/site_ansto/hardsup/cameradriver.c @@ -0,0 +1,757 @@ +/** + * \file cameradriver.c + * \brief This provides the IO layer which feeds inputs from a user or + * the camera server to the state machine, and then sends any state machine + * output back to SICS or the camera. + * + * \Author Ferdi Franceschini February 2013 + * + * Copyright: see file Copyright.txt + */ +#include +#include +#include +#include +#include +#include +#include "camera.h" + +#define ERRLEN 256 +#define MSGLEN 256 + +enum camstates {idle, acquiring, processing, saving}; +#define CAMDRIV_ERRTABLE \ + TR(ENONE, "OK") \ + TR(EFAIL, "command failed") \ + TR(EBUSYACQ, "camera busy acquiring image") \ + TR(EBUSYSAVE, "camera busy saving image") \ + TE(EBUSYPROC, "camera busy processing image") + +#define TR(a,b) a, +#define TE(a,b) a +enum errcodes {CAMDRIV_ERRTABLE}; +#undef TR +#undef TE + +#define TR(a,b) b, +#define TE(a,b) b +static char *errmsg[] = {CAMDRIV_ERRTABLE}; +#undef TR +#undef TE + +#define CAMDRIV_PARTABLE \ + TR(CLOCK, "clock") \ + TR(BIN, "bin") \ + TR(SIZE, "size") \ + TR(GAIN, "gain") \ + TR(FLIP, "flip") \ + TR(XSTART, "xstart") \ + TR(YSTART, "ystart") \ + TR(XEND, "xend") \ + TR(YEND, "yend") \ + TR(EXPOSURE, "exposure") \ + TR(TEMP, "temperature") \ + TR(THRESH, "threshold") \ + TR(SHOPT, "shutteropentime") \ + TE(SHCLT, "shutterclosetime") +#define TR(a,b) 1+ +#define TE(a,b) 1 +static int NUMCAMPAR = CAMDRIV_PARTABLE; +#undef TR +#undef TE + +#define FLIP_TABLE \ + TR(NORMAL, "normal") \ + TR(INVINT, "invintensity") \ + TR(NORMFH, "normfh") \ + TR(NORMFV, "normfv") \ + TR(NORMFHV, "normfhv") \ + TR(INVFH, "invfh") \ + TR(INVFV, "invfv") \ + TE(INVFHV, "invfhv") + +#define TR(a,b) a, +#define TE(a,b) a +enum campar {CAMDRIV_PARTABLE, MULTI}; +enum flipval {FLIP_TABLE}; +#undef TR +#undef TE + +#define TR(a,b) b, +#define TE(a,b) b +static char *cacmdstr[] = {CAMDRIV_PARTABLE, "multi", NULL}; +static char *flipcmdstr[] = {FLIP_TABLE, NULL}; +#undef TR +#undef TE + +// Camera get/set commands: ['status', 'info', 'state', 'camera', 'meta', 'file'] +#define ECMDSTART 0 +#define EGETSTART 100 +#define ESETSTART 200 +typedef struct { + float clockMHz; + float bin; + float size; + float gain; + enum flipval flip; + float xstart; + float ystart; + float xend; + float yend; + float exposure; + float temp; + float thresh; + float shopt; + float shclt; + int updatecfg; +} camcfg_t; + +typedef struct { + int debug; + char *asynq; + camsm_t state_machine; + pNWTimer state_timer; + int status; + enum errcodes camError; + camcfg_t camera; +/* filecfg_t file;*/ + pAsyncUnit asyncUnit; +} CamObj; + +/* Camera communications and protocol handlers */ +static pAsyncProtocol CAM_Protocol = NULL; +static int cb_state_timer(void *ctx, int mode); +static int cb_getstate(pAsyncTxn txn); + +static void CAM_Notify(void* context, int event) { + CamObj *self = (CamObj *) context; + + switch (event) { + case AQU_DISCONNECT: + SICSLogWrite("CAM:(AQU_DISCONNECT)", eLogError); + break; + case AQU_RECONNECT: + SICSLogWrite("CAM:(AQU_RECONNECT)", eLogError); + if (self->state_timer) { + NetWatchRemoveTimer(self->state_timer); + self->state_timer=0; + } + break; + } + return; +} + +static int CAM_Tx(pAsyncProtocol p, pAsyncTxn txn) { + + if (txn == NULL) { + return 0; + } + txn->txn_status = ATX_ACTIVE; + if (AsyncUnitWrite(txn->unit, txn->out_buf, txn->out_len) < 0) { + return 0; + } + return 1; +} + +int defaultHandleInput(pAsyncProtocol p, pAsyncTxn txn, int ch); +static int CAM_Rx(pAsyncProtocol p, pAsyncTxn txn, int ch) { + int ret = 1; + + if (ch == '\r') + ret = 1; + else if (ch == '\n') + ret = AQU_POP_CMD; + else if (txn->inp_idx < txn->inp_len) + txn->inp_buf[txn->inp_idx++] = ch; + else + ret = AQU_POP_CMD; + + return ret; +} +static int CAM_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) { + if (event == AQU_TIMEOUT) { + pTxn->txn_status = ATX_TIMEOUT; + return AQU_POP_CMD; + } + return AQU_POP_CMD; +} + +void CameraInitProtocol(SicsInterp *pSics) { + if (CAM_Protocol == NULL) { + CAM_Protocol = AsyncProtocolCreate(pSics, "CAMERA", NULL, NULL); + CAM_Protocol->sendCommand = CAM_Tx; + CAM_Protocol->handleInput = CAM_Rx; + CAM_Protocol->prepareTxn = NULL; + CAM_Protocol->killPrivate = NULL; + CAM_Protocol->handleEvent = CAM_Ev; + #if 0 + CAM_Protocol->sendTerminator = strdup("\r\n"); + CAM_Protocol->replyTerminator[0] = strdup("\r\n"); + #endif + } +} + +/* CounterDriver interface functions */ +static int CamGetStatus(CounterDriver *cntrData, float *fControl) { + CamObj *camdrv= (CamObj *)cntrData->pData; + + return camdrv->status; +} + +/* \brief run_sm, call the state machine with the given input. + * \param self, driver context including current state + * \param ev_sym, input event + */ +static void run_sm(CamObj *self, enum event_codes ev_sym) { + char sscur[SSLEN+1], ssnext[SSLEN+1], esout[ESLEN+1], message[MSGLEN+1]; + + if (self->debug) + strncpy(sscur, strstate(self->state_machine.Sc), SSLEN); + + camdriv_input(self, &self->state_machine, ev_sym); + + if (self->debug) { + strncpy(ssnext, strstate(self->state_machine.Sc), SSLEN); + strncpy(esout, strevent(self->state_machine.Eo), ESLEN); + snprintf(message, MSGLEN, "DEBUG:(run_sm) Scurr:%s Ei:%s", + sscur,event_names[ev_sym]); + SICSLogWrite(message, eLog); + snprintf(message, MSGLEN, "DEBUG:(run_sm) Snext:%s Eo:%s", ssnext,esout); + SICSLogWrite(message, eLog); + } +} + +/* \brief sendcfg, Send the camera configuration to the camera server + */ +int sendcfg(CamObj *self) { + int status, replen=MSGLEN; + char reply[MSGLEN+1], logmsg[MSGLEN+1]; + char cfgCmd[MSGLEN+1]; + float clock = self->camera.clockMHz; + + sprintf(cfgCmd, + "set camera,clock=%.*fmhz,bin=%dx,size=%d,gain=%dxhs,flip=%s,xstart=%d,ystart=%d,xend=%d,yend=%d,exposure=%f,temperature=%f,threshold=%d,shutteropentime=%d,shutterclosetime=%d", + clock>=1 ? 0 : 1, clock, (int)self->camera.bin, (int)self->camera.size, + (int)self->camera.gain, flipcmdstr[self->camera.flip], + (int)self->camera.xstart, (int)self->camera.ystart, + (int)self->camera.xend, (int)self->camera.yend, self->camera.exposure, + self->camera.temp, (int)self->camera.thresh, (int)self->camera.shopt, + (int)self->camera.shclt + ); + + status = AsyncUnitTransact(self->asyncUnit, cfgCmd, strlen(cfgCmd), reply, &replen); + if (status <= 0) + return 0; + else + if (strncmp("OK", reply, 2) == 0) + return 1; + else { + snprintf(logmsg, MSGLEN, "CAM:(sendcfg) reply=%s", reply); + SICSLogWrite(logmsg, eLogError); + return 0; + } +} + +/* Called by the scan command and via the count and countnb subcommands of a + * counter object. Will update the configuration if necessary. + */ +static int CamStart(CounterDriver *cntrData) { + CamObj *self = NULL; + enum event_codes cd_sym; + char logmsg[MSGLEN+1]; + + self = cntrData->pData; + + /* Send the updated configuration to the camera server if it has been changed + * on SICS since the last shot was taken. */ + if (self->camera.updatecfg) { + if (sendcfg(self) == 0) { + snprintf(logmsg, MSGLEN, "CAM:(CamStart) Failed to upload configuration"); + SICSLogWrite(logmsg, eLogError); + return 0; + } + self->camera.updatecfg = 0; + } + + if (self->state_machine.multi) { + cd_sym = ECD_MLTI_ON; + } else { + cd_sym = ECD_TK_SHOT; + } + run_sm(self, cd_sym); + return 1; +} + +static int CamPause(CounterDriver *cntrData) { + return 1; +} +static int CamContinue(CounterDriver *cntrData) { + return 1; +} +static int CamHalt(CounterDriver *cntrData) { + CamObj *self = cntrData->pData; + + if (self->state_machine.multi) { + run_sm(self, ECD_MLTI_OFF); + } + return 1; +} +/* TODO what should the counter data be set to? Total intensity? */ +static int CamReadValues(CounterDriver *cntrData) { + int status, iReplyLen=MSGLEN; + char *cmd="TODO ", pReply[MSGLEN]; + CamObj *self = NULL; + return 1; + + self = cntrData->pData; + status = AsyncUnitTransact(self->asyncUnit, cmd, strlen(cmd), pReply, &iReplyLen); + return 1; +} + +static int CamGetError(CounterDriver *cntrData, int *iCode, char *error, int iErrLen) { + CamObj *camdrv=NULL; + camdrv = (CamObj *) cntrData->pData; + *iCode = camdrv->camError; + + camdrv->camError = ENONE; + switch (*iCode) { + case EBUSYACQ: + snprintf(error, (size_t) iErrLen, + "CAM: Can't complete operation, %s", errmsg[EBUSYACQ]); + break; + case EBUSYSAVE: + snprintf(error, (size_t) iErrLen, + "CAM: Can't complete operation, %s", errmsg[EBUSYSAVE]); + break; + case EBUSYPROC: + snprintf(error, (size_t) iErrLen, + "CAM: Can't complete operation, %s", errmsg[EBUSYPROC]); + break; + case ENONE: + snprintf(error, (size_t) iErrLen, + "CAM: Can't complete operation, %s", errmsg[ENONE]); + break; + case EFAIL: + snprintf(error, (size_t) iErrLen, "CAM: %s", errmsg[EFAIL]); + break; + } + return 1; +} + +static int CamTryAndFixIt(CounterDriver *cntrData, int iCode) { + return COTERM; +} + +static int CamSet(CounterDriver *cntrData, char *name, int iCter, float fVal) { + CamObj *camdriv= (CamObj *)cntrData->pData; + + int found=0; + char *cmd; + enum campar id; + enum flipval flip; + + for (id=0; (cmd = cacmdstr[id]) != NULL; id++) { + if (strcmp(cmd,name) == 0) { + found=1; + break; + } + } + + if (!found) { + return 0; + } + switch (id) { + case MULTI: + if (fVal != 0 && fVal != 1) { + return 0; + } else { + camdriv->state_machine.multi = fVal; + } + break; + case CLOCK: + if (fVal > 0) { + camdriv->camera.clockMHz = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case BIN: + if (fVal > 0) { + camdriv->camera.bin = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case SIZE: + if (fVal > 0) { + camdriv->camera.size = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case GAIN: + if (fVal > 0) { + camdriv->camera.gain = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case FLIP: + flip = fVal; + switch (flip) { + case NORMAL: + case INVINT: + case NORMFH: + case NORMFV: + case NORMFHV: + case INVFH: + case INVFV: + case INVFHV: + camdriv->camera.flip = flip; + camdriv->camera.updatecfg = 1; + break; + default: + return 0; + break; + } + break; + case XSTART: + if (fVal > 0) { + camdriv->camera.xstart = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case YSTART: + if (fVal > 0) { + camdriv->camera.ystart = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case XEND: + if (fVal > 0) { + camdriv->camera.xend = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case YEND: + if (fVal > 0) { + camdriv->camera.yend = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case EXPOSURE: + if (fVal > 0) { + camdriv->camera.exposure = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case TEMP: + camdriv->camera.temp = fVal; + camdriv->camera.updatecfg = 1; + break; + case THRESH: + if (fVal > 0) { + camdriv->camera.thresh = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case SHOPT: + if (fVal > 0) { + camdriv->camera.shopt = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + case SHCLT: + if (fVal > 0) { + camdriv->camera.shclt = fVal; + camdriv->camera.updatecfg = 1; + } else { + return 0; + } + break; + default: + return 0; + } + return 1; +} + +static int CamGet(CounterDriver *cntrData, char *name, int iCter, float *fVal) { + CamObj *camdriv= (CamObj *)cntrData->pData; + + int found=0; + char *cmd; + enum campar id; + + for (id=0; (cmd = cacmdstr[id]) != NULL; id++) { + if (strcmp(cmd,name) == 0) { + found=1; + break; + } + } + + if (!found) { + return 0; + } + switch (id) { + case MULTI: + *fVal = (float) camdriv->state_machine.multi; + break; + case CLOCK: + *fVal = camdriv->camera.clockMHz; + break; + case BIN: + *fVal = camdriv->camera.bin; + break; + case SIZE: + *fVal = camdriv->camera.size; + break; + case GAIN: + *fVal = camdriv->camera.gain; + break; + case FLIP: + *fVal = camdriv->camera.flip; + break; + case XSTART: + *fVal = camdriv->camera.xstart; + break; + case YSTART: + *fVal = camdriv->camera.ystart; + break; + case XEND: + *fVal = camdriv->camera.xend; + break; + case YEND: + *fVal = camdriv->camera.yend; + break; + case EXPOSURE: + *fVal = camdriv->camera.exposure; + break; + case TEMP: + *fVal = camdriv->camera.temp; + break; + case THRESH: + *fVal = camdriv->camera.thresh; + break; + case SHOPT: + *fVal = camdriv->camera.shopt; + break; + case SHCLT: + *fVal = camdriv->camera.shclt; + break; + default: + return 0; + } + return 1; +} + +static int CamSend(CounterDriver *cntrData, char *pText, char *pReply, int iReplyLen) { + int status; + CamObj *self = NULL; + + self = cntrData->pData; + + status = AsyncUnitTransact(self->asyncUnit, pText, strlen(pText), pReply, &iReplyLen); + if (status <= 0) + return 0; + else + return 1; +} + +static int cb_shotcmd(pAsyncTxn txn) { + CamObj *self = (CamObj *) txn->cntx; + char *resp = txn->inp_buf, message[MSGLEN+1]; + enum event_codes cd_sym; + + if (strncmp(resp, "OK", 2) != 0) { + self->camError = EFAIL; + return 0; + } + return 1; +} + +int camdriv_out(void *me, event_t Eo) { + int len; + char cmd[MSGLEN]="", logmsg[MSGLEN+1]=""; + CamObj *self = (CamObj *)me; + + if (Eo.ca) { + /* send command to camera */ + switch (Eo.ca) { + case ECA_TK_SHOT: + len = strlen(event_signatures[ECA_TK_SHOT]); + strncpy(cmd, event_signatures[ECA_TK_SHOT], len); + break; + case ECA_MLTI_ON: + len = strlen(event_signatures[ECA_MLTI_ON]); + strncpy(cmd, event_signatures[ECA_MLTI_ON], len); + break; + case ECA_MLTI_OFF: + len = strlen(event_signatures[ECA_MLTI_OFF]); + strncpy(cmd, event_signatures[ECA_MLTI_OFF], len); + break; + default: + snprintf(logmsg, MSGLEN, "CAM:(camdriv_out) Unhandled event %s", event_names[Eo.ca]); + SICSLogWrite(logmsg, eLogError); + return 0; + } + AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN); + if (self->debug) { + snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out:Eo.ca): ev=%s, output=%s\n", + event_names[Eo.ca], event_signatures[Eo.ca]); + SICSLogWrite(logmsg, eLog); + } + } + if (Eo.cm) { + snprintf(logmsg, MSGLEN, "TODO:(camdriv_out:Eo.cm): ev=%s, output=%s\n", + event_names[Eo.cm], event_signatures[Eo.cm]); + SICSLogWrite(logmsg, eLogError); + } + if (Eo.cd) { + snprintf(logmsg, MSGLEN, "TODO:(camdriv_out:Eo.cm): ev=%s, output=%s\n", + event_names[Eo.cd], event_signatures[Eo.cd]); + SICSLogWrite(logmsg, eLogError); + } + if (Eo.dr) { + /* send msg to SICS */ + switch (Eo.dr) { + case EDR_IDLE: + self->status = HWIdle; + break; + case EDR_BUSY: + self->status = HWBusy; + break; + case EDR_FAULT: + self->status = HWFault; + break; + default: + snprintf(logmsg, MSGLEN, "CAM:(camdriv_out) Unhandled event %s", event_names[Eo.dr]); + SICSLogWrite(logmsg, eLogError); + return 0; + } + if (self->debug) { + snprintf(logmsg, MSGLEN, "DEBUG:(camdriv_out): ev=%s, output=%s\n", + event_names[Eo.dr], event_signatures[Eo.dr]); + SICSLogWrite(logmsg, eLog); + } + } + return 1; +} +static int cb_state_timer(void *ctx, int mode) { + CamObj *self = (CamObj *) ctx; + char errmsg[32]=""; + char cmd[MSGLEN]; + int len, status; + + len = strlen(event_signatures[ECA_GET_STATE]); + strncpy(cmd, event_signatures[ECA_GET_STATE], len); + status = AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_getstate, self, MSGLEN); + + if (status==1) { + return 1; + } else { + snprintf(errmsg, 31, "CAM:(cb_getstate) AsyncUnitSendTxn failed"); + SICSLogWrite(errmsg, eLogError); + return 0; + } +} + +/* Input from camera state feedback */ +static int cb_getstate(pAsyncTxn txn) { + CamObj *self = (CamObj *) txn->cntx; + char *resp = txn->inp_buf, message[MSGLEN+1]; + int len = txn->inp_idx, ret=1, time_rem, time_tot; + enum event_codes ca_sym, cm_sym; + + + if (txn->txn_status == ATX_TIMEOUT) { + ret = 0; + } else if ( cam_parse_status(resp, &ca_sym, &time_rem, &time_tot) == -1) { + snprintf(message, MSGLEN, + "CAM:(cb_getstate) cam_parse_status failed to parse '%s'",resp); + SICSLogWrite(message, eLogError); + ret = 0; + } else { + cm_sym = camera_model(ca_sym); + run_sm(self, cm_sym); + } + if (self->state_timer) { + NetWatchRemoveTimer(self->state_timer); + self->state_timer=0; + } + NetWatchRegisterTimer(&self->state_timer, 500, cb_state_timer, self); + + return ret; +} + +pCounterDriver CreateCam(SConnection *pCon, char *name, char *asynq) { + char msg[ERRLEN], cmd[MSGLEN], reply[MSGLEN]; + int len, reply_len; + state_t start_state = {.cl=SCL_RDY, .cm=SCM_IDLE, .dr=SDR_IDLE}; + + pCounterDriver pCntDriv = NULL; + CamObj *pNewCam = NULL; + + pNewCam = (CamObj *) malloc(sizeof(CamObj)); + memset(pNewCam, 0, sizeof(CamObj)); + STset(&pNewCam->state_machine.Sc, start_state); + EVclr(&pNewCam->state_machine.Eo); + pNewCam->state_machine.output_fn = camdriv_out; + pNewCam->state_machine.multi = 0; + pNewCam->state_timer = 0; + pNewCam->status = HWIdle; + pNewCam->camError = ENONE; + pNewCam->debug = 1; + pNewCam->camera.updatecfg = 1; + pNewCam->asynq = strdup(asynq); + + if (!AsyncUnitCreate(asynq, &pNewCam->asyncUnit)) { + snprintf(msg, ERRLEN, "CAM:AsyncQueue %s has not been defined", asynq); + SCWrite(pCon, msg, eError); + return NULL; + } + AsyncUnitSetTimeout(pNewCam->asyncUnit, 1000); + AsyncUnitSetNotify(pNewCam->asyncUnit, pNewCam, CAM_Notify); + + pCntDriv = CreateCounterDriver(name, "anstocamera"); + if (pCntDriv == NULL) + return NULL; + + + pCntDriv->GetStatus = CamGetStatus; + pCntDriv->Start = CamStart; + pCntDriv->Pause = CamPause; + pCntDriv->Continue = CamContinue; + pCntDriv->Halt = CamHalt; + pCntDriv->ReadValues = CamReadValues; + pCntDriv->GetError = CamGetError; + pCntDriv->TryAndFixIt = CamTryAndFixIt; + pCntDriv->Set = CamSet; + pCntDriv->Get = CamGet; + pCntDriv->Send = CamSend; + + pCntDriv->iNoOfMonitors = 1; + pCntDriv->pData = pNewCam; + + len = strlen(event_signatures[ECA_GET_STATE]); + strncpy(cmd, event_signatures[ECA_GET_STATE], len); + AsyncUnitSendTxn(pNewCam->asyncUnit, cmd, len, cb_getstate, pNewCam, MSGLEN); + return pCntDriv; +}