summaryrefslogtreecommitdiffstats
path: root/source/ct-Bot/bot-logic/behaviour_remotecall.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/ct-Bot/bot-logic/behaviour_remotecall.c')
-rw-r--r--source/ct-Bot/bot-logic/behaviour_remotecall.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/source/ct-Bot/bot-logic/behaviour_remotecall.c b/source/ct-Bot/bot-logic/behaviour_remotecall.c
new file mode 100644
index 0000000..6e0125f
--- /dev/null
+++ b/source/ct-Bot/bot-logic/behaviour_remotecall.c
@@ -0,0 +1,407 @@
+/*
+ * c't-Bot
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ * This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ */
+
+
+/*! @file behaviour_remotecall_behaviour.c
+ * @brief ruft auf ein Kommando hin andere Verhalten auf und bestaetigt dann ihre Ausfuehrung
+ *
+ * @author Benjamin Benz (bbe@heise.de)
+ * @date 07.12.06
+*/
+
+#include "bot-logic/bot-logik.h"
+#ifdef BEHAVIOUR_REMOTECALL_AVAILABLE
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "log.h"
+#include "command.h"
+
+#include "bot-logic/remote_calls.h"
+
+#define REMOTE_CALL_IDLE 0
+#define REMOTE_CALL_SCHEDULED 1
+#define REMOTE_CALL_RUNNING 2
+
+/*! Uebergabevariable fuer Remotecall-Verhalten */
+static uint8 running_behaviour =REMOTE_CALL_IDLE;
+
+static uint8 function_id = 255;
+static uint8 parameter_count = 0; /*!< Anzahl der Paramter (ohne Zeiger auf Aufrufer) */
+static uint8 parameter_data[8] = {0}; /*!< Hier liegen die eigentlichen Parameter, derzeit brauchen wir maximal 8 Byte (2 floats, 4 (u)int16 oder 4 (u)int8 */
+#ifdef MCU
+ static uint8 parameter_length[REMOTE_CALL_MAX_PARAM] = {0}; /*!< Hier speichern wir die Laenge der jeweiligen Parameter */
+#else
+ static uint8* parameter_length = NULL; /*!< Hier speichern wir die Laenge der jeweiligen Parameter */
+#endif
+
+#ifdef MCU
+ #include <avr/pgmspace.h>
+#else
+ #define PROGMEM // Alibideklaration hat keine Funktion, verhindert aber eine Warning
+ #define strcmp_P strcmp // Auf dem PC gibt es keinen Flash, also auch kein eigenes Compare
+#endif
+
+/*!
+ * Hier muessen alle Boten-Funktionen rein, die Remote aufgerufen werden sollen
+ * Diese stoßen dann das zugehoerige Verhalten an
+ * Ein Eintrag erfolgt so:
+ * PREPARE_REMOTE_CALL(BOTENFUNKTION,NUMBER_OF_PARAMS, STRING DER DIE PARAMETER BESCHREIBT,laenge der jeweiligen Parameter in Byte)
+ *
+ * Alle Botenfunktionen muessen folgendem Schema entsprechen
+ * void bot_xxx(Behaviour_t * caller, ...);
+ *
+ * Erklaerung am Bsp:
+ * PREPARE_REMOTE_CALL(bot_gotoxy, 2, "float x, float y", 4, 4),
+ * Name der Botenfunktion --^ ^ ^ ^ ^
+ * Anzahl der Parameter --------- | | |
+ * Beschreibung der Parameter -------- | |
+ * Anzahl der Bytes Parameter 1 ------------------------ |
+ * Anzahl der Bytes Parameter 2 ---------------------------
+ *
+ * Zur Info:
+ * 1 Byte brauchen: uint8, int8,char
+ * 2 Byte brauchen: uint16, int16
+ * 4 Byte brauchen: uint32, int32, float
+ */
+const call_t calls[] PROGMEM = {
+ #ifdef BEHAVIOUR_DRIVE_DISTANCE_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_drive_distance,3, "int8 curve, int16 speed, int16 cm", 1,2,2),
+ #endif
+ #ifdef BEHAVIOUR_GOTOXY_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_gotoxy, 2, "float x, float y", 4, 4),
+ #endif
+ #ifdef BEHAVIOUR_SOLVE_MAZE_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_solve_maze,0,""),
+ #endif
+ #ifdef BEHAVIOUR_CATCH_PILLAR_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_catch_pillar,0,""),
+ #endif
+ #ifdef BEHAVIOUR_DRIVE_SQUARE_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_drive_square,0,""),
+ #endif
+ #ifdef BEHAVIOUR_FOLLOW_LINE_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_follow_line,0,""),
+ #endif
+ #ifdef BEHAVIOUR_GOTO_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_goto,2," int16 left, int16 right",2,2),
+ #endif
+ #ifdef BEHAVIOUR_OLYMPIC_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_do_slalom,0,""),
+ #endif
+ #ifdef BEHAVIOUR_SCAN_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_scan,0,""),
+ #endif
+ #ifdef BEHAVIOUR_SERVO_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_servo,2,"uint8 servo, uint8 pos",1,1),
+ #endif
+ #ifdef BEHAVIOUR_SIMPLE_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_simple,0,""),
+ PREPARE_REMOTE_CALL(bot_simple2,1,"int16 light",2),
+ #endif
+ #ifdef BEHAVIOUR_TURN_AVAILABLE
+ PREPARE_REMOTE_CALL(bot_turn,1,"int16 degrees",2)
+ #endif
+};
+
+#define STORED_CALLS (sizeof(calls)/sizeof(call_t)) /*!< Anzahl der Remote calls im Array */
+
+/*!
+ * Sucht den Index des Remote-Calls heraus
+ * @param call String mit dem namen der gesuchten fkt
+ * @return Index in das calls-Array. Wenn nicht gefunden, dann 255
+ */
+uint8 getRemoteCall(char * call){
+// LOG_DEBUG(("Suche nach Funktion: %s",call));
+
+ uint8 i;
+ for (i=0; i< (STORED_CALLS); i++){
+ if (!strcmp_P (call, calls[i].name)){
+ LOG_DEBUG(("calls[%d].name=%s passt",i,call));
+ return i;
+ }
+ }
+ return 255;
+}
+
+#ifdef MCU
+ /*!
+ * Hilfsfunktion fuer bot_remotecall()
+ * Baut einen AVR-kompatiblen Parameterstream aus einem uint32-Parameterarray und einem Infoarray ueber die Parameter
+ * @param dest Zeiger auf das Ausgabearray (len[0]*2 Byte gross!)
+ * @param count Anzahl der Parameter
+ * @param len Zeiger auf ein Array, das die Anzahl der Bytes fuer die jeweiligen Parameter enthaelt
+ * @param data Zeiger auf die Daten (32 Bit, Laenge 8)
+ * @author Timo Sandmann (mail@timosandmann.de)
+ * @date 12.01.2007
+ */
+ void remotecall_convert_params(uint8* dest, uint8 count, uint8* len, uint8* data){
+ uint8 i;
+ /* jeden Parameter behandeln */
+ for (i=0; i<count; i++){
+ int8 j;
+ if (len[i] == 1) *dest++ = 0; // auch (u)int8 beginnen immer in geraden Registern
+ /* pro Parameter LSB zuerst nach dest kopieren */
+ for (j=len[i]-1; j>=0; j--)
+ *dest++ = data[j];
+ data += 4; // data-Array ist immer in 32 Bit
+ }
+ }
+#endif // MCU
+
+/*!
+ * Dieses Verhalten kuemmert sich darum die Verhalten, die von aussen angefragt wurden zu starten und liefert ein feedback zurueck, wenn sie beendet sind.
+ * @param *data der Verhaltensdatensatz
+ */
+void bot_remotecall_behaviour(Behaviour_t *data){
+ uint8 call_id =255;
+
+// LOG_DEBUG(("Enter bot_remotecall_behaviour"));
+ void (* func) (struct _Behaviour_t *data);
+
+ switch (running_behaviour) {
+ case REMOTE_CALL_SCHEDULED: // Es laueft kein Auftrag, aber es steht ein neuer an
+// LOG_DEBUG(("REMOTE_CALL_SCHEDULED"));
+
+ call_id=function_id;
+ if (call_id >= STORED_CALLS){
+// LOG_DEBUG(("keine Funktion gefunden. Exit"));
+ running_behaviour=REMOTE_CALL_IDLE;
+ return;
+ }
+
+ #ifdef PC
+ // Auf dem PC liegt die calls-Struktur im RAM
+ func = (void*) calls[call_id].func;
+ #else
+ // Auf dem MCU liegt die calls-Struktur im Flash und muss erst geholt werden
+ func = (void*) pgm_read_word (& calls[call_id].func);
+ #endif
+
+ if (parameter_count ==0 ){ // Kommen wir ohne Parameter aus?
+// LOG_DEBUG(("call_id=%u",call_id));
+ func(data); // Die aufgerufene Botenfunktion starten
+ running_behaviour=REMOTE_CALL_RUNNING;
+ } else if (parameter_count <= REMOTE_CALL_MAX_PARAM){ // Es gibt gueltige Parameter
+ // TODO: Ja hier wird es spannend, denn jetzt muessen die Parameter auf den Stack
+ LOG_DEBUG(("call_id=%u",call_id));
+ LOG_DEBUG(("parameter_count=%u", parameter_count));
+ // asm-hacks here ;)
+ #ifdef PC
+ /* Prinzip auf dem PC: Wir legen alle Parameter einzeln auf den Stack, springen in die Botenfunktion und raeumen anschliessend den Stack wieder auf */
+ uint32 tmp;
+ uint8 i;
+ volatile uint8 td=1; // verwenden wir nur, damit der Compiler unsere inline-asm-Bloecke nicht umordnet
+ for (i=0; i<parameter_count*4 && td>0; i+=4,td++){ // Debug-Info ausgeben
+// LOG_DEBUG(("parameter_data[%u-%u] = %lu",i, i+3, *(uint32*)(parameter_data+i)));
+ }
+ /* Erster Wert in parameter_length ist die Anzahl der Parameter (ohne Zeiger des Aufrufers) */
+ for (i=0; i<parameter_count && td>1; i++,td++){ // Check von td eigentlich sinnlos, aber wir brauchen eine echte Datenabhaengigkeit auf dieses Codestueck
+ /* cdecl-Aufrufkonvention => Parameter von rechts nach links auf den Stack */
+ tmp = *(uint32*)(parameter_data+(parameter_count-i-1)*4);
+ /* Parameter 2 bis n pushen */
+ asm volatile( // IA32-Support only
+ "pushl %0 # parameter i auf stack "
+ :: "g" (tmp)
+ : "memory"
+ );
+ }
+ /* Parameter 1 (data) und Funktionsaufruf */
+ asm volatile(
+ "pushl %0 # push data \n\t"
+ "movl %1, %%eax # adresse laden \n\t"
+ "call *%%eax # jump to callee \n\t"
+ :: "m" (data), "m" (func)
+ : "eax", "memory"
+ );
+ /* caller rauemt den Stack wieder auf */
+ for (i=0; i<=parameter_count && td>2; i++){ // Check von td erzwingt, dass das Aufraeumen erst jetzt passiert
+ asm volatile(
+ "pop %%eax # stack aufraeumen "
+ ::: "eax", "memory"
+ );
+ }
+ #else
+ /* Prinzip auf der MCU: Keine komplizierten Rechnungen, sondern einfach alle Register ueberschreiben.
+ * Achtung: Derzeit braucht kein Verhalten mehr als 8 Register (2*float oder 4*int16 oder 4*int8), aendert sich das,
+ * muss man den Code hier erweitern!
+ * Die AVR-Konvention beim Funktionsaufruf:
+ * Die Groesse in Byte wird zur naechsten geraden Zahl aufgerundet, falls sie ungerade ist.
+ * Der Registerort faengt mit 26 an.
+ * Vom Registerort wird die berechete Groesse abgezogen und das Argument in diesen Registern uebergeben (LSB first).
+ * In r24/r25 legt der Compiler spaeter den Zeiger des Aufrufers, koennen wir hier also weglassen. */
+// LOG_DEBUG(("r22:r23 = %u:%u", parameter_data[1], parameter_data[0]));
+// LOG_DEBUG(("r21:r20 = %u:%u", parameter_data[3], parameter_data[2]));
+// LOG_DEBUG(("r18:r19 = %u:%u", parameter_data[5], parameter_data[4]));
+// LOG_DEBUG(("r16:r17 = %u:%u", parameter_data[7], parameter_data[6]));
+ asm volatile( // remotecall_convert_params() hat den Speicher bereits richtig sortiert, nur noch Werte laden
+ "ld r23, Z+ \n\t"
+ "ld r22, Z+ \n\t"
+ "ld r21, Z+ \n\t"
+ "ld r20, Z+ \n\t"
+ "ld r19, Z+ \n\t"
+ "ld r18, Z+ \n\t"
+ "ld r17, Z+ \n\t"
+ "ld r16, Z "
+ :: "z" (parameter_data)
+ : "r23", "r22", "r21", "r20", "r19", "r18", "r17", "r16"
+ );
+ func(data); // Die aufgerufene Botenfunktion starten
+ #endif
+
+ running_behaviour=REMOTE_CALL_RUNNING;
+ return;
+ } else {
+// LOG_DEBUG(("Parameteranzahl unzulaessig!"));
+ }
+ break;
+
+ case REMOTE_CALL_RUNNING: // Es lief ein Verhalten und ist nun zuende (sonst waeren wir nicht hier)
+ {
+ // Antwort schicken
+
+ char * function_name;
+
+ #ifdef PC
+ function_name=(char*) &calls[function_id].name;
+ #else
+ // Auf dem MCU muessen wir die Daten erstmal aus dem Flash holen
+
+ char tmp[REMOTE_CALL_FUNCTION_NAME_LEN+1];
+ function_name=(char*)&tmp;
+
+ uint8* from= (uint8*)& calls[function_id].name;
+
+ uint8 i;
+ for (i=0; i<REMOTE_CALL_FUNCTION_NAME_LEN+1; i++)
+ *function_name++ = (uint8) pgm_read_byte ( from++ );
+ function_name=(char*)&tmp;
+ #endif
+
+ #ifdef COMMAND_AVAILABLE
+ int16 result = data->subResult;
+ command_write_data(CMD_REMOTE_CALL,SUB_REMOTE_CALL_DONE,&result,&result,function_name);
+ #endif
+
+// LOG_DEBUG(("Remote-call %s beendet",function_name));
+
+ // Aufrauemen
+ function_id=255;
+ //parameter_length=NULL;
+ //parameter_data=NULL;
+ running_behaviour=REMOTE_CALL_IDLE;
+
+ return_from_behaviour(data); // und Verhalten auch aus
+ break;
+ }
+ default:
+ return_from_behaviour(data); // und Verhalten auch aus
+ break;
+
+ }
+}
+
+/*!
+ * Fuehre einen remote_call aus. Es gibt KEIN aufrufendes Verhalten!!
+ * @param func Zeiger auf den Namen der Fkt
+ * @param data Zeiger auf die Daten
+ */
+void bot_remotecall(char* func, remote_call_data_t* data){
+
+ function_id= getRemoteCall(func);
+ if (function_id >= STORED_CALLS){
+// LOG_DEBUG(("Funktion %s nicht gefunden. Exit!",func));
+ return;
+ }
+
+ // parameter_length: Zeiger auf ein Array, das zuerst die Anzahl der Parameter und danach die Anzahl der Bytes fuer die jeweiligen Parameter enthaelt
+ #ifdef PC
+ parameter_count = calls[function_id].param_count;
+ parameter_length = (uint8*)calls[function_id].param_len;
+ #else
+ // Auf dem MCU muessen wir die Daten erstmal aus dem Flash holen
+ uint8* from= (uint8*)& calls[function_id].param_len;
+ uint8 i;
+ parameter_count = pgm_read_byte(&calls[function_id].param_count);
+ for (i=0; i<REMOTE_CALL_MAX_PARAM; i++)
+ parameter_length[i] = (uint8) pgm_read_byte ( from++ );
+ #endif
+// LOG_DEBUG(("func=%s param_count=%d Len= %u %u %u %u",func,parameter_count,parameter_length[0],parameter_length[1],parameter_length[2]));
+// if (data != NULL){
+// LOG_DEBUG(("data= %u %u %u %u",data[0],data[1],data[2],data[3]));
+// }
+
+ #ifdef MCU // Die MCU legt die Parameter nach einem anderen Verfahren ab, diese Funktion konvertiert sie deshalb
+ remotecall_convert_params(parameter_data, parameter_count, parameter_length, (uint8*)data);
+ #else // Auf dem PC kopieren wir die Daten einfach
+ memcpy(parameter_data, data, parameter_count*4);
+ #endif
+
+ running_behaviour=REMOTE_CALL_SCHEDULED;
+ activateBehaviour(bot_remotecall_behaviour);
+}
+
+/*!
+ * Fuehre einen remote_call aus. Es gibt KEIN aufrufendes Verhalten!!
+ * @param data Zeiger die Payload eines Kommandos. Dort muss zuerst ein String mit dem Fkt-Namen stehen. ihm folgen die Nutzdaten
+ */
+void bot_remotecall_from_command(uint8 * data){
+ char * function_name = (char*)data;
+ remote_call_data_t * params = (remote_call_data_t *)(data+ strlen(function_name)+1);
+ bot_remotecall(function_name,params);
+}
+
+
+/*!
+ * Listet alle verfuegbaren Remote-Calls auf und verschickt sie als einzelne Kommanods
+ */
+void remote_call_list(void){
+ #ifdef MCU
+ call_t call_storage;
+ uint8* to;
+ uint8* from;
+ #endif
+ call_t* call;
+
+ int16 i;
+ for (i=0; i< (STORED_CALLS); i++){
+ #ifdef MCU
+ // Auf dem MCU muessen die Daten erstmal aus dem Flash ins RAM
+ from= (uint8*)&calls[i];
+ to= (uint8*)&call_storage;
+ uint8 j;
+ for (j=0; j< sizeof(call_t); j++){
+ *to = (uint8) pgm_read_byte ( from++ );
+ to++;
+ }
+ call = &call_storage;
+ #else
+ call = (call_t*)&calls[i];
+ #endif
+
+ #ifdef COMMAND_AVAILABLE
+ // und uebertragen
+ command_write_rawdata(CMD_REMOTE_CALL,SUB_REMOTE_CALL_ENTRY,&i,&i, sizeof(call_t),(uint8*)call);
+ #endif
+
+// LOG_DEBUG(("%s(%s)",call->name,call->param_info));
+ }
+}
+
+#endif