diff --git a/mc/Makefile b/mc/Makefile index d77a88f0ff7cea977af84501bb286e0119121177..e23c384ae006cf987e7773ad303bbb67c73dab91 100644 --- a/mc/Makefile +++ b/mc/Makefile @@ -5,7 +5,8 @@ TARGETS=lth_fixes.mc \ hijacknet_lth_net.mc \ fei82557End.mc \ hijacknet_test.mc \ - extctrl2014_lc.mc + extctrl2014_lc.mc \ + hijacknet_extctrl2014.mc AUTO_GEN=extctrl2014_lc.c \ extctrl2014_lc.h \ mc.globalize.c @@ -19,6 +20,10 @@ EXTRA_CFLAGS_hijacknet_lth_net=-I/opt/robot/include EXTRA_CFLAGS_fei82557End=-Wno-error -Wno-all -Wno-endif-labels EXTRA_CFLAGS_extctrl2014_lc=-I/usr/include/labcomm \ -DLABCOMM_COMPAT="<labcomm_compat.h>" +EXTRA_CFLAGS_hijacknet_extctrl2014=-I/usr/include/labcomm \ + -DLABCOMM_COMPAT="<labcomm_compat.h>" +EXTRA_LDFLAGS_hijacknet_extctrl2014=extctrl2014_lc.mc -Wl,-r,-llabcomm2014 +HIDE_hijacknet_extctrl2014=labcomm2014_ ROBOT= ROBOTWARE=$(shell [ -n "$(ROBOT)" ] && \ ../tools/abb_extctrl_setup.py --robot "$(ROBOT)" --robotware) @@ -32,7 +37,7 @@ all: $(TARGETS) mc_test_load: hijack_fei.mc.load spray_test.mc.load -%.mc: %.mc.c +%.mc: %.mc.c Makefile WIND_BASE=/opt/robot/include/vxworks/5.5.1/ \ i586-wrs-vxworks-gcc \ -O3 -Wall -Wno-unused-but-set-variable -Werror \ @@ -44,9 +49,9 @@ mc_test_load: hijack_fei.mc.load spray_test.mc.load -I. \ -I../common \ -o $@ \ - -c $< + $< $(EXTRA_LDFLAGS_$*) -%.mc: %.c +%.mc: %.c Makefile WIND_BASE=/opt/robot/include/vxworks/5.5.1/ \ i586-wrs-vxworks-gcc \ -O3 -Wall -Wno-unused-but-set-variable -Werror \ @@ -56,7 +61,14 @@ mc_test_load: hijack_fei.mc.load spray_test.mc.load -I. \ -I../common \ -o $@ \ - -c $< + $< $(EXTRA_LDFLAGS_$*) + for h in $(HIDE_$*) ; do \ + nm $@ | sed -e "s/.*[DTB] \($$h.*\)/\1/gp;d" > $@.hide || exit 1; \ + echo "Hiding: $$h ($$(cat $@.hide | wc -l) symbols)" ; \ + i586-wrs-vxworks-objcopy --localize-symbols=$@.hide $@ $@.tmp && \ + mv $@.tmp $@ || echo "Hiding failed for $$h" || exit 1 ; \ + rm $@.hide ; \ + done %_lc.c %_lc.h: %.lc labcomm2014 --c=$*_lc.c --h=$*_lc.h $< @@ -92,7 +104,10 @@ host_commands: @echo "ncftpput -uu -pp $(ROBOT) /hd0a/$(DESTDIR) hijacknet_hpnet.mc" @echo "ncftpput -uu -pp $(ROBOT) /hd0a/$(DESTDIR) hijacknet_fei.mc" @echo "ncftpput -uu -pp $(ROBOT) /hd0a/$(DESTDIR) hijacknet.mc" + @echo @echo "ncftpput -uu -pp $(ROBOT) /hd0a/$(DESTDIR) hijacknet_test.mc" + @echo + @echo "ncftpput -uu -pp $(ROBOT) /hd0a/$(DESTDIR) hijacknet_extctrl2014.mc" .PHONY: robot_commands robot_commands: @@ -101,8 +116,11 @@ robot_commands: @echo "ld < /hd0a/$(DESTDIR)/hijacknet_hpnet.mc" @echo "ld < /hd0a/$(DESTDIR)/hijacknet_fei.mc" @echo "ld < /hd0a/$(DESTDIR)/hijacknet.mc" + @echo @echo "ld < /hd0a/$(DESTDIR)/hijacknet_test.mc" - + @echo + @echo "ld < /hd0a/$(DESTDIR)/hijacknet_extctrl2014.mc" + @echo "hijacknet_extctrl2014_connect \"fei0\",\"38:60:77:b5:0e:cf\"" .PHONY: clean clean: @@ -114,3 +132,4 @@ distclean: clean hijacknet_fei.mc: fei82557.mc.h hijacknet_private.h hijacknet.mc: hijacknet.h +hijacknet_extctrl2014.mc: extctrl2014_lc.mc diff --git a/mc/extctrl_pub.irc5_main_110310.h b/mc/extctrl_pub.irc5_main_110310.h new file mode 100644 index 0000000000000000000000000000000000000000..e73a82e75084aae1b981cec06d971234afb6658b --- /dev/null +++ b/mc/extctrl_pub.irc5_main_110310.h @@ -0,0 +1,98 @@ +#ifndef _EXTCTRL_PUB_H_ +#define _EXTCTRL_PUB_H_ + +typedef struct { + int value[4]; +} axis_configuration_t; // Up to 128 axes (32 * 4) + +typedef struct { + int manualMode; // 1 = teach mode + int controlActive; // 1 = at least torque mode active +} controller_state; + + +typedef struct extctrl_irb2ext_joint { + /* Control parameters: */ + float parKp; /* Position control proportional gain. */ + float parKv; /* Velocity control proportional gain. */ + float parKi; /* Velocity control integration gain. */ + float parTrqMin; /* Minimum torque (control output) allowed. */ + float parTrqMax; /* Dito maximum. */ + + /* Exposed (measured or computed) states: */ + double posRawAbs; /* Measured unfiltered motor angle. [abs] */ + double posRawFb; /* Measured unfiltered motor angle. [feedback] */ + double posFlt; /* Filtered motor angle. */ + float velRaw; /* Unfiltered angular velocity. */ + float velFlt; /* Dito filtered, also for motor shaft. */ + float velOut; /* Dito according to actual velocity control ref. */ + float trqRaw; /* Measured (or internal) unfiltered torque. */ + float trqRefFlt; /* filtered Torque ref */ + + double posRef; /* Nominal position reference. */ + float velRef; /* Dito for the velocity reference/feedforward. */ + float trqFfw; /* Model-based torque feedforward. */ + float trqFfwGrav; /* total dynamic model based torque. */ +} EXTCTRL_IRB2EXT_JOINT; + +typedef struct extctrl_ext2irb_joint { + /* Possibly updated control parameters: */ + float parKp; + float parKv; + float parKi; + + /* Modified references and feed forward: */ + double posRef; + float velRef; + float trqFfw; +} EXTCTRL_EXT2IRB_JOINT; + +typedef struct extctrl_communication_data { + int instruction; + float value1; + float value2; + float value3; + float value4; + float value5; + float value6; + char str1[20]; + char str2[20]; +} EXTCTRL_COMMUNICATION_DATA; + +/* To use extctrl_communication from RAPID + * RAPID SYNTAX: + * + * VAR mocgendata mydata=[1,2,3,4,5,6,'str1','str2']; + * MocGenInstr 88999, mydata + * mydata is written to extctrl_communication and NEW data is returned + */ + + +/* + * To be called by programs invoked from VxWorks prompt, + * i.e. concurrently, hence we need mutex protection. Mutex + * is allocated by extctrl_initialize. + */ + +typedef int (*extctrl_active_cb)(axis_configuration_t axis_configuration); + +typedef int (*extctrl_submit_cb)(controller_state ctrl_state , + axis_configuration_t axis_configuration, + EXTCTRL_IRB2EXT_JOINT *joint); + +typedef int (*extctrl_obtain_cb)(axis_configuration_t axis_configuration, + EXTCTRL_EXT2IRB_JOINT *joint); + +typedef int (*extctrl_communication_cb)(axis_configuration_t ac, + EXTCTRL_COMMUNICATION_DATA *data); + +#define extctrl_install_callbacks extctrl_install_callbacks_irc5_110310 + +int extctrl_install_callbacks(extctrl_active_cb submit_active, + extctrl_submit_cb submit, + extctrl_active_cb obtain_active, + extctrl_obtain_cb obtain, + extctrl_active_cb communication_active, + extctrl_communication_cb communication); + +#endif diff --git a/mc/hijacknet_extctrl2014.c b/mc/hijacknet_extctrl2014.c new file mode 100644 index 0000000000000000000000000000000000000000..d07d701936ea4e36d8acde5ad511618b2ce2b376 --- /dev/null +++ b/mc/hijacknet_extctrl2014.c @@ -0,0 +1,1318 @@ +#include <stdio.h> +#include <semLib.h> +#include <sysLib.h> +#include <string.h> +#include <taskLib.h> +#include <stdlib.h> +#include <extctrl_pub.irc5_main_110310.h> +#include "extctrl2014_lc.h" +#include <labcomm2014.h> +#include <labcomm2014_default_error_handler.h> +#include <labcomm2014_default_memory.h> +#include <labcomm2014_default_scheduler.h> +#include <labcomm2014_private.h> +#include <hijacknet.h> +#if 0 +#include <labcomm_private.h> +#include <lth/lth_net/lth_net.h> +#include <lth/lth_net/lth_net_defs.h> +#include "extctrl_irc5_main_20120112.h" +#endif + +#define EXTCTRL2015_ETH_ID (('E' << 8) + ('X')) +#define flag_MASK 0x07 +#define flag_LAST_FRAG 0x01 +#define flag_NEED_ACK 0x02 +#define flag_IS_ACK 0x04 +#define kind_MASK 0x30 +#define kind_INIT 0x10 +#define kind_DATA 0x20 + + +/* +we need an /hd0a/orca/robotdata + +ac0.value[0] ac0.value[1] ac0.value[2] ac0.value[3] id0 +... +acN.value[0] acN.value[1] acN.value[2] acN.value[3] idN +*/ + +#define MILLISECONDS_TO_TICK(ms) ((ms) * 1000 / sysClkRateGet()) +#define TIMING_BINS 400 + +#define MAX_JOINT 128 +#define MAX_ROBOT 4 +#define MAX_FORCE_SENSORS 1 +#define BUFFER_SIZE 1500 + +extern unsigned long int cpuFrequency; + +extern void moc_io_get_force_and_torque_feedback(int index, + float values[6]); + +typedef enum { + s_disconnected, + s_send_syn, + s_get_syn, + s_setup, + s_send_setup_syn, + s_connected, + s_submitting, + s_obtaining, + s_error +} state_t; + +static char *state_name[] = { + "disconnected", + "send_syn", + "get_syn", + "setup", + "send_setup_syn", + "connected", + "submitting", + "obtaining", + "error" +}; + +static struct state { + state_t state; + SEM_ID mutex; + SEM_ID sem; + SEM_ID sem_communicate; + HIJACKNET_DRIVER *driver; + unsigned char src[6]; + unsigned char dst[6]; + int channel; + int cookie; + struct encoder_writer { + struct labcomm2014_writer writer; + struct labcomm2014_writer_action_context action_context; + int need_ack; + int frag_start; + unsigned char buffer[BUFFER_SIZE]; + } writer; + struct { + struct labcomm2014_encoder *encoder; + int sequence; + } encoder; + struct { + struct labcomm2014_decoder *decoder; + int sequence; + int fragment; + int pos; + int valid; + unsigned char buffer[1500]; + } decoder; + unsigned int t_submit_active[MAX_ROBOT]; + unsigned int t_submit[MAX_ROBOT]; + unsigned int t_obtain_active[MAX_ROBOT]; + unsigned int t_obtain[MAX_ROBOT]; + unsigned int t_communicate_active[MAX_ROBOT]; + unsigned int t_communicate_submit[MAX_ROBOT]; + unsigned int t_communicate_obtain[MAX_ROBOT]; + int communicate_count[MAX_ROBOT]; + int loops[MAX_ROBOT]; + int ext2irb_updated; + int submitted; + int missed; + int total_missed; + unsigned int cpu_MHz; + unsigned int timing[TIMING_BINS][MAX_ROBOT]; + + int maxrobot; + axis_configuration_t ac[MAX_ROBOT]; + int kind[MAX_ROBOT]; + int axis_count[MAX_ROBOT]; + extctrl2014_irb2ext_joint_net irb2ext_joint[MAX_JOINT]; + extctrl2014_ext2irb_joint_net ext2irb_joint[MAX_JOINT]; + extctrl2014_irb2ext_robot_net irb2ext_robot[MAX_ROBOT]; + extctrl2014_ext2irb_robot_net ext2irb_robot[MAX_ROBOT]; + extctrl2014_extctrl_communication_data ext2rapid[MAX_ROBOT]; + extctrl2014_extctrl_communication_data rapid2ext[MAX_ROBOT]; + extctrl2014_irb2ext_net irb2ext; + extctrl2014_ext2irb_net ext2irb; + int force_sensors; + extctrl2014_force_torque_vector force[MAX_FORCE_SENSORS]; + extctrl2014_force_torque_net force_net; +} state; + +#if 0 + +#define PROTOCOL_ERROR \ + printf("PROTOCOL ERROR %s:%d\n", __FUNCTION__, __LINE__); \ + state->state = s_error; + +static inline unsigned int rdtsc() +{ + // Since we have at least one system with broken rdtsc (high and low word + // might be updated during read), use just low word + + unsigned int result; + __asm__ __volatile__("rdtsc" : "=a" (result) : /* no inputs */ : "%edx"); + return result; +} + +static int get_robot_index(axis_configuration_t ac) +{ + int i; + + for (i = 0 ; i < state.maxrobot ; i++) { + if (state.ac[i].value[0] != ac.value[0]) { continue; }; + if (state.ac[i].value[1] != ac.value[1]) { continue; }; + if (state.ac[i].value[2] != ac.value[2]) { continue; }; + if (state.ac[i].value[3] != ac.value[3]) { continue; }; + return i; + } + return -1; +} + +static int writer(labcomm_writer_t *w, labcomm_writer_action_t action) +{ + int result = 0; + struct state *state; + + //printf("%s: %d\n", __FUNCTION__, action); + state = w->context; + switch ((int)action) { /* Cast needed to avoid compile warning + at LABCOMM_USER_ACTION below */ + case labcomm_writer_alloc: { + w->data = state->encoder.buffer; + w->data_size = sizeof(state->encoder.buffer); + w->count = sizeof(state->encoder.buffer); + w->pos = 22; + result = sizeof(state->encoder.buffer); + } break; + case labcomm_writer_free: { + // Ignored + } break; + case labcomm_writer_start: { + if (state->state == s_setup) { + // Reserve space for header only during setup, when in data + // exchange mode, position reset is done after each flush + w->pos = 22; + } + } break; + case labcomm_writer_continue: { + PROTOCOL_ERROR; + } break; + case labcomm_writer_end: { + if (state->state == s_setup) { + int sequence = state->encoder.sequence; + + while (sequence == state->encoder.sequence) { + int pos; + + // Write header into packet + memcpy(&w->data[0], state->dst, 6); + memcpy(&w->data[6], state->src, 6); + pos = w->pos; + w->pos = 12; + labcomm_write_short(w, LTH_NET_ID); + labcomm_write_int(w, sequence); + labcomm_write_short(w, 0); + labcomm_write_short(w, LTH_NET_SETUP_PACKET | (pos - 22)); + w->pos = pos; + lth_net_send(state->unit, w->data, w->pos); + semTake(state->sem, MILLISECONDS_TO_TICK(1000)); + } + w->pos = 22; + } + } break; + case labcomm_writer_available: { + } break; + case LABCOMM_USER_ACTION(1): { + int pos; + + // Write header into packet + memcpy(&w->data[0], state->dst, 6); + memcpy(&w->data[6], state->src, 6); + pos = w->pos; + w->pos = 12; + labcomm_write_short(w, LTH_NET_ID); + labcomm_write_int(w, state->encoder.sequence); + labcomm_write_short(w, 0); + labcomm_write_short(w, pos - 22); + w->pos = pos; + lth_net_send(state->unit, w->data, w->pos); + state->encoder.sequence++; + w->pos = 22; + } break; + default: { + printf("Unhandled action: %d\n", action); + } break; + } + + return result; +} + +static int reader(labcomm_reader_t *r, labcomm_reader_action_t action) +{ + int result = 0; + struct state *state; + //printf("%s: %d (%d)\n", __FUNCTION__, action, r->pos); + state = r->context; + switch (action) { + case labcomm_reader_alloc: { + r->data = state->decoder.buffer; + r->data_size = sizeof(state->decoder.buffer); + r->count = 0; + result = sizeof(state->decoder.buffer); + } break; + case labcomm_reader_free: { + // Ignored + } break; + case labcomm_reader_start: { + r->pos = 0; + if (state->decoder.valid) { + r->count = state->decoder.pos; + } else { + r->count = 0; + } + result = r->count; + } break; + case labcomm_reader_continue: { + PROTOCOL_ERROR; + } break; + case labcomm_reader_end: { + state->decoder.valid = 0; + } break; + } + return result; +} + +static void send_flags(int unit, int sequence, int flags) +{ + unsigned char buf[22]; + struct labcomm_writer w; + + //printf("send_flags sequence=%x flags=%x\n", sequence, flags); + labcomm_buffer_writer_setup(&w, buf, sizeof(buf)); + memcpy(&w.data[0], state.dst, 6); + memcpy(&w.data[6], state.src, 6); + w.pos += 12; + labcomm_write_short(&w, LTH_NET_ID); + labcomm_write_int(&w, sequence); + labcomm_write_short(&w, 0); + labcomm_write_short(&w, flags); + lth_net_send(0, w.data, w.pos); +} + +void ext2irb_net_handler(extctrl2014_ext2irb_net *v, + void *context) +{ + struct state *state = context; + int i, j; + + // Mutex should already be taken here + state->ext2irb_updated = 1; + + if (state->ext2irb.robot.n_0 != v->robot.n_0) { + // Robot count mismatch + goto out; + } + for (i = 0 ; i < v->robot.n_0 ; i++) { + if (state->ext2irb_robot[i].joint.n_0 != v->robot.a[i].joint.n_0) { + // Joint count mismatch + goto out; + } + for (j = 0 ; j < v->robot.a[i].joint.n_0 ; j++) { + state->ext2irb.robot.a[i].joint.a[j] = v->robot.a[i].joint.a[j]; + } + if (v->robot.a[i].mocgendata.n_0) { + // No more resend of rapid data needed + state->irb2ext_robot[i].mocgendata.n_0 = 0; + // Indicate RAPID data received + state->ext2irb_robot[i].mocgendata.n_0 = 1; + state->ext2irb_robot[i].mocgendata.a[0] = v->robot.a[i].mocgendata.a[0]; +// semGive(state->sem_communicate); + } + } + + semGive(state->sem); +out: + ; +} + +static void send_to_ext() { + semTake(state.sem, 0); + if (state.force_torque_send) { + int i; + + for (i = 0 ; i < state.force_torque_send ; i++) { + moc_io_get_force_and_torque_feedback (i, state.force[i].a); + } + labcomm_encode_extctrl2014_force_torque_net( + state.encoder.encoder, &state.force_net); + } + labcomm_encode_extctrl2014_irb2ext_net(state.encoder.encoder, + &state.irb2ext); + labcomm_internal_encoder_user_action(state.encoder.encoder, + LABCOMM_USER_ACTION(1)); +} + +static int submit_active(axis_configuration_t ac) +{ + int result = 0; + int current_robot; + + current_robot = get_robot_index(ac); + if (current_robot >= 0) { + state.loops[current_robot]++; + state.t_submit_active[current_robot] = rdtsc(); + if (state.state == s_submitting || state.state == s_obtaining) { + result = 1; + } + } + return result; +} + +static int submit(controller_state ctrl_state, + axis_configuration_t ac, + EXTCTRL_IRB2EXT_JOINT *src) +{ + int result = -1; + int current_robot; + + current_robot = get_robot_index(ac); + if (current_robot >= 0) { + int i, axis_count; + extctrl2014_irb2ext_joint_abs *dst; + + state.t_submit[current_robot] = rdtsc(); + state.submitted = 1; + semTake(state.mutex, WAIT_FOREVER); + + axis_count = state.axis_count[current_robot]; + dst = state.irb2ext.robot.a[current_robot].joint.a; + state.irb2ext.robot.a[current_robot].kind = state.kind[current_robot]; + state.irb2ext.obtaining = state.state == s_obtaining; + state.irb2ext.manualMode = ctrl_state.manualMode; + state.irb2ext.controlActive = ctrl_state.controlActive; + for (i = 0 ; i < axis_count ; i++) { + // Control parameters + dst[i].parKp = src[i].parKp; + dst[i].parKv = src[i].parKv; + dst[i].parKi = src[i].parKi; + dst[i].parTrqMin = src[i].parTrqMin; + dst[i].parTrqMax = src[i].parTrqMax; + + // Exposed (measured or computed) states + dst[i].posRawAbs = src[i].posRawAbs; + dst[i].posRawFb = src[i].posRawFb; + dst[i].posFlt = src[i].posFlt; + dst[i].velRaw = src[i].velRaw; + dst[i].velFlt = src[i].velFlt; + dst[i].velOut = src[i].velOut; + dst[i].trqRaw = src[i].trqRaw; + dst[i].trqRefFlt = src[i].trqRefFlt; + + // State references and feedforward signals (sum of torques are applied) + dst[i].posRef = src[i].posRef; + dst[i].velRef = src[i].velRef; + dst[i].trqFfw = src[i].trqFfw; + dst[i].trqFfwGrav = src[i].trqFfwGrav; + } + + semGive(state.mutex); + result = 0; + } + return result; +} + +static int obtain_active(axis_configuration_t ac) +{ + int result = 0; + int current_robot; + + if (state.submitted) { + state.submitted = 0; + send_to_ext(); + } + current_robot = get_robot_index(ac); + if (current_robot >= 0) { + state.t_obtain_active[current_robot] = rdtsc(); + if (state.state == s_obtaining) { + result = 1; + } + } + return result; +} + +static int obtain(axis_configuration_t ac, + EXTCTRL_EXT2IRB_JOINT *dst) +{ + int result = -1; + int current_robot; + + current_robot = get_robot_index(ac); + if (current_robot == 0) { + if (semTake(state.sem, 2) == 0 && state.ext2irb_updated) { + state.missed = 0; + } else { + state.total_missed++; + state.missed++; + } + state.ext2irb_updated = 0; + } + + if (current_robot >= 0) { + int i, axis_count, bin; + + semTake(state.mutex, WAIT_FOREVER); + state.t_obtain[current_robot] = rdtsc(); + // 10 us bins + bin = ((state.t_obtain[current_robot] - + state.t_submit_active[current_robot]) / + (state.cpu_MHz * (10 + 1))); + if (bin > TIMING_BINS) { + bin = TIMING_BINS - 1; + } + state.timing[bin][current_robot]++; + + if (state.missed <= 2) { + extctrl2014_ext2irb_joint_offset *offset; + extctrl2014_irb2ext_joint_abs *ref; + + axis_count = state.axis_count[current_robot]; + ref = state.irb2ext.robot.a[current_robot].joint.a; + offset = state.ext2irb.robot.a[current_robot].joint.a; + for (i = 0 ; i < axis_count ; i++) { + // Possibly updated control parameters + dst[i].parKp = offset[i].parKp; + dst[i].parKv = offset[i].parKv; + dst[i].parKi = offset[i].parKi; + + // Modified references and feed forward + // YES, we are applying offsets from last seen sample to this sample + // even if we have missed a few samples... + dst[i].posRef = ref[i].posRef + offset[i].posOffset; + dst[i].velRef = ref[i].velRef + offset[i].velOffset; + dst[i].trqFfw = ref[i].trqFfw + offset[i].trqFfwOffset; + } + result = 0; + } + } + semGive(state.mutex); + + return result; +} + +static int communicate_active(axis_configuration_t ac) +{ + int result = 0; + int current_robot; + + // Never called? Ignored?? + current_robot = get_robot_index(ac); + if (current_robot >= 0) { + state.t_communicate_active[current_robot] = rdtsc(); + if (state.state == s_submitting || state.state == s_obtaining) { + result = 1; + } + } + return result; +} + +static int communicate(axis_configuration_t ac, + EXTCTRL_COMMUNICATION_DATA *data) +{ + int result = -1; + int current_robot; + + current_robot = get_robot_index(ac); + if (current_robot >= 0) { + // Temporary workaround while ABB locks networking while calling + // extctrl_communicate: + // 1. Send data where data.instruction is even + // 2. Receiva data when data.instruction is odd, + // only return result if received + // packet.instruction == data.instruction - 1 + if ((data->instruction & 1) == 0) { + // Send packet + if (state.state == s_submitting || state.state == s_obtaining) { + extctrl2014_extctrl_communication_data *dst; + + state.communicate_count[current_robot]++; + state.t_communicate_submit[current_robot] = rdtsc(); + dst = state.irb2ext.robot.a[current_robot].mocgendata.a; + state.irb2ext.robot.a[current_robot].mocgendata.n_0 = 1; +// semTake(state.sem_communicate, 0); + semTake(state.mutex, WAIT_FOREVER); + dst->instruction = data->instruction; + dst->value1 = data->value1; + dst->value2 = data->value2; + dst->value3 = data->value3; + dst->value4 = data->value4; + dst->value5 = data->value5; + dst->value6 = data->value6; + memcpy(dst->string1.a, data->str1, sizeof(data->str1)); + memcpy(dst->string2.a, data->str2, sizeof(data->str2)); + semGive(state.mutex); + result = 0; + } + } else { + // Check if there is a received packet + if (state.state == s_obtaining) { + extctrl2014_extctrl_communication_data *src; + + src = state.ext2irb.robot.a[current_robot].mocgendata.a; + // semTake(state.sem_communicate, 8); + + state.t_communicate_obtain[current_robot] = rdtsc(); + semTake(state.mutex, WAIT_FOREVER); + if (src->instruction == data->instruction - 1) { + // We got valid a response to the RAPID data + data->value1 = src->value1; + data->value2 = src->value2; + data->value3 = src->value3; + data->value4 = src->value4; + data->value5 = src->value5; + data->value6 = src->value6; + memcpy( data->str1, src->string1.a, sizeof(data->str1)); + memcpy( data->str2, src->string2.a, sizeof(data->str2)); + result = 0; + } + src->instruction = 0; + semGive(state.mutex); + } + } + } + return result; +} + +static int hook(int ether_unit, void *data, int length, void *context) +{ + unsigned char *buf = data; + struct state *state = context; + lth_net_head_t head; + int dump = 1; + + semTake(state->mutex, WAIT_FOREVER); + //printf("%s: %d\n", __FUNCTION__, length); + // TODO: validate ethernet addresses and kind + if (lth_net_head_valid(&buf[12], length - 12, &head)) { + dump = 0; + if (head.length == 0) { + // Flag only packet + if (head.flags == LTH_NET_SYN_PACKET) { + if (state->state == s_send_syn || state->state == s_get_syn) { + state->decoder.sequence = head.sequence; + state->state = s_setup; + semGive(state->sem); + } + if (state->state == s_setup && + state->decoder.sequence == head.sequence) { + send_flags(state->unit, head.sequence, + LTH_NET_ACK_PACKET | LTH_NET_SYN_PACKET); + } + } else if (head.flags == (LTH_NET_ACK_PACKET | LTH_NET_SYN_PACKET) && + head.sequence == state->encoder.sequence) { + if (state->state == s_send_syn) { + state->state = s_get_syn; + semGive(state->sem); + } + } else if (head.flags == (LTH_NET_SETUP_PACKET | LTH_NET_ACK_PACKET) && + head.sequence == state->encoder.sequence) { + state->encoder.sequence++; + semGive(state->sem); + } else if (head.flags == (LTH_NET_SETUP_PACKET | LTH_NET_SYN_PACKET)) { + send_flags(state->unit, head.sequence, + LTH_NET_SETUP_PACKET | LTH_NET_ACK_PACKET | + LTH_NET_SYN_PACKET); + } else if (head.flags == (LTH_NET_SETUP_PACKET | LTH_NET_ACK_PACKET | + LTH_NET_SYN_PACKET)) { + if (state->state == s_send_setup_syn && + head.sequence == state->encoder.sequence) { + state->state = s_connected; + state->encoder.sequence++; + semGive(state->sem); + } + } else { + printf("UNHANDLED\n"); + dump = 1; + } + } else { + // Data packet + int send_ack = 0; + + if ((head.flags & LTH_NET_MORE_FRAGMENTS) == 0 && + head.sequence == state->decoder.sequence - 1) { + send_ack = 1; + } else if (((state->state == s_setup && + head.sequence == state->decoder.sequence) || + (state->state == s_connected || + state->state == s_submitting || + state->state == s_obtaining)) && + state->decoder.valid == 0) { + if (head.fragment == 0) { + state->decoder.pos = 0; + state->decoder.fragment = head.fragment; + } + if (state->decoder.fragment == head.fragment && + state->decoder.pos + length - 22 < sizeof(state->decoder.buffer)) { + memcpy(&state->decoder.buffer[state->decoder.pos], &buf[22], + length - 22); + state->decoder.pos += length - 22; + state->decoder.fragment++; + if ((head.flags & LTH_NET_MORE_FRAGMENTS) == 0) { + send_ack = 1; + state->decoder.sequence++; + state->decoder.valid = 1; + // TODO: while loop? + labcomm_decoder_decode_one(state->decoder.decoder); + } + } + } else { + printf("Unhandled data %d %d %d %d\n", + state->state == s_setup, + head.sequence == state->decoder.sequence, + state->state == s_connected, + state->decoder.valid == 0); +// dump = 1; + } + if (send_ack && (head.flags & LTH_NET_SETUP_PACKET)) { + send_flags(state->unit, head.sequence, + LTH_NET_SETUP_PACKET | LTH_NET_ACK_PACKET); + } + } + } + semGive(state->mutex); + if (dump) { + int i; + + for (i = 0 ; i < length ; i++) { + if (i > 0 && i % 16 == 0) { printf("\n"); } + printf("%02x ", buf[i]); + } + printf("\n"); + } + return 0; +} + +static int hijacknet_extctrl2014_connect() +{ + printf("Connnect\n"); + + state.encoder.sequence = rdtsc(); + labcomm_decoder_register_extctrl2014_ext2irb_net( + state.decoder.decoder, ext2irb_net_handler, &state); + while (1) { + state_t current_state; + + semTake(state.sem, MILLISECONDS_TO_TICK(1000)); + semTake(state.mutex, WAIT_FOREVER); + current_state = state.state; + semGive(state.mutex); + + printf("state: %d %s\n", current_state, state_name[current_state]); + switch (current_state) { + case s_uninstalled: { + goto out; + } break; + case s_send_syn: { + semTake(state.mutex, WAIT_FOREVER); + send_flags(state.unit, state.encoder.sequence, LTH_NET_SYN_PACKET); + semGive(state.mutex); + } break; + case s_setup: { + labcomm_encoder_register_extctrl2014_irb2ext_net( + state.encoder.encoder); + if (state.force_torque_send) { + state.force_net.n_0 = state.force_torque_send; + state.force_net.a = state.force; + labcomm_encoder_register_extctrl2014_force_torque_net( + state.encoder.encoder); + } + semTake(state.mutex, WAIT_FOREVER); + state.state = s_send_setup_syn; + semGive(state.mutex); + } break; + case s_send_setup_syn: { + semTake(state.mutex, WAIT_FOREVER); + send_flags(state.unit, state.encoder.sequence, + LTH_NET_SETUP_PACKET |LTH_NET_SYN_PACKET); + semGive(state.mutex); + } break; + case s_connected: { + printf("A\n"); + extctrl_install_callbacks(submit_active, submit, + obtain_active, obtain, + communicate_active, communicate); + printf("B\n"); + goto out; + } break; + default: { + semTake(state.mutex, WAIT_FOREVER); + printf("Unhandled State: %d\n", state.state); + semGive(state.mutex); + } break; + + } + } +out: + printf("Connected\n"); + return 0; +} + + + + +int hijacknet_extctrl2014_debug() +{ +#define PRINT_FIELD(label, field) { \ + int i; \ + printf(" %-17s", label ":"); \ + for (i = 0 ; i < state.maxrobot ; i++) { \ + printf("%10d ", state.field[i]); \ + } \ + printf("\n"); \ + } + PRINT_FIELD("kind", kind); +#undef PRINT_FIELD + printf(" obtaining: %d\n", state.irb2ext.obtaining); + printf(" manualMode: %d\n", state.irb2ext.manualMode); + printf(" controlActive: %d\n", state.irb2ext.controlActive); + { + int i, j, n; + + for (i = 0; i < MAX_JOINT ; i++) { + n = 0; + for (j = 0 ; j < state.maxrobot ; j++) { + if (state.irb2ext.robot.a[j].joint.n_0 > i) { + n++; + } + } + if (n) { + printf(" *** joint %d ***\n", i); +#define PRINT_FIELD(label, field1, field2) { \ + int j; \ + printf(" %-17s", label ":"); \ + for (j = 0 ; j < state.maxrobot ; j++) { \ + if (state.irb2ext.robot.a[j].joint.n_0 > i) { \ + printf("%10g (%10g) ", \ + state.ext2irb.robot.a[j].joint.a[i].field1, \ + state.irb2ext.robot.a[j].joint.a[i].field2); \ + } else { \ + printf("%10s %12s", "", ""); \ + } \ + } \ + printf("\n"); \ + } + PRINT_FIELD("parKp", parKp, parKp); + PRINT_FIELD("parKv", parKv, parKv); + PRINT_FIELD("parKi", parKi, parKi); + PRINT_FIELD("posOffset", posOffset, posRef); + PRINT_FIELD("velOffset", velOffset, velRef); + PRINT_FIELD("trqFfwOffset", trqFfwOffset, trqFfw); +#undef PRINT_FIELD + } + } + } + { + int i; + + for (i = 0 ; i < MAX_FORCE_SENSORS ; i++) { + float v[6]; + + moc_io_get_force_and_torque_feedback (i, v); + printf("F/T#%d %8g %8g %8g %8g %8g %8g %s\n", + i, v[0], v[1], v[2], v[3], v[4], v[5], + (i < state.force_torque_send)? "active" : "inactive"); + } + } + return 0; +} + +int hijacknet_extctrl2014_zero_counters() +{ + int i,j ; + + state.total_missed = 0; + state.missed = 0; + for (i = 0 ; i < state.maxrobot ; i++) { + state.loops[i] = 0; + for (j = 0 ; j < TIMING_BINS ; j++) { + state.timing[j][i] = 0; + } + } + return 0; +} + +int extctrl_stop_submit() +{ + int result = -1; + + if (state.state != s_submitting && state.state != s_obtaining) { + printf("%s: not in states { submitting, obtaining }\n", __FUNCTION__); + } else { + state.state = s_connected; + result = 0; + } + return result; +} + +int extctrl_start_submit() +{ + int result = -1; + + if (state.state != s_connected && state.state != s_obtaining) { + printf("%s: not in states { installed, obtaining }\n", __FUNCTION__); + } else { + state.state = s_submitting; + result = 0; + } + return result; +} + + +#endif + +int extctrl_stop_obtain() +{ + int result = -1; + +// void *x = labcomm2014_encoder_new; +// printf("%p\n", x); + + if (state.state != s_obtaining) { + printf("%s: not in states { obtaining }\n", __FUNCTION__); + } else { + state.state = s_submitting; + result = 0; + } + return result; +} + +int extctrl_start_obtain() +{ + int result = -1; + + if (state.state != s_submitting) { + printf("%s: not in states { submitting }\n", __FUNCTION__); + } else { + state.state = s_obtaining; + result = 0; + } + return result; +} + +int hijacknet_extctrl2014_status() +{ + printf("%s: \n", __FUNCTION__); + printf(" Built: %s %s\n", __DATE__, __TIME__); + printf(" State: %s (%d)\n", state_name[state.state], state.state); + printf(" Destination: %02x:%02x:%02x:%02x:%02x:%02x\n", + state.dst[0], state.dst[1], state.dst[2], + state.dst[3], state.dst[4], state.dst[5]); + printf(" Source: %02x:%02x:%02x:%02x:%02x:%02x\n", + state.src[0], state.src[1], state.src[2], + state.src[3], state.src[4], state.src[5]); +#define PRINT_FIELD(label, field) { \ + int i; \ + printf(" %-17s", label ":"); \ + for (i = 0 ; i < state.maxrobot ; i++) { \ + printf("%10d ", state.field[i]); \ + } \ + printf("\n"); \ + } + + printf(" missed: %10d %10d\n", state.total_missed, state.missed); + PRINT_FIELD("loops", loops); + PRINT_FIELD("RAPID comm", communicate_count); + PRINT_FIELD("t_submit_active", t_submit_active); + PRINT_FIELD("t_submit", t_submit); + PRINT_FIELD("t_obtain_active", t_obtain_active); + PRINT_FIELD("t_obtain", t_obtain); + PRINT_FIELD("t_comm_active", t_communicate_active); + PRINT_FIELD("t_comm_submit", t_communicate_submit); + PRINT_FIELD("t_comm_obtain", t_communicate_obtain); +#undef PRINT_FIELD + { + int i, j; + + for (i = 0 ; i < TIMING_BINS ; i++) { + int n = 0; + for (j = 0 ; j < state.maxrobot ; j++) { + n += state.timing[i][j]; + } + if (n) { + printf(" %4d - %4d: ", i*10, (i+1) * 10); + for (j = 0 ; j < state.maxrobot ; j++) { + printf("%d ", state.timing[i][j]); + } + printf("\n"); + } + } + } + return state.state; +} + +static int writer_alloc( + struct labcomm2014_writer *w, + struct labcomm2014_writer_action_context *action_context) +{ + printf("%s %s %s\n", __FUNCTION__, __DATE__, __TIME__); + w->data = state.writer.buffer; + w->data_size = BUFFER_SIZE; + w->count = BUFFER_SIZE; + w->pos = 0; + + return w->error; +} + +static int writer_free( + struct labcomm2014_writer *w, + struct labcomm2014_writer_action_context *action_context) +{ + printf("%s %s %s\n", __FUNCTION__, __DATE__, __TIME__); + w->data = NULL; + w->data_size = 0; + w->count = 0; + w->pos = 0; + + return w->error; +} + +static int writer_start( + struct labcomm2014_writer *w, + struct labcomm2014_writer_action_context *action_context, + int index, + const struct labcomm2014_signature *signature, + void *value) +{ + int cookie = state.cookie; + unsigned char flags; + + printf("%s %p %p\n", __FUNCTION__, signature, value); + w->pos = 0; + if (signature != NULL && value != NULL) { + /* DATA packet */ + state.writer.need_ack = 0; + flags = kind_DATA | flag_LAST_FRAG; + } else { + state.writer.need_ack = 1; + if (signature == NULL && value == NULL) { + /* INIT packet, labcomm not involved */ + flags = kind_INIT | flag_LAST_FRAG | flag_NEED_ACK; + cookie = 0; + } else if (signature == NULL) { + /* ID packet */ + flags = kind_DATA | flag_LAST_FRAG | flag_NEED_ACK; + } else { + /* signature packet */ + flags = kind_DATA | flag_LAST_FRAG | flag_NEED_ACK; + } + } + + // Write header into packet + memcpy(&w->data[0], state.dst, 6); + memcpy(&w->data[6], state.src, 6); + w->pos = 12; + labcomm2014_write_short(w, EXTCTRL2015_ETH_ID); + labcomm2014_write_byte(w, flags); + labcomm2014_write_packed32(w, state.channel); + labcomm2014_write_int(w, cookie); + labcomm2014_write_int(w, index); + /* reserve maximum space for frag_num and frag_length */ + state.writer.frag_start = w->pos; + w->pos += 10; + + return w->error; +} + +static int writer_end( + struct labcomm2014_writer *w, + struct labcomm2014_writer_action_context *action_context) +{ + int first, last, length, offset; + + first = state.writer.frag_start + 10; + last = w->pos; + length = last - first; + w->pos = state.writer.frag_start; + labcomm2014_write_packed32(w, 0); /* frag_num */ + labcomm2014_write_packed32(w, length); /* frag_length */ + offset = first - w->pos; + w->data[last] = 'X'; + memmove(&w->data[w->pos], &w->data[first], length); + last = last - offset; + w->data[last] = 'Y'; + printf("%s %d %d %d %d\n", __FUNCTION__, first, last, length, offset); + hijacknet_send(state.driver, w->data, last + offset); + w->pos = 0; + + return w->error; +} + +static int writer_flush( + struct labcomm2014_writer *w, + struct labcomm2014_writer_action_context *action_context) +{ + printf("%s %s %s\n", __FUNCTION__, __DATE__, __TIME__); + w->pos = 0; + + return w->error; +} + +static const struct labcomm2014_writer_action writer_action = { + .alloc = writer_alloc, + .free = writer_free, + .start = writer_start, + .end = writer_end, + .flush = writer_flush, + .ioctl = NULL +}; + +static void init_writer() +{ + state.writer.action_context.next = NULL; + state.writer.action_context.action = &writer_action; + state.writer.action_context.context = &state.writer; + state.writer.writer.action_context = &state.writer.action_context; + state.writer.writer.memory = NULL; + state.writer.writer.data = state.writer.buffer; + state.writer.writer.data_size = BUFFER_SIZE; + state.writer.writer.count = BUFFER_SIZE; + state.writer.writer.pos = 0; +} + +static int read_robot_data(char *path) +{ + int result = 0; + FILE *f; + int maxjoint; + + // Read arm configuration + f = fopen(path, "r"); + if (f == NULL) { + result = -1; + goto out; + } + state.maxrobot = 0; + maxjoint = 0; + state.irb2ext.robot.a = state.irb2ext_robot; + state.ext2irb.robot.a = state.ext2irb_robot; + while (1) { + int n, joints; + unsigned int ac[4], kind; + + n = fscanf(f, "%x %x %x %x %x", &ac[0], &ac[1], &ac[2], &ac[3], &kind); + if (n == EOF) { break; } + if (n == 5) { + if (state.maxrobot > MAX_ROBOT) { + printf("Too many robots in %s\n", path); + result = -1; + goto close; + } + state.ac[state.maxrobot].value[0] = ac[0]; + state.ac[state.maxrobot].value[1] = ac[1]; + state.ac[state.maxrobot].value[2] = ac[2]; + state.ac[state.maxrobot].value[3] = ac[3]; + state.kind[state.maxrobot] = kind; + + { + int i, j; + + joints = 0; + for (i = 0 ; i < 4 ; i++) { + for (j = 0 ; j < 32 ; j++) { + if (ac[i] & (1 << j)) { joints++; } + } + } + } + state.axis_count[state.maxrobot] = joints; + + state.irb2ext.robot.a[state.maxrobot].joint.n_0 = joints; + state.irb2ext.robot.a[state.maxrobot].joint.a = + &state.irb2ext_joint[maxjoint]; + state.irb2ext.robot.a[state.maxrobot].mocgendata.n_0 = 0; + state.irb2ext.robot.a[state.maxrobot].mocgendata.a = + &state.rapid2ext[state.maxrobot]; + + state.ext2irb.robot.a[state.maxrobot].joint.n_0 = joints; + state.ext2irb.robot.a[state.maxrobot].joint.a = + &state.ext2irb_joint[maxjoint]; + state.ext2irb.robot.a[state.maxrobot].mocgendata.n_0 = 0; + state.ext2irb.robot.a[state.maxrobot].mocgendata.a = + &state.ext2rapid[state.maxrobot]; + + maxjoint += joints; + state.maxrobot++; + } + } + state.irb2ext.robot.n_0 = state.maxrobot; + state.ext2irb.robot.n_0 = state.maxrobot; +close: + fclose(f); +out: + return result; +} + +static int atoe(unsigned char mac[6], char *str) +{ + unsigned int buf[6]; + int err; + + err = sscanf(str, "%x:%x:%x:%x:%x:%x", + &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]); + if (err == 6) { + mac[0] = buf[0]; + mac[1] = buf[1]; + mac[2] = buf[2]; + mac[3] = buf[3]; + mac[4] = buf[4]; + mac[5] = buf[5]; + } + return err != 6; +} + +static char* etoa(unsigned char mac[6]) +{ + static char result[18]; + + sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return result; +} + +int hijacknet_extctrl2014_connect(char *interface, + char *destination, + unsigned int channel, + unsigned int force_sensors) +{ + printf("%s %s %s\n", __FUNCTION__, __DATE__, __TIME__); + + /* Sanity check and save parameters */ + if (!interface) { + printf("No ethernet interface defined\n"); + state.state = s_error; + return -1; + } + if (!destination) { + printf("No ethernet destination address defined\n"); + state.state = s_error; + return -2; + } + if (atoe(state.dst, destination)) { + printf("Invalid ethernet destination address '%s'\n", destination); + state.state = s_error; + return -3; + } + state.channel = channel; + if (force_sensors > MAX_FORCE_SENSORS) { + printf("Too many force torque sensors requested [%d > %d]\n", + force_sensors, MAX_FORCE_SENSORS); + return -4; + } else { + state.force_sensors = force_sensors; + } + + + /* Sanity check data sizes */ + if (sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str1) != + sizeof(state.rapid2ext[0].string1.a)) { + printf("Size of RAPID string1 differ %d %d\n", + sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str1), + sizeof(state.rapid2ext[0].string1.a)); + state.state = s_error; + return -5; + } + if (sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str2) != + sizeof(state.rapid2ext[0].string2.a)) { + printf("Size of RAPID string2 differ %d %d\n", + sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str2), + sizeof(state.rapid2ext[0].string2.a)); + state.state = s_error; + return -6; + } + if (sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str1) != + sizeof(state.ext2rapid[0].string1.a)) { + printf("Size of RAPID string1 differ %d %d\n", + sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str1), + sizeof(state.ext2rapid[0].string1.a)); + state.state = s_error; + return -7; + } + if (sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str2) != + sizeof(state.ext2rapid[0].string2.a)) { + printf("Size of RAPID string2 differ %d %d\n", + sizeof(((EXTCTRL_COMMUNICATION_DATA*)(NULL))->str2), + sizeof(state.ext2rapid[0].string2.a)); + state.state = s_error; + return -8; + } + + /* Get robot configuration */ + if (read_robot_data("/hd0a/orca/robotdata") < 0) { + printf("Failed to read '/hd0a/orca/robotdata', format (in hex):\n"); + printf(" ac0 ac1 ac2 ac3 id\n"); + return -9; + } + + init_extctrl2014__signatures(); + state.driver = hijacknet_open(interface); + hijacknet_get_mac_address(state.driver, state.src); + + printf("%s", etoa(state.dst)); + printf("%s", etoa(state.src)); + + hijacknet_extctrl2014_status(); + init_writer(); + /* Send INIT */ + labcomm2014_writer_start(&state.writer.writer, + state.writer.writer.action_context, + 0, NULL, NULL); + labcomm2014_writer_end(&state.writer.writer, + state.writer.writer.action_context); + + /* register data types */ + state.encoder.encoder = labcomm2014_encoder_new( + &state.writer.writer, + labcomm2014_default_error_handler, + labcomm2014_default_memory, + labcomm2014_default_scheduler); + labcomm2014_encoder_register_extctrl2014_irb2ext_net( + state.encoder.encoder); + labcomm2014_encoder_register_extctrl2014_force_torque_net( + state.encoder.encoder); + labcomm2014_encoder_free(state.encoder.encoder); + hijacknet_close(state.driver); +#if 0 +#endif +// state.decoder.decoder = labcomm2014_decoder_new(reader, &state); + +// state.unit = unit; + state.cpu_MHz = cpuFrequency / 1000000; + state.mutex = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE); + state.sem = semBCreate(0, 0); + state.sem_communicate = semBCreate(0, 0); + +#if 0 + if (lth_net_add_hook(state.unit, hook, &state) != 0) { + goto out; + } + state.state = s_send_syn; + state.decoder.valid = 0; + taskSpawn("t_connect", 254, 0, 20000, hijacknet_extctrl2014_connect, + 0,0,0,0,0,0,0,0,0,0); +out: +#endif + return 0; +} + +int hijacknet_extctrl2014_disconnect() +{ + if (state.state != s_disconnected) { + extctrl_install_callbacks(NULL, NULL, + NULL, NULL, + NULL, NULL); +// lth_net_del_hook(0, hook); + state.state = s_disconnected; + semDelete(state.mutex); + semDelete(state.sem); + semDelete(state.sem_communicate); + } + return 0; +} + +