Initial implementation of Dingo Camera driver.
Takes a single shot or a multishot. If the user changes the camera configuration in SICS the new config will be uploaded when the next take shot/multishot command is sent. This driver allows the scan object in SICS to drive the scan variable to the next target while the camera is processing and saving data after acquiring an image. TODO Implement "set file,..." command.
This commit is contained in:
8
site_ansto/TESTS/dingo_camera/Makefile
Normal file
8
site_ansto/TESTS/dingo_camera/Makefile
Normal file
@@ -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
|
||||
|
||||
@@ -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
|
||||
194
site_ansto/TESTS/dingo_camera/camera_test.c
Normal file
194
site_ansto/TESTS/dingo_camera/camera_test.c
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* \file camera_test.c
|
||||
* \brief Test camera.[ch] state machine.
|
||||
*
|
||||
* \Author Ferdi Franceschini February 2013
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
8
site_ansto/TESTS/dingo_camera/camrep2sym_expect.txt
Normal file
8
site_ansto/TESTS/dingo_camera/camrep2sym_expect.txt
Normal file
@@ -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
|
||||
14
site_ansto/TESTS/dingo_camera/trans_fn_expect.txt
Normal file
14
site_ansto/TESTS/dingo_camera/trans_fn_expect.txt
Normal file
@@ -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
|
||||
218
site_ansto/hardsup/camera.c
Normal file
218
site_ansto/hardsup/camera.c
Normal file
@@ -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 <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 };
|
||||
#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}}
|
||||
};
|
||||
247
site_ansto/hardsup/camera.h
Normal file
247
site_ansto/hardsup/camera.h
Normal file
@@ -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
|
||||
757
site_ansto/hardsup/cameradriver.c
Normal file
757
site_ansto/hardsup/cameradriver.c
Normal file
@@ -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 <string.h>
|
||||
#include <sics.h>
|
||||
#include <obdes.h>
|
||||
#include <countdriv.h>
|
||||
#include <nwatch.h>
|
||||
#include <asyncqueue.h>
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user