diff --git a/site_ansto/Makefile b/site_ansto/Makefile index a53eb412..b33751d2 100644 --- a/site_ansto/Makefile +++ b/site_ansto/Makefile @@ -167,7 +167,9 @@ OBJ= site_ansto.o anstoutil.o\ hmcontrol_ansto.o\ lssmonitor.o \ beamstopaction.o action.o \ - tclClock.o tclDate.o tclUnixTime.o + tclClock.o tclDate.o tclUnixTime.o \ + camera.o\ + cameradriver.o all: ../matrix/libmatrix.a $(COREOBJ:%=../%) $(EXTRA:%=../%) libansto.a libhardsup $(CC) -g -o SICServer $(COREOBJ:%=../%) $(EXTRA:%=../%) $(SUBLIBS) $(PSI_SLIBS:%=../%) $(PSI_LIBS) $(GHTTP_LIBS) diff --git a/site_ansto/TESTS/dingo_camera/Makefile b/site_ansto/TESTS/dingo_camera/Makefile new file mode 100644 index 00000000..65d18253 --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/Makefile @@ -0,0 +1,5 @@ +# vim: ft=make: ts=8: sw=8: noet: cindent: + +all: camera_test.c ../../camera.c ../../camera.h + gcc -g -Wall -Wextra -std=c99 -pedantic -I../../ -o camera_test camera_test.c ../../camera.c + diff --git a/site_ansto/TESTS/dingo_camera/camera_test b/site_ansto/TESTS/dingo_camera/camera_test new file mode 100755 index 00000000..9ce9f044 Binary files /dev/null and b/site_ansto/TESTS/dingo_camera/camera_test differ 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..20fbbe3b --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/camera_test.c @@ -0,0 +1,132 @@ + +#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, int 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, ca_sym, cm_sym, time_rem, time_tot; + 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 + }; + + 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", event_names[cm_sym], event_names[ca_sym], camrep[i] ); + else + printf("%s(%s): time remaining = %d, total time = %d\n",event_names[cm_sym], event_names[ca_sym], time_rem, time_tot); + } + } + 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 + }; + + 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"); + } + return 1; +} + +int camdriv_out(event_t Eo) { + if (Eo.ca) { + /* send command to camera */ + printf("camdriv_out: ev=%s, output=%s\n", event_names[Eo.ca], event_signatures[Eo.ca]); + } + if (Eo.cm) { + printf("camdriv_out: ev=%s, output=%s\n", event_names[Eo.cm], event_signatures[Eo.cm]); + } + if (Eo.cd) { + printf("camdriv_out: ev=%s, output=%s\n", event_names[Eo.cd], event_signatures[Eo.cd]); + } + if (Eo.dr) { + /* send msg to SICS */ + printf("camdriv_out: ev=%s, output=%s\n", event_names[Eo.dr], event_signatures[Eo.dr]); + } + return 1; +} +int test_camdriv_input(void) { + int Ein=ECD_TK_SHOT; + camdriv_t cdinfo = { + .Sc = {.cl=SCL_RDY, .cm=SCM_IDLE, .dr=SDR_IDLE}, + .Eo = {.ca=0}, + .multi = 0, + .output_fn = camdriv_out, + }; + + camdriv_input(&cdinfo, Ein); + return 1; +} + +int main() { + int ret; + + ret = test_camrep2sym(); + ret = test_trans_fn(); + ret = test_camdriv_input(); + + if (ret) + return 0; + else + return 1; +} diff --git a/site_ansto/TESTS/dingo_camera/expect.txt b/site_ansto/TESTS/dingo_camera/expect.txt new file mode 100644 index 00000000..fd902120 --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/expect.txt @@ -0,0 +1,22 @@ +ECM_ACQ(ECA_START): time remaining = 3907, total time = 3686 +ECM_ACQ(ECA_OPEN_SHUTR): time remaining = 539454, total time = 3686 +ECM_ACQ(ECA_ACQ_IMAGE): time remaining = 109, total time = 3686 +ECM_PROC(ECA_CLOSE_SHUTR): time remaining = 218, total time = 3686 +ECM_PROC(ECA_READ_IMAGE): time remaining = 328, total time = 3686 +ECM_PROC(ECA_READ_IMAGE): time remaining = 328, total time = 3686 +ECM_PROC(ECA_READ_IMAGE): time remaining = 328, total time = 3686 +ECM_IDLE(ECA_IDLE) <- Idle +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_ACQ,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_ACQ,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/TESTS/dingo_camera/test.txt b/site_ansto/TESTS/dingo_camera/test.txt new file mode 100644 index 00000000..fd902120 --- /dev/null +++ b/site_ansto/TESTS/dingo_camera/test.txt @@ -0,0 +1,22 @@ +ECM_ACQ(ECA_START): time remaining = 3907, total time = 3686 +ECM_ACQ(ECA_OPEN_SHUTR): time remaining = 539454, total time = 3686 +ECM_ACQ(ECA_ACQ_IMAGE): time remaining = 109, total time = 3686 +ECM_PROC(ECA_CLOSE_SHUTR): time remaining = 218, total time = 3686 +ECM_PROC(ECA_READ_IMAGE): time remaining = 328, total time = 3686 +ECM_PROC(ECA_READ_IMAGE): time remaining = 328, total time = 3686 +ECM_PROC(ECA_READ_IMAGE): time remaining = 328, total time = 3686 +ECM_IDLE(ECA_IDLE) <- Idle +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_ACQ,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_ACQ,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/camera.c b/site_ansto/camera.c new file mode 100644 index 00000000..06148d61 --- /dev/null +++ b/site_ansto/camera.c @@ -0,0 +1,197 @@ +#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; + } +} +/* NOTE the state is actually of the form (SCL_X,SCM_X,SDR_X) + * the table only shows the part of the state tuple which must match. + */ + +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; +} + +void camdriv_input(void *caller, camdriv_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; +} + +/* 0 = wildcard for states. + * 0 = no output for the output events. + */ +int START_STATE = SCL_RDY|SCM_IDLE|SDR_IDLE; +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_TK_MLTI,{.dr=0}, {.cl=SCL_TK_MLTI}}, +{{.cl=SCL_TK_SHOT}, ECM_IDLE, {.ca=ECA_TK_SHOT}, {.cl=SCL_WT_ACQ}}, +{{.cl=SCL_TK_MLTI}, ECM_IDLE, {.ca=ECA_MLTI_ON}, {.cl=SCL_WT_ACQ}}, +{{.cl=SCL_TK_SHOT}, ECD_CLEAR, {.dr=0}, {.cl=SCL_RDY}}, +{{.cl=SCL_TK_MLTI}, ECD_CLEAR, {.dr=0}, {.cl=SCL_RDY}}, +{{.cl=SCL_WT_ACQ}, ECM_ACQ, {.dr=0}, {.cl=SCL_RDY}}, +{{.cl=SCL_WT_ACQ}, ECM_PROC, {.dr=0}, {.cl=SCL_RDY}}, +{{.cl=SCL_WT_ACQ}, ECM_STOP, {.dr=0}, {.cl=SCL_RDY}}, +/* ffr: Uncomment when we're sure that we don't receive "Idle" after "take shot" + * {{.cl=SCL_WT_ACQ}, 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_TK_MLTI,{.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/camera.h b/site_ansto/camera.h new file mode 100644 index 00000000..2cbdde2a --- /dev/null +++ b/site_ansto/camera.h @@ -0,0 +1,213 @@ +/* Deterministic Finite State machine transducer (Mealy) + * The system is made up of three commponents, 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 stae 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. + * + * 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 +/* TODO + * int output(state, event) return output event code + * int transition(state,event) return next state + * + */ + +/* 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_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_CMD_INPUT, "received a command") \ + TR(ECD_CLEAR, "Clear latched command") \ + TR(ECD_TK_SHOT, "take shot") \ + TR(ECD_TK_MLTI, "take multi on") \ + TR(ECD_GET_STATE, "get state") \ + TE(ECD_GET_STATUS, "get status") + +/* DRIVER EVENT TABLE + */ +#define DRIVER_EVENT_TABLE \ + TR(EDR_IDLE, "HWIDLE") \ + TR(EDR_BUSY, "HWBUSY") \ + TE(EDR_FAULT, "HWFAULT") + +#define ESLEN 32 + +/* STATE CODES + * NOTE: Every state will respond to a timer event and + * 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") \ + TE(SCL_WT_ACQ, "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 + +/* Generate 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. Defined in camera.c. */ +extern char *event_names[]; + +/* The signature array defines the identifying characters at the start of a reply. */ +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; +typedef struct { + state_t Sc; + enum event_codes Ei; + event_t Eo; + state_t Sn; +} trans_t; + +typedef struct { + state_t Sc; + event_t Eo; + int multi; + int (*output_fn)(void *caller, event_t Eo); +} camdriv_t; + +/* 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 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) +#define INSYS(Sc,St) ( INCL(Sc,St) && INCM(Sc,St) && INDR(Sc,St) ) + + +void EVclr(event_t *E); +void EVset(event_t *E, event_t Ev); +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); + +void camdriv_input(void *caller, camdriv_t *self, enum event_codes event_sym); + +char *strstate(state_t s); +char *strevent(event_t E); +extern trans_t TRANS_TABLE[]; +#endif diff --git a/site_ansto/cameradriver.c b/site_ansto/cameradriver.c new file mode 100644 index 00000000..7d13251c --- /dev/null +++ b/site_ansto/cameradriver.c @@ -0,0 +1,378 @@ +/* MGS_ :Message from camera Get Status command + */ +#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 +char *errmsg[] = {CAMDRIV_ERRTABLE}; +#undef TR +#undef TE + +// Camera get/set commands: ['status', 'info', 'state', 'camera', 'meta', 'file'] +#define ECMDSTART 0 +#define EGETSTART 100 +#define ESETSTART 200 +enum CommandCodes {cmdTake = 1, cmdClear, cmdGet, cmdSet, cmdEnd}; +enum GetCodes {gCamera = 1, gFile, gMeta, gInfo, gStatus, gState, gEnd}; +enum SetCodes {sCamera = 1, sFile, sMeta, sInfo, sEnd}; +char *CMD[13] = {"temp", "shspd", "ffmt", "binres", "imsz", "xstart", "ystart", "floc", "fname", "fcntr", "acq", "status", "shtime"}; + +struct __cameraObj { + int debug; + camdriv_t state_machine; + pNWTimer state_timer; + int status; + enum errcodes camError; + pAsyncUnit asyncUnit; +}; +typedef struct __cameraObj CamObj; + +/* Camera communications and protocol handlers */ +static pAsyncProtocol CAM_Protocol = NULL; + +static int CAM_Tx(pAsyncProtocol p, pAsyncTxn txn) { + int ret = 1; + + if (txn) { + txn->txn_status = ATX_ACTIVE; + if (AsyncUnitWrite(txn->unit, txn->out_buf, txn->out_len) < 0) { + if (AsyncUnitReconnect(txn->unit) < 0) + ret = 0; + } + ret = 1; + } else { + ret = 0; + } + return ret; +} +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; +} +/* TODO +static int CAM_Ev(pAsyncProtocol p, pAsyncTxn txn, int event) { +} +*/ + +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; + #if 0 + CAM_Protocol->handleEvent = CAM_Ev; + 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; +} +static int replyOK(enum CommandCodes cc, char *reply) { + return 1; +} + +static void crank_state_machine(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:(%s:%d) Scurr:%s Ein:%s|Snext:%s Eout:%s",__FILE__,__LINE__, + sscur,event_names[ev_sym],ssnext,esout); + SICSLogWrite(message, eLog); + } +} +static int CamStart(CounterDriver *cntrData) { + CamObj *self = NULL; + enum event_codes cd_sym; + + self = cntrData->pData; + + if (self->state_machine.multi) { + cd_sym = ECD_TK_MLTI; + } else { + cd_sym = ECD_TK_SHOT; + } + crank_state_machine(self, cd_sym); + return 1; +} +static int CamPause(CounterDriver *cntrData) { + return 1; +} +static int CamContinue(CounterDriver *cntrData) { + return 1; +} +static int CamHalt(CounterDriver *cntrData) { + return 1; +} +static int CamReadValues(CounterDriver *cntrData) { + int status, iReplyLen=MSGLEN; + char *cmd="get imsz", pReply[MSGLEN]; + CamObj *self = NULL; + return 1; + + self = cntrData->pData; + // fTime = [get shtime] + 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, "CAMERR: Can't complete operation, %s", errmsg[EBUSYACQ]); + break; + case EBUSYSAVE: + snprintf(error, (size_t) iErrLen, "CAMERR: Can't complete operation, %s", errmsg[EBUSYSAVE]); + break; + case EBUSYPROC: + snprintf(error, (size_t) iErrLen, "CAMERR: Can't complete operation, %s", errmsg[EBUSYPROC]); + break; + case ENONE: + snprintf(error, (size_t) iErrLen, "CAMERR: Can't complete operation, %s", errmsg[ENONE]); + break; + case EFAIL: + snprintf(error, (size_t) iErrLen, "CAMERR: %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) { + return 1; +} +static int CamGet(CounterDriver *cntrData, char *name, int iCter, float *fVal) { + 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); + 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]; + 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; + } + AsyncUnitSendTxn(self->asyncUnit, cmd, len, cb_shotcmd, self, MSGLEN); + if (self->debug) { + snprintf(logmsg, MSGLEN, "DEBUG: camdriv_out: ev=%s, output=%s\n", event_names[Eo.ca], event_signatures[Eo.ca]); + SICSLogWrite(logmsg, eLog); + } + } + if (Eo.cm) { + snprintf(logmsg, MSGLEN, "ERROR: NOT IMPLEMENTED, camdriv_out: ev=%s, output=%s\n", event_names[Eo.cm], event_signatures[Eo.cm]); + SICSLogWrite(logmsg, eLogError); + } + if (Eo.cd) { + snprintf(logmsg, MSGLEN, "ERROR: NOT IMPLEMENTED, camdriv_out: 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; + } + 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_getstate(pAsyncTxn txn); +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, "ERROR:(%s) AsyncUnitTransact failed",__FILE__); + 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 ( cam_parse_status(resp, &ca_sym, &time_rem, &time_tot) == -1) { + snprintf(message, MSGLEN, "ERROR:(%s:%d) cam_parse_status failed on '%s'",__FILE__,__LINE__,resp); + SICSLogWrite(message, eLogError); + ret = 0; + } else { + cm_sym = camera_model(ca_sym); + crank_state_machine(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; + + if (!AsyncUnitCreate(asynq, &pNewCam->asyncUnit)) { + snprintf(msg, ERRLEN, "CAMERR:AsyncQueue %s has not been defined", asynq); + SCWrite(pCon, msg, eError); + return NULL; + } + + 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; +} +#if 0 +int CameraAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { +CamObj *pCam = (CamObj *) pData; +} +int CamMakeCmd(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { + CamObj *pNew = NULL; + + pNew = CreateCam(argv[1], argv[2]); + if (pNew == NULL) + return 0; + AddCommand(pSics, argv[1], CameraAction, NULL, pNew); + return 1; +} +#endif diff --git a/site_ansto/cameradriver.h b/site_ansto/cameradriver.h new file mode 100644 index 00000000..3e0d3a01 --- /dev/null +++ b/site_ansto/cameradriver.h @@ -0,0 +1,4 @@ +#ifndef CAMERADRIVER_H +#define CAMERADRIVER_H +void CameraInitProtocol(SicsInterp *pSics); +#endif diff --git a/site_ansto/hardsup/makefile b/site_ansto/hardsup/makefile index 33b8c0b4..939b3265 100644 --- a/site_ansto/hardsup/makefile +++ b/site_ansto/hardsup/makefile @@ -32,6 +32,7 @@ HOBJ += sct_rfamp.o HOBJ += sinqhttpprot.o HOBJ += sct_protek608.o HOBJ += sct_lfprot.o +HOBJ += camserv.o libhlib.a: $(HOBJ) rm -f libhlib.a diff --git a/site_ansto/instrument/barebones.tcl b/site_ansto/instrument/barebones.tcl index 92365c4e..194ab486 100644 --- a/site_ansto/instrument/barebones.tcl +++ b/site_ansto/instrument/barebones.tcl @@ -9,7 +9,7 @@ # Required by server_config.tcl VarMake Instrument Text Internal -Instrument echidna +Instrument bear Instrument lock #START SERVER CONFIGURATION SECTION diff --git a/site_ansto/site_ansto.c b/site_ansto/site_ansto.c index 980ababd..796906d0 100644 --- a/site_ansto/site_ansto.c +++ b/site_ansto/site_ansto.c @@ -48,6 +48,7 @@ #include "safetyplc.h" #include "lssmonitor.h" #include "beamstopaction.h" +#include "cameradriver.h" /*@observer@*//*@null@*/ pCounterDriver CreateMonCounter(/*@observer@*/SConnection *pCon, /*@observer@*/char *name, char *params); @@ -65,6 +66,7 @@ extern void AddTCPMBProtocol (); extern void AddLFGenProtocol(); extern int ANSTO_MakeHistMemory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); extern int testLogCmd(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); +extern pCounterDriver CreateCam(SConnection *pCon, char *name, char *asynq); int SICS_Site(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { @@ -134,6 +136,7 @@ static void AddCommands(SicsInterp *pInter) NHQ200InitProtocol(pInter); ORHVPSInitProtocol(pInter); LS340InitProtocol(pInter); + CameraInitProtocol(pInter); AddCommand(pInter,"InstallProtocolHandler", InstallProtocol,NULL,NULL); AddCommand(pInter,"hostnam",hostNamCmd,NULL,NULL); AddCommand(pInter,"portnum",portNumCmd,NULL,NULL); @@ -224,6 +227,8 @@ static pCounterDriver CreateCounterDriverAnsto(SConnection *pCon, strtolower(argv[2]); if(strcmp(argv[2],"anstomonitor") == 0) { pDriver = CreateMonCounter(pCon, argv[1], argv[3]); + } else if (strcmp(argv[2], "anstocamera") == 0) { + pDriver = CreateCam(pCon, argv[1], argv[3]); } if(!pDriver){ return NULL;