Auteur Sujet: projet centrale "LaBox" wifi DCC++ Can  (Lu 556234 fois)

Pyk35

  • Full Member
  • ***
  • Messages: 110
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #60 le: mars 16, 2020, 07:28:36 pm »
Bonjour Christophe,

Je pense que sur une donnée partagée de type int, ton code ne risque rien. Par contre, si on s'amuse à modifier un objet sur un coeur et de manipuler l'objet par l'autre coeur, je crois qua ça se passera mal tôt ou tard.
A+
A+
Cédric

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1083
  • HO avec DCC++
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #61 le: mars 16, 2020, 09:44:31 pm »
Bonjour Christophe,

Je pense que sur une donnée partagée de type int, ton code ne risque rien. Par contre, si on s'amuse à modifier un objet sur un coeur et de manipuler l'objet par l'autre coeur, je crois qua ça se passera mal tôt ou tard.
A+

Je suis bien conscient des limites et je l'ai bien précisé. Cependant, en y regardant de plus près, il me semble que ce petit exemple est réaliste dans un système de messagerie par exemple. Il n’y a en effet de modification de données que dans une seule deux tâches.

C’est je crois le cas que tu évoquais précédemment avec le traitement web dans une tâche et le reste dans une autre tâche.

Mais comme il n’y a pas de gestion de file, la valeur de la variable peut avoir été modifié dans la tâche 1 avant même que la tâche 2 n’ai pu la traiter. C'est une autre limite.

Ce petit exemple voulait également répondre à quelqu’un qui disait que ça semblait compliqué. En fait pas du tout et il y a dans ce que je montre tout pour faire fonctionner une tâche sur le core 0 et un autre sur le core 1  permettant ainsi d’accroitre les performances.

Le seul vrai problème se pose comme tu le précises bien lorsque les deux tâches travaillent sur les mêmes objets en modification.

Je trouve en tous cas ceci amusant !



Pierre59

  • Sr. Member
  • ****
  • Messages: 346
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #62 le: mars 17, 2020, 08:30:21 am »
Bonjour

Ce n'est pas évident que cela fonctionne bien. Cela dépend comment est réalisée l'instruction  intVar++; si elle n'est pas faite avec une SEULE instruction du langage machine, du genre charger dans un registre, incrémenter le registre, remettre en mémoire, cela foirera inexorablement, un changement de tache pouvant se faire entre les instructions.

J'ai beaucoup pratiqué le multitâche jadis et ai travaillé dans des équipes de recherche sur le parallélisme, il faut gérer des sections critiques.

J'ai des doutes aussi avec les Serial.print.

Cordialement

Pierre


Thierry

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 810
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #63 le: mars 17, 2020, 08:54:00 am »
L'exemple est sympa et parlant. La difficulté de la gestion multi-tâche réside essentiellement par les synchronisations qu'il faut généralement mettre en place pour éviter les conflits d’accès et/ou d'écriture sur les mêmes données. Les langages C et C++ prévoient des mécanismes type mutex, sémaphore ou lock pour justement contourner ces problèmes. Je ne sais pas ce qui est vraiment disponible pour l'ESP32, et en particulier si c'est utilisable avec un Arduino IDE...

Pierre59

  • Sr. Member
  • ****
  • Messages: 346
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #64 le: mars 17, 2020, 09:53:42 am »

Autre problème pouvant arriver, le compilateur C/C++ fait des optimisations assez poussées à la compilation, par exemple il pourrait garder la variable intVar dans un registre (s'il y en a assez sur le processeur) et ne pas la ranger en mémoire partagée, on peut palier ce problème en la rendant "volatile" (volatile int intVar).

Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1714
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #65 le: mars 17, 2020, 10:02:56 am »
Bonjour

Pierre et Thierry ont 100% raison.

J'ai un TP là dessus en OS temps-réel. C'est amusant de voir la variable partagée prendre des valeurs arbitraires.

Le plus pénible avec la concurrence d'accès à des données partagée est que, à moins de faire un programme taillé exprès pour la mettre en évidence rapidement, le bug ne se produit que rarement. Donc quasi impossible à trouver par des tests.

FreeRTOS possède des sémaphores pour mettre en œuvre des sections critiques : https://www.freertos.org/a00113.html

Cordialement

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1083
  • HO avec DCC++
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #66 le: mars 17, 2020, 10:10:35 am »
L'exemple est sympa et parlant. La difficulté de la gestion multi-tâche réside essentiellement par les synchronisations qu'il faut généralement mettre en place pour éviter les conflits d’accès et/ou d'écriture sur les mêmes données. Les langages C et C++ prévoient des mécanismes type mutex, sémaphore ou lock pour justement contourner ces problèmes. Je ne sais pas ce qui est vraiment disponible pour l'ESP32, et en particulier si c'est utilisable avec un Arduino IDE...

L’API de FreeRTOS est vraiment très complète pour gérer en particulier les queues, les semaphores et les mutexes.

Je pense que l’on doit pouvoir arriver à des choses fiables sur un ESP32 en multi-tâches. Pour l’instant, je m’intéresse au passage de données d’une tâche à une autre (qui était l’exemple pris par Cédric) dans le cas d’une messagerie avec gestion de la communication web sur une tâche et traitement des données sur une autre par exemple.

Les différents paramètres du message seraient stockés dans une structure (date, ID, contenu, …) qui elle-même serait passée en paramètre entre les tâches.

Il m’intéresserait d’appliquer cela à ma passerelle CAN/WiFi car c’est exactement la problématique.


Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3040
  • 100% Arduino et N
    • Voir le profil
Re : Re : projet centrale wifi DCC++ Can
« Réponse #67 le: mars 17, 2020, 11:07:02 am »
Il m’intéresserait d’appliquer cela à ma passerelle CAN/WiFi car c’est exactement la problématique.

Ce dont on aura besoin aussi dans la "Box". Super  :D
Cordialement,
Dominique

Pierre59

  • Sr. Member
  • ****
  • Messages: 346
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #68 le: mars 17, 2020, 04:45:24 pm »

j'ai fait tourner le programme de Christophe sur un Esp32 bicore. Quand les délais dans la boucle de chaque taches sont identiques cela se passe bien, touts les entiers successifs sont affichés.

Mais évidemment si les délais ne sont pas identique cela ne marche pas on rate des valeurs ou certaines sont multipliées.

En suivant le lien fourni par Jean-Luc j'ai monté un sémaphore d'exclusion mutuelle et changé un peu le programme.

Il y a un compteur pour chaque tache, grâce au sémaphore ces compteurs sont incrémentés de façon synchrone (toujours égaux à 1 près) et ceci quelque soient les délais de chaque tache. Les deux entier qui s'affichent sont toujours égaux et il n'en manque pas, il n'y a pas de duplication non plus.

Cela ressemble un peu à un envoi de message d'une tache à l'autre avec accusé de réception.

Le programme :

TaskHandle_t Task0;
TaskHandle_t Task1;

SemaphoreHandle_t xSemaphore = NULL; // semaphore d'exclusion mutuelle

int intVar0 = 0;
int intVar1 = 0;

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.printf("\n");

  xSemaphore = xSemaphoreCreateMutex(); // creation du semaphore

  //create a task that will be executed in the Task0code() function, with priority 1 and executed on core 0
  xTaskCreatePinnedToCore(
    Task0code,   /* Task function. */
    "Task0",     /* name of task. */
    10000,       /* Stack size of task */
    NULL,        /* parameter of the task */
    1,           /* priority of the task */
    &Task0,      /* Task handle to keep track of created task */
    0);          /* pin task to core 0 */
  delay(500);

  //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
    Task1code,   /* Task function. */
    "Task1",     /* name of task. */
    10000,       /* Stack size of task */
    NULL,        /* parameter of the task */
    1,           /* priority of the task */
    &Task1,      /* Task handle to keep track of created task */
    1);          /* pin task to core 1 */
  delay(500);
}

//Task0code
void Task0code( void * pvParameters ) {
  Serial.printf("Task0 running on core : %d\n", xPortGetCoreID());

  for (;;) {
   if( xSemaphore != NULL )
    {
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) // essai d'obtention du semaphore
        {
            if (intVar0==intVar1) intVar0++;
           
            xSemaphoreGive( xSemaphore ); // liberation du semaphore
        }
        else
        {
            Serial.printf("Task0 on core : %d pas obtenu semaphore\n", xPortGetCoreID());
        }
    }   
    delay(10); // delay(1000); // plus vite ou moins vite
  }
}

//Task1code
void Task1code( void * pvParameters ) {
  Serial.printf("Task1 running on core : %d\n", xPortGetCoreID());

  for (;;) {
   if( xSemaphore != NULL )
    {
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) // essai d'obtention du semaphore
        {
            if (intVar0==intVar1+1) { intVar1++; Serial.printf (" %d %d\n", intVar0,intVar1); }
           
            xSemaphoreGive( xSemaphore );  // liberation du semaphore
        }
        else
        {
            Serial.printf("Task1 on core : %d pas obtenu semaphore\n", xPortGetCoreID());
         }
    }   
    delay(500);
  }
}

void loop() {}

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1083
  • HO avec DCC++
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #69 le: mars 17, 2020, 05:04:21 pm »
J'ai complété le programme pour avoir une vraie file d'attente pour faire une messagerie qui échangerait les données d'une tâche à l'autre. Bien sûr, une tâche tourne sur le cœur 0 et l'autre sur le cœur 1 sinon ça n'a pas d'intérêt.

Ca m'intéresse d'avoir vos retours.

PS : Pierre, je viens juste de voir ta réponse et je ne l'ai pas analysée. Cependant, le ne suis pas certain qu'il y ait besoin de sémaphore dans ce cas car il n'y a pas de modifications de données entre les tâches, non ?

TaskHandle_t vSenderTask;
TaskHandle_t vReceiverTask;

/* Declaration d'une variable xQueue de type QueueHandle_t qui servira a stocker la file. */
QueueHandle_t xQueue;
/* Taille de la file*/
int queueSize = 1000;
/* Taille maxi de la chaine de caractere */
#define maxCaractInQueue 25


void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.printf("\n");

  xQueue = xQueueCreate( queueSize, maxCaractInQueue );
  if ( xQueue != NULL ) {

    xTaskCreatePinnedToCore(
      vSenderTaskCode,            /* Task function. */
      "vSenderTask",              /* name of task. */
      10000,                      /* Stack size of task */
      NULL,
      1,                          /* priority of the task */
      &vSenderTask,               /* Task handle to keep track of created task */
      0);                         /* pin task to core 0 */
    delay(500);

    xTaskCreatePinnedToCore(
      vReceiverTaskCode,          /* Task function. */
      "vReceiverTask",            /* name of task. */
      10000,                      /* Stack size of task */
      NULL,                       /* parameter of the task */
      2,                          /* priority of the task */
      &vReceiverTask,             /* Task handle to keep track of created task */
      1);                         /* pin task to core 1 */
    delay(500);
  }
  else
    /* The queue could not be created. */
    Serial.printf("The queue could not be created.");
}



void vSenderTaskCode( void *parameter ) {
  BaseType_t xStatus;
  /* Variable qui va contenir la valeur a envoyer*/
  char strTx[maxCaractInQueue];
  Serial.print("vSenderTask\n");
  for (int i = 0; i < queueSize; i++) {
    sprintf(strTx, "Ligne %d", i);
    xStatus = xQueueSendToBack( xQueue, (char*) strTx, 0 );
    if ( xStatus != pdPASS )
      Serial.printf( "Could not send to the queue.\r\n" );
    else
      Serial.printf("Send : %s\n", strTx);
  }
  vTaskDelete( NULL );
}

void vReceiverTaskCode( void *parameter ) {
  BaseType_t xStatus;
  /* Variable qui va contenir la valeur recue*/
  char strRx[maxCaractInQueue];
  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );
  Serial.print("vReceiverTask\n");
  for ( ;; ) {
    if ( uxQueueMessagesWaiting( xQueue ) != 0 )
      Serial.printf( "Queue should have been empty!\n" );

    xStatus = xQueueReceive( xQueue, &strRx, xTicksToWait );
    if ( xStatus == pdPASS )
      /* Data was successfully received from the queue, print out the received
        value. */
      Serial.printf( "Received : %s\n", &strRx );
    else
      Serial.printf( "Could not receive from the queue.\r\n" );
  }
}

void loop() {}
« Modifié: mars 17, 2020, 10:03:39 pm par bobyAndCo »

Pierre59

  • Sr. Member
  • ****
  • Messages: 346
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #70 le: mars 17, 2020, 05:12:21 pm »

Oui il n'y a pas de modifications entre les taches, cela marche aussi sans sémaphore, désole.

Je cherche un exemple plus convaincant.

Pierre

Pierre59

  • Sr. Member
  • ****
  • Messages: 346
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #71 le: mars 17, 2020, 06:04:55 pm »
Je pense avoir un exemple plus convaincant du fonctionnement des sémaphores.

J'ai une chaine de caractères partagée. Je la fabrique lentement après l'avoir effacée dans un tache et je l'affiche lentement dans l'autre tache.

J'ai du mettre en commentaires les affichages de non obtention de sémaphore car il polluent l'affichage.
Cela veut aussi dire que les taches n'obtiennent pas toujours le sémaphore.
Voila ce que cela donne :

Task0 running on core : 0
Task1 running on core : 1
0123456789
0123456789
0123456789
0123456789
0123456789
0123456789

Voila ce que cela donne sans sémaphore :

Task0 running on core : 0
Task1 running on core : 1
0123456789
----------
0123456789
01--------
--23456789
0123------
----456789
012345----
------6789
01234567--
--------89
0123456789
----------
0123456789
01--------
--23456789
0123------
----456789


TaskHandle_t Task0;
TaskHandle_t Task1;

SemaphoreHandle_t xSemaphore = NULL; // semaphore d'exclusion mutuelle

char chaine[11]; // chaine de 10 caracteres

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.printf("\n");

  chaine[10]=0;
 
  xSemaphore = xSemaphoreCreateMutex(); // creation du semaphore

  //create a task that will be executed in the Task0code() function, with priority 1 and executed on core 0
  xTaskCreatePinnedToCore(
    Task0code,   /* Task function. */
    "Task0",     /* name of task. */
    10000,       /* Stack size of task */
    NULL,        /* parameter of the task */
    1,           /* priority of the task */
    &Task0,      /* Task handle to keep track of created task */
    0);          /* pin task to core 0 */
  delay(500);

  //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
    Task1code,   /* Task function. */
    "Task1",     /* name of task. */
    10000,       /* Stack size of task */
    NULL,        /* parameter of the task */
    1,           /* priority of the task */
    &Task1,      /* Task handle to keep track of created task */
    1);          /* pin task to core 1 */
  delay(500);
}

//Task0code
void Task0code( void * pvParameters ) {
  Serial.printf("Task0 running on core : %d\n", xPortGetCoreID());

  for (;;) {
   if( xSemaphore != NULL )
    {
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) // essai d'obtention du semaphore
        {
            for (int i=0; i<10; i++) { chaine[i]='-'; delay(5); } // effacement de la chaine
            for (int i=0; i<10; i++) { chaine[i]='0'+i; delay(5); } // fabrication de la chaine
            xSemaphoreGive( xSemaphore ); // liberation du semaphore
        }
        else
        {
            //Serial.printf("Task0 on core : %d pas obtenu semaphore\n", xPortGetCoreID());
        } 
    }   
    delay(10); // delay(1000); // plus vite ou moins vite
  }
}

//Task1code
void Task1code( void * pvParameters ) {
  Serial.printf("Task1 running on core : %d\n", xPortGetCoreID());

  for (;;) { //int intVar;
   if( xSemaphore != NULL )
    {
        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) // essai d'obtention du semaphore
        {           
            for (int i=0; i<10; i++) { Serial.printf ("%c",chaine[i]); delay(10); }
            Serial.printf ("\n"); // affichage de la chaine
           
            xSemaphoreGive( xSemaphore );  // liberation du semaphore
        }
        else
        {
            //Serial.printf("Task1 on core : %d pas obtenu semaphore\n", xPortGetCoreID());
         }
    }   
    delay(500);
  }
}

void loop() {}

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1083
  • HO avec DCC++
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #72 le: mars 17, 2020, 07:01:11 pm »
Merci Pierre, l'exemple est en effet simple et parlant.

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1083
  • HO avec DCC++
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #73 le: mars 18, 2020, 12:14:47 am »
La même chose que ce que j'ai posté précédemment mais avec des structures pour manipuler des variables complexes.

/*
   Documentation @ http://web.ist.utl.pt/~ist11993/FRTOS-API/group___queue_management.html#xQueueReceive
*/

TaskHandle_t vSenderTask;
TaskHandle_t vReceiverTask;

/* Declaration d'une variable xQueue de type QueueHandle_t qui servira a stocker la file. */
QueueHandle_t xQueue;
/* Taille de la file*/
const uint32_t queueSize = 1000; // Prevoir une taille suffisante pour ne pas perdre de messages
/* Taille maxi de la chaine de caractere */
#define maxCaractInQueue 25

struct Msg {
  uint32_t id;
  char str[maxCaractInQueue];
} msg;


void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.printf("\n");

  xQueue = xQueueCreate( queueSize, sizeof( struct Msg * ));
  if ( xQueue != NULL ) {

    xTaskCreatePinnedToCore(
      vSenderTaskCode,            /* Task function. */
      "vSenderTask",              /* name of task. */
      10000,                      /* Stack size of task */
      NULL,
      1,                          /* priority of the task */
      &vSenderTask,               /* Task handle to keep track of created task */
      0);                         /* pin task to core 0 */
    delay(500);

    xTaskCreatePinnedToCore(
      vReceiverTaskCode,          /* Task function. */
      "vReceiverTask",            /* name of task. */
      10000,                      /* Stack size of task */
      NULL,                       /* parameter of the task */
      2,                          /* priority of the task */
      &vReceiverTask,             /* Task handle to keep track of created task */
      1);                         /* pin task to core 1 */
    delay(500);
  }
  else
    Serial.printf("The queue could not be created.");
}


void vSenderTaskCode( void *parameter ) {
  struct Msg *ptTxMsg;
  BaseType_t xStatus;
  ptTxMsg = &msg;
  Serial.print("vSenderTask\n");
  for (int i = 0; i < 10000; i++) {
    ptTxMsg->id = i;
    sprintf(ptTxMsg->str, "Ligne %d", i);
    xStatus = xQueueSendToBack( xQueue, (void*) &ptTxMsg, 0 );
    if ( xStatus == pdPASS )
      Serial.printf("Send : %d\n", ptTxMsg->id);
    else
      Serial.printf( "Could not send to the queue.\n" );
  }
  vTaskDelete( NULL );
}

void vReceiverTaskCode( void *parameter ) {
  struct Msg *ptRxMsg;
  BaseType_t xStatus;
  const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );
  Serial.print("vReceiverTask\n");
  for ( ;; ) {
    if ( uxQueueMessagesWaiting( xQueue ) != 0 )
      Serial.printf( "Queue should have been empty!\n" );
    xStatus = xQueueReceive( xQueue, &(ptRxMsg), xTicksToWait );
    if ( xStatus == pdPASS )
      Serial.printf( "Received n° %d - value : %s\n", ptRxMsg->id, ptRxMsg->str );
    else
      Serial.printf( "Could not receive from the queue.\n" );
  }
}

void loop() {}

Pierre59

  • Sr. Member
  • ****
  • Messages: 346
    • Voir le profil
Re : projet centrale wifi DCC++ Can
« Réponse #74 le: mars 18, 2020, 07:53:42 am »
Bonjour

Tu est sur que les xQueue incluent un mécanisme de protection contre les accès simultanés issus de plusieurs taches ?

Pierre