scpi-pps: Create one channel per MQ/output combination.

This allows frontends to trivially select which MQs on which channels
they want to see; others won't even be fetched.
This commit is contained in:
Bert Vermeulen 2014-09-08 03:26:19 +02:00
parent 379d260965
commit 01b0257aef
3 changed files with 107 additions and 63 deletions

View File

@ -30,6 +30,12 @@ static const int32_t scanopts[] = {
SR_CONF_SERIALCOMM, SR_CONF_SERIALCOMM,
}; };
static struct pps_channel_instance pci[] = {
{ SR_MQ_VOLTAGE, SCPI_CMD_GET_MEAS_VOLTAGE, "V" },
{ SR_MQ_CURRENT, SCPI_CMD_GET_MEAS_CURRENT, "I" },
{ SR_MQ_POWER, SCPI_CMD_GET_MEAS_POWER, "P" },
};
static int init(struct sr_context *sr_ctx) static int init(struct sr_context *sr_ctx)
{ {
return std_init(sr_ctx, di, LOG_PREFIX); return std_init(sr_ctx, di, LOG_PREFIX);
@ -43,13 +49,16 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
struct sr_channel_group *cg; struct sr_channel_group *cg;
struct sr_channel *ch; struct sr_channel *ch;
const struct scpi_pps *device; const struct scpi_pps *device;
struct pps_channel *pch;
const struct channel_group_spec *cgs; const struct channel_group_spec *cgs;
struct pps_channel_group *pcg; struct pps_channel_group *pcg;
GRegex *model_re; GRegex *model_re;
GMatchInfo *model_mi; GMatchInfo *model_mi;
GSList *l;
uint64_t mask; uint64_t mask;
unsigned int i, j; unsigned int ch_num, ch_idx, old_idx, i, j;
const char *vendor; const char *vendor;
char ch_name[16];
if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) { if (sr_scpi_get_hw_id(scpi, &hw_info) != SR_OK) {
sr_info("Couldn't get IDN response."); sr_info("Couldn't get IDN response.");
@ -83,10 +92,31 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
devc->device = device; devc->device = device;
sdi->priv = devc; sdi->priv = devc;
for (i = 0; i < device->num_channels; i++) { ch_idx = 0;
ch = sr_channel_new(i, SR_CHANNEL_ANALOG, TRUE, for (ch_num = 0; ch_num < device->num_channels; ch_num++) {
device->channels[i].name); /* Create one channel per measurable output unit. */
sdi->channels = g_slist_append(sdi->channels, ch); old_idx = ch_idx;
for (i = 0; i < ARRAY_SIZE(pci); i++) {
if (!scpi_cmd_get(sdi, pci[i].command))
continue;
g_snprintf(ch_name, 16, "%s%s", pci[i].prefix,
device->channels[ch_num].name);
ch = sr_channel_new(ch_idx++, SR_CHANNEL_ANALOG, TRUE, ch_name);
pch = g_malloc0(sizeof(struct pps_channel));
pch->hw_output_idx = ch_num;
pch->hwname = device->channels[ch_num].name;
pch->mq = pci[i].mq;
ch->priv = pch;
sdi->channels = g_slist_append(sdi->channels, ch);
}
if (ch_idx == old_idx) {
/*
* Didn't create any channels for this hardware output.
* This can happen if the device has no measurement capability.
*/
g_free(pch);
continue;
}
} }
for (i = 0; i < device->num_channel_groups; i++) { for (i = 0; i < device->num_channel_groups; i++) {
@ -95,8 +125,12 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
cg->name = g_strdup(cgs->name); cg->name = g_strdup(cgs->name);
for (j = 0, mask = 1; j < 64; j++, mask <<= 1) { for (j = 0, mask = 1; j < 64; j++, mask <<= 1) {
if (cgs->channel_index_mask & mask) { if (cgs->channel_index_mask & mask) {
ch = g_slist_nth_data(sdi->channels, j); for (l = sdi->channels; l; l = l->next) {
cg->channels = g_slist_append(cg->channels, ch); ch = l->data;
pch = ch->priv;
if (pch->hw_output_idx == j)
cg->channels = g_slist_append(cg->channels, ch);
}
} }
} }
pcg = g_malloc0(sizeof(struct pps_channel_group)); pcg = g_malloc0(sizeof(struct pps_channel_group));
@ -171,6 +205,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
struct dev_context *devc; struct dev_context *devc;
struct sr_scpi_dev_inst *scpi; struct sr_scpi_dev_inst *scpi;
struct sr_channel *ch; struct sr_channel *ch;
struct pps_channel *pch;
const GVariantType *gvtype; const GVariantType *gvtype;
unsigned int i; unsigned int i;
int cmd, ret; int cmd, ret;
@ -187,8 +222,6 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
* These options only apply to channel groups with a single * These options only apply to channel groups with a single
* channel -- they're per-channel settings for the device. * channel -- they're per-channel settings for the device.
*/ */
if (g_slist_length(cg->channels) > 1)
return SR_ERR_NA;
/* /*
* Config keys are handled below depending on whether a channel * Config keys are handled below depending on whether a channel
@ -204,6 +237,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
} }
ch = cg->channels->data; ch = cg->channels->data;
pch = ch->priv;
} }
gvtype = NULL; gvtype = NULL;
@ -260,14 +294,14 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi,
} }
if (gvtype) { if (gvtype) {
if (cg) if (cg)
ret = scpi_cmd_resp(sdi, data, gvtype, cmd, ch->name); ret = scpi_cmd_resp(sdi, data, gvtype, cmd, pch->hwname);
else else
ret = scpi_cmd_resp(sdi, data, gvtype, cmd); ret = scpi_cmd_resp(sdi, data, gvtype, cmd);
} else if (cg) { } else if (cg) {
switch (key) { switch (key) {
case SR_CONF_OUTPUT_REGULATION: case SR_CONF_OUTPUT_REGULATION:
ret = SR_ERR; ret = SR_ERR;
if (scpi_cmd(sdi, SCPI_CMD_GET_OUTPUT_REGULATION, ch->name) == SR_OK) { if (scpi_cmd(sdi, SCPI_CMD_GET_OUTPUT_REGULATION, pch->hwname) == SR_OK) {
if (sr_scpi_get_string(scpi, NULL, &s) == SR_OK) { if (sr_scpi_get_string(scpi, NULL, &s) == SR_OK) {
if (strcmp(s, "CC") && strcmp(s, "CV") && strcmp(s, "UR")) { if (strcmp(s, "CC") && strcmp(s, "CV") && strcmp(s, "UR")) {
sr_dbg("Unknown response to SCPI_CMD_GET_OUTPUT_REGULATION: %s", s); sr_dbg("Unknown response to SCPI_CMD_GET_OUTPUT_REGULATION: %s", s);
@ -292,6 +326,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
const struct sr_channel_group *cg) const struct sr_channel_group *cg)
{ {
struct sr_channel *ch; struct sr_channel *ch;
struct pps_channel *pch;
double d; double d;
int ret; int ret;
const char *s; const char *s;
@ -329,38 +364,39 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
if (g_slist_length(cg->channels) > 1) if (g_slist_length(cg->channels) > 1)
return SR_ERR_NA; return SR_ERR_NA;
ch = cg->channels->data; ch = cg->channels->data;
pch = ch->priv;
switch (key) { switch (key) {
case SR_CONF_OUTPUT_ENABLED: case SR_CONF_OUTPUT_ENABLED:
s = g_variant_get_boolean(data) ? "ON" : "OFF"; s = g_variant_get_boolean(data) ? "ON" : "OFF";
ret = scpi_cmd(sdi, SCPI_CMD_SET_OUTPUT_ENABLED, ch->name, s); ret = scpi_cmd(sdi, SCPI_CMD_SET_OUTPUT_ENABLED, pch->hwname, s);
break; break;
case SR_CONF_OUTPUT_VOLTAGE_MAX: case SR_CONF_OUTPUT_VOLTAGE_MAX:
d = g_variant_get_double(data); d = g_variant_get_double(data);
ret = scpi_cmd(sdi, SCPI_CMD_SET_VOLTAGE_MAX, ch->name, d); ret = scpi_cmd(sdi, SCPI_CMD_SET_VOLTAGE_MAX, pch->hwname, d);
break; break;
case SR_CONF_OUTPUT_CURRENT_MAX: case SR_CONF_OUTPUT_CURRENT_MAX:
d = g_variant_get_double(data); d = g_variant_get_double(data);
ret = scpi_cmd(sdi, SCPI_CMD_SET_CURRENT_MAX, ch->name, d); ret = scpi_cmd(sdi, SCPI_CMD_SET_CURRENT_MAX, pch->hwname, d);
break; break;
case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED: case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
s = g_variant_get_boolean(data) ? "ON" : "OFF"; s = g_variant_get_boolean(data) ? "ON" : "OFF";
ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLED, ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_ENABLED,
ch->name, s); pch->hwname, s);
break; break;
case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD: case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
d = g_variant_get_double(data); d = g_variant_get_double(data);
ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD, ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_VOLTAGE_PROTECTION_THRESHOLD,
ch->name, d); pch->hwname, d);
break; break;
case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED: case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
s = g_variant_get_boolean(data) ? "ON" : "OFF"; s = g_variant_get_boolean(data) ? "ON" : "OFF";
ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLED, ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_CURRENT_PROTECTION_ENABLED,
ch->name, s); pch->hwname, s);
break; break;
case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD: case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
d = g_variant_get_double(data); d = g_variant_get_double(data);
ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD, ret = scpi_cmd(sdi, SCPI_CMD_SET_OVER_CURRENT_PROTECTION_THRESHOLD,
ch->name, d); pch->hwname, d);
break; break;
default: default:
ret = SR_ERR_NA; ret = SR_ERR_NA;
@ -424,17 +460,12 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
} }
} else { } else {
/* Channel group specified. */ /* Channel group specified. */
if (!sdi)
return SR_ERR_ARG;
/* /*
* Per-channel-group options depending on a channel are actually * Per-channel-group options depending on a channel are actually
* done with the first channel. Channel groups in PPS can have * done with the first channel. Channel groups in PPS can have
* more than one channel, but they will typically be of equal * more than one channel, but they will typically be of equal
* specification for use in series or parallel mode. Drop requests * specification for use in series or parallel mode.
* for groups with more than one channel just to make sure.
*/ */
if (g_slist_length(cg->channels) > 1)
return SR_ERR_NA;
ch = cg->channels->data; ch = cg->channels->data;
switch (key) { switch (key) {
@ -477,7 +508,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
struct dev_context *devc; struct dev_context *devc;
struct sr_scpi_dev_inst *scpi; struct sr_scpi_dev_inst *scpi;
struct sr_channel *ch; struct sr_channel *ch;
int ret; struct pps_channel *pch;
int cmd, ret;
if (sdi->status != SR_ST_ACTIVE) if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED; return SR_ERR_DEV_CLOSED;
@ -486,23 +518,30 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi,
scpi = sdi->conn; scpi = sdi->conn;
devc->cb_data = cb_data; devc->cb_data = cb_data;
if ((ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 100, if ((ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 10,
scpi_pps_receive_data, (void *)sdi)) != SR_OK) scpi_pps_receive_data, (void *)sdi)) != SR_OK)
return ret; return ret;
std_session_send_df_header(sdi, LOG_PREFIX); std_session_send_df_header(sdi, LOG_PREFIX);
/* Prime the pipe. */ /* Prime the pipe with the first channel's fetch. */
devc->state = STATE_VOLTAGE;
ch = sdi->channels->data; ch = sdi->channels->data;
pch = ch->priv;
devc->cur_channel = ch; devc->cur_channel = ch;
scpi_cmd(sdi, SCPI_CMD_GET_MEAS_VOLTAGE, ch->name); if (pch->mq == SR_MQ_VOLTAGE)
cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
else if (pch->mq == SR_MQ_CURRENT)
cmd = SCPI_CMD_GET_MEAS_CURRENT;
else if (pch->mq == SR_MQ_POWER)
cmd = SCPI_CMD_GET_MEAS_POWER;
else
return SR_ERR;
scpi_cmd(sdi, cmd, pch->hwname);
return SR_OK; return SR_OK;
} }
static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
{ {
struct dev_context *devc;
struct sr_scpi_dev_inst *scpi; struct sr_scpi_dev_inst *scpi;
float f; float f;
@ -511,7 +550,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
if (sdi->status != SR_ST_ACTIVE) if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED; return SR_ERR_DEV_CLOSED;
devc = sdi->priv;
scpi = sdi->conn; scpi = sdi->conn;
/* /*
@ -522,9 +560,6 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
sr_scpi_get_float(scpi, NULL, &f); sr_scpi_get_float(scpi, NULL, &f);
sr_scpi_source_remove(sdi->session, scpi); sr_scpi_source_remove(sdi->session, scpi);
/* Just in case something is queued up. */
devc->state = STATE_STOP;
return SR_OK; return SR_OK;
} }

View File

@ -107,8 +107,10 @@ SR_PRIV int scpi_pps_receive_data(int fd, int revents, void *cb_data)
struct sr_datafeed_analog analog; struct sr_datafeed_analog analog;
const struct sr_dev_inst *sdi; const struct sr_dev_inst *sdi;
struct sr_scpi_dev_inst *scpi; struct sr_scpi_dev_inst *scpi;
struct pps_channel *pch;
GSList *l; GSList *l;
float f; float f;
int cmd;
(void)fd; (void)fd;
(void)revents; (void)revents;
@ -119,51 +121,47 @@ SR_PRIV int scpi_pps_receive_data(int fd, int revents, void *cb_data)
if (!(devc = sdi->priv)) if (!(devc = sdi->priv))
return TRUE; return TRUE;
if (devc->state == STATE_STOP)
return TRUE;
scpi = sdi->conn; scpi = sdi->conn;
/* Retrieve requested value for this state. */ /* Retrieve requested value for this state. */
if (sr_scpi_get_float(scpi, NULL, &f) == SR_OK) { if (sr_scpi_get_float(scpi, NULL, &f) == SR_OK) {
pch = devc->cur_channel->priv;
packet.type = SR_DF_ANALOG; packet.type = SR_DF_ANALOG;
packet.payload = &analog; packet.payload = &analog;
analog.channels = g_slist_append(NULL, devc->cur_channel); analog.channels = g_slist_append(NULL, devc->cur_channel);
analog.num_samples = 1; analog.num_samples = 1;
if (devc->state == STATE_VOLTAGE) { analog.mq = pch->mq;
analog.mq = SR_MQ_VOLTAGE; if (pch->mq == SR_MQ_VOLTAGE)
analog.unit = SR_UNIT_VOLT; analog.unit = SR_UNIT_VOLT;
} else { else if (pch->mq == SR_MQ_CURRENT)
analog.mq = SR_MQ_CURRENT;
analog.unit = SR_UNIT_AMPERE; analog.unit = SR_UNIT_AMPERE;
} else if (pch->mq == SR_MQ_POWER)
analog.unit = SR_UNIT_WATT;
analog.mqflags = SR_MQFLAG_DC; analog.mqflags = SR_MQFLAG_DC;
analog.data = &f; analog.data = &f;
sr_session_send(sdi, &packet); sr_session_send(sdi, &packet);
g_slist_free(analog.channels); g_slist_free(analog.channels);
} }
if (devc->state == STATE_VOLTAGE) { /* Find next enabled channel. */
/* Just got voltage, request current for this channel. */ do {
devc->state = STATE_CURRENT; l = g_slist_find(sdi->channels, devc->cur_channel);
scpi_cmd(sdi, SCPI_CMD_GET_MEAS_CURRENT, devc->cur_channel->name); if (l->next)
} else if (devc->state == STATE_CURRENT) { devc->cur_channel = l->next->data;
/* else
* Done with voltage and current for this channel, switch to devc->cur_channel = sdi->channels->data;
* the next enabled channel. } while (!devc->cur_channel->enabled);
*/
do {
l = g_slist_find(sdi->channels, devc->cur_channel);
if (l->next)
devc->cur_channel = l->next->data;
else
devc->cur_channel = sdi->channels->data;
} while (!devc->cur_channel->enabled);
/* Request voltage. */ pch = devc->cur_channel->priv;
devc->state = STATE_VOLTAGE; if (pch->mq == SR_MQ_VOLTAGE)
scpi_cmd(sdi, SCPI_CMD_GET_MEAS_VOLTAGE, devc->cur_channel->name); cmd = SCPI_CMD_GET_MEAS_VOLTAGE;
} else if (pch->mq == SR_MQ_CURRENT)
cmd = SCPI_CMD_GET_MEAS_CURRENT;
else if (pch->mq == SR_MQ_POWER)
cmd = SCPI_CMD_GET_MEAS_POWER;
else
return SR_ERR;
scpi_cmd(sdi, cmd, pch->hwname);
return TRUE; return TRUE;
} }

View File

@ -101,6 +101,18 @@ struct channel_group_spec {
uint64_t features; uint64_t features;
}; };
struct pps_channel {
int mq;
unsigned int hw_output_idx;
char *hwname;
};
struct pps_channel_instance {
int mq;
int command;
char *prefix;
};
struct pps_channel_group { struct pps_channel_group {
uint64_t features; uint64_t features;
}; };
@ -122,7 +134,6 @@ struct dev_context {
/* Operational state */ /* Operational state */
/* Temporary state across callbacks */ /* Temporary state across callbacks */
int state;
struct sr_channel *cur_channel; struct sr_channel *cur_channel;
}; };