Messwerte mit pico-c an Website senden

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • Gast

    Messwerte mit pico-c an Website senden

    Hallo zusammen,

    Mein pico-c script ruft die Messwerte über 7 x getinput() ab und sendet sie mit POST über einen Stream an ein Remote-System zur weiteren Analyse.

    Nach etwa zwei Tagen funktioniert Mein pico-c Programm nicht mehr.

    Wenn ich in die Datei /log/def.log gehe, sehe ich die Meldung:
    2018-12-25 06: 47: 56.576; recId = atoi (cValue);
    ^
    VZ1: 145: 23 out of memory (Alloc)

    Mit der atoi() Funktion hat das nichts zu tun auch ohne diese Funktion stoppt das Programm mit "out of memory".

    Ich rufe keine Funktionen auf, um zur Laufzeit Speicher anzufordern (malloc), und weiß nicht, welche Funktionen Speicher in Hintergrund anfordern (malloc).

    Wo ich STREAM* pTcpStream = stream_create (TCPDEVICE, 0, 0); anruf
    wird vor dem Ende der Funktionsblök eine stream_close (pTcpStream) ausgeführt.

    Hier ist eine Liste der verwendeten Funktionen:
    current_time = getcurrenttime()
    getyear(current_time, 1), getmonth(current_time, 1), getday(current_time, 1)
    gethour(current_time, 1), getminute(current_time, 1), getsecond(current_time, 1))

    getinput(iDpNr)
    setoutputtext(iChannel, cStatusMessage)

    atoi(), memset(), strlen(), strncpy(), strstr(), strcat(), strcmp()

    printf(), sprint()
    STREAM* pTcpStream = stream_create(TCPDEVICE, 0, 0)
    stream_write(pTcpStream, pTcpCmd, strlen(pTcpCmd))
    stream_flush(pTcpStream)
    stream_read(pTcpStream, szTmpBuffer, 256, 4000)
    stream_close(pTcpStream)

    getcpuinfo(), getheapusage(), getmaxheap()
    sleeps()

    Gruss,
    Willy
  • Gast

    #2
    poste mal dein ganzes pico-c
    irgendwo allokierst du speicher, den du nicht mehr freigibst

    Kommentar

    • Gast

      #3
      Danke für deine Antwort,

      Das Programm sendet eine Json-Zeichenfolge an den Host und erhält eine json-Zeichenfolge zurück mit dem Ergebnis (Anzahl der hinzugefügten Punkte in der Datenbank).
      Der erhaltene Json wird im Moment nur teilweise zerlegt.

      Gruss Willy

      json strings:
      Send json =
      {"Id":"104","Project":"pradaschier","Function":"Ad dValuesToVzDb","Version":"1.0","Datapoints":[{"UUID":"4c224d30-fd72-11e8-a254-2b771fddb37e","Value":"-3.5625","Round":"1"},{"UUID":"17afc7d0-0053-11e9-b6a9-8378a643c221","Value":"60.875","Round":"1"},{"UUID ":"72b6f120-0053-11e9-ad7b-1354ac797948","Value":"52.875","Round":"1"},{"UUID ":"8c3884c0-0053-11e9-8fec-01cddd4f1d71","Value":"44.5","Round":"1"},{"UUID": "ab43a8d0-0053-11e9-ab88-21ab9a8bfa7a","Value":"22.8125","Round":"1"},{"UUI D":"bed22af0-0053-11e9-a9f7-ef86d3ba3eb0","Value":"15.4375","Round":"1"},{"UUI D":"ed313550-0053-11e9-9d62-d71f7b8e9aff","Value":"29.3125","Round":"1"}]}

      Received json =
      {"Id":"104","Result":"ACK","Reply":"Values stored in db","DpInserts":"7","DpReceived":"7"}

      pico-c
      // Loxone pico c script tp send values to myhost via RPC
      // 15.11.2018 Willy Verbiest - Creation local test
      // 21.12.2018 Willy Verbiest - Live at site
      // 22.12.2018 Willy Verbiest - Test at myhost site
      // 23.12.2018 Willy Verbiest - Removed GetCSV function (out of memory?)

      #define PROGRAMTITLE "VZ1-v04.c 23.12.2018" // Contains source file name
      #define ACRONYM "VZ1"
      char *cDpDefs[] = {
      // VZ UUID = 36 chars
      "4c224d30-fd72-11e8-a254-2b771fddb37e",
      "17afc7d0-0053-11e9-b6a9-8378a643c221",
      "72b6f120-0053-11e9-ad7b-1354ac797948",
      "8c3884c0-0053-11e9-8fec-01cddd4f1d71",
      "ab43a8d0-0053-11e9-ab88-21ab9a8bfa7a",
      "bed22af0-0053-11e9-a9f7-ef86d3ba3eb0",
      "ed313550-0053-11e9-9d62-d71f7b8e9aff"
      };

      /*
      *** Common code between VZ1 and VZ2 programs ***
      */
      #define SLEEPTIMESTARTPHASE 10 // s During start we sleep only 10s for visual check in log
      #define SLEEPTIMERUNPHASE 60 // s After start we run every 1 minutes.
      #define SLEEPONSTART 5 // s Initial sleep to evaluate behind other programs
      #define DECIMALS 1 // Decimal places for remote host

      // Location of destination HOST
      #define POST "POST /rpc/index.php HTTP/1.1" // POST + path on the server
      #define POSTTAG "json="

      // projekte.myhost.ch
      #define HOST "Host: projekte.myhost.ch" // Host: + sub-domain name
      #define TCPDEVICE "/dev/tcp/myhost.ch/80" // Communication device

      // System run definitions
      #define BUFF_SIZE 1024
      #define RD_BLOCK_SIZE 256
      #define SD_BLOCK_SIZE 1024
      #define JSON_SIZE 640
      #define RD_TIMEOUT 3000 // in ms
      #define DPJSONSIZE 1184 // Maximum json size
      #define DPOUTMAX 13 // Maximum analog inputs

      // End definitions, do not change anything below this line!
      // --------------------------------------------------------

      int iDps = sizeof (cDpDefs) / 4; // Total of defined datapoints
      char progname[] = PROGRAMTITLE;
      char cStatusMessage[50] = "VZ transport";
      int maxRunInfo = 5; // Stop printf after maxRunInfo loops
      int sleepstimeStart = SLEEPTIMESTARTPHASE; // During start
      int sleepstimeRun = SLEEPTIMERUNPHASE; // After start
      char date_time[20];
      unsigned int current_time = getcurrenttime();
      int iDpsVzAccepted = 0;
      int iPackageId = 0;
      char json[JSON_SIZE];
      char pTcpCmd[SD_BLOCK_SIZE];
      char szBuffer[BUFF_SIZE];
      char szTmpBuffer[RD_BLOCK_SIZE];
      int recId = 0;
      char recResult[10];
      char recReply[20];
      int recDpInserts = 0;
      int recDpReceived = 0;


      void changeChar(char* word, char cold, char cnew) {
      // Used for change char ' with char " especialy to keep json strings readable
      int n = strlen(word);
      if (n > 0) {
      for (int i = 0; i < n; i++) {
      if (word[i] == cold) {
      word[i] = cnew;
      }
      }
      }
      }

      int findLastIndex(char* str, char x) {
      char * pStr = str; // first copy the pointer to not change the original
      int pos = 0;
      int i = 0;
      while (*pStr++) {
      i++;
      if (pStr[0] == x) {
      pos = i;
      }
      }
      return pos;
      }

      char * getJsonDps() {
      static char cDps[DPJSONSIZE];
      char cDpTemp[254];
      char cUUID[40];
      int iRound;
      char sep[2] = "\0\0";
      memset(cDps, '\0', sizeof (cDps)); // Clear cDps
      for (int iDpNr = 0; iDpNr < iDps; iDpNr++) {
      cUUID = cDpDefs[iDpNr];
      iRound = DECIMALS;
      if (cDps[0] != '\0') sprintf(sep, "%s", ","); // This is not the first datapoint
      sprintf(cDpTemp, "%s{'UUID':'%s','Value':'%f','Round':'%d'}", sep, cUUID, getinput(iDpNr), iRound);
      strcat(cDps, cDpTemp);
      }
      if (maxRunInfo) printf(" %s getJsonDps(%d): %s", ACRONYM, strlen(cDps), cDps);
      return cDps;
      }

      void makeJsonValues() {
      if (maxRunInfo) printf(" %s makeJsonValues begin", ACRONYM);
      char proc[] = "AddValuesToVzDb";
      float fVersion = 1.0;
      if (++iPackageId > 255) iPackageId = 1;
      // Build json in pTcpCmd buffer
      memset(pTcpCmd, '\0', sizeof (pTcpCmd)); // Clear array
      strcat(pTcpCmd, "{'Id':'%d',"); // package ID reference
      strcat(pTcpCmd, "'Proc':'%s',"); // Add values to Volkszaehler db
      strcat(pTcpCmd, "'Version':'%f',"); // Current version of package
      strcat(pTcpCmd, "'Datapoints':[");
      strcat(pTcpCmd, getJsonDps()); // Inserting datapoints **************************************
      strcat(pTcpCmd, "]}");
      // Complete json filling the variables in
      sprintf(json, pTcpCmd, iPackageId, proc, fVersion);
      changeChar(json, '\'', '"'); // Replace char ' with char "
      }

      /* Assign Key Value to variables */
      void processKeyValue(char * cKey, char * cValue) {
      // Copy key values to variables
      if (strcmp(cKey, "Id") == 0) {
      recId = atoi(cValue);
      } else if (strcmp(cKey, "Result") == 0) {
      sprintf(recResult, "%s", cValue);
      } else if (strcmp(cKey, "Reply") == 0) {
      sprintf(recReply, "%s", cValue);
      } else if (strcmp(cKey, "DpInserts") == 0) {
      recDpInserts = atoi(cValue);
      } else if (strcmp(cKey, "DpReceived") == 0) {
      recDpReceived = atoi(cValue);
      }
      // Clear cKey and cValue
      memset(cKey, '\0', sizeof (cKey));
      memset(cValue, '\0', sizeof (cValue));
      }

      /* Unpack the json string in Key Values */
      void processReceivedJson(char* pJson) {
      // Clear results
      recId = 0;
      memset(recResult, '\0', sizeof (recResult));
      memset(recReply, '\0', sizeof (recReply));
      recDpInserts = 0;
      recDpReceived = 0;
      // Process received json
      changeChar(pJson, '\'', '"'); // Replace char ' with char "
      int iMode = Search;
      char cKey[240] = "\0";
      char cValue[240] = "\0";
      int iKeyVal = 0;
      char cChars[2] = "\0\0";
      for (int i = 0; i < strlen(pJson); i++) {
      cChars[0] = pJson[i];
      switch (iMode) {
      case Search:
      if (pJson[i] == '"') {
      iMode = Collect;
      }
      break;
      case Collect:
      if (pJson[i] == '"') {
      iMode = Search;
      if (iKeyVal == 0) {
      iKeyVal = 1;
      } else {
      iKeyVal = 0;
      processKeyValue(cKey, cValue);
      }
      } else {
      if (iKeyVal == 0) {
      sprintf(cKey, "%s%s", cKey, cChars);
      } else {
      sprintf(cValue, "%s%s", cValue, cChars);
      }
      break;
      }
      }
      }
      }

      /* Send a json Post message to remote host */
      int sendJsonToRemoteHost() {
      iDpsVzAccepted = 0;
      char contentLen[10];
      sprintf(contentLen, "%d", strlen(json) + strlen(POSTTAG));
      memset(pTcpCmd, '\0', sizeof (pTcpCmd));
      strcat(pTcpCmd, POST); // POST + path on the server
      strcat(pTcpCmd, "\r\n");
      strcat(pTcpCmd, HOST); // Host: + sub-domain name
      strcat(pTcpCmd, "\r\n");
      strcat(pTcpCmd, "Accept: */*\r\n");
      strcat(pTcpCmd, "Content-Length: ");
      strcat(pTcpCmd, contentLen);
      strcat(pTcpCmd, "\r\n");
      strcat(pTcpCmd, "Content-Type: application/x-www-form-urlencoded\r\n\r\n");
      strcat(pTcpCmd, POSTTAG); // Name of POST tag
      strcat(pTcpCmd, json);
      if (maxRunInfo) printf(" %s sendJsonToRemoteHost pTcpCmd[%d]: \r\n%s\r\n", ACRONYM, strlen(pTcpCmd), pTcpCmd);
      // Create, write and flush stream
      STREAM* pTcpStream = stream_create(TCPDEVICE, 0, 0); // create tcp stream
      stream_write(pTcpStream, pTcpCmd, strlen(pTcpCmd)); // write to output buffer
      stream_flush(pTcpStream); // flush output buffer
      // Clear all buffers
      int nCnt = 0;
      int nBytesReceived = 0;
      memset(szTmpBuffer, '\0', sizeof (szTmpBuffer));
      memset(szBuffer, '\0', sizeof (szBuffer));
      // Receive answer
      if (maxRunInfo) printf(" %s Stream Opened for sending json request", ACRONYM);
      do {
      nCnt = stream_read(pTcpStream, szTmpBuffer, RD_BLOCK_SIZE, 4000);
      if (nCnt + nBytesReceived > BUFF_SIZE) {
      nBytesReceived = -1;
      break; // File is too large
      } else if (nCnt > 0) {
      strncpy((char*) szBuffer + nBytesReceived, szTmpBuffer, nCnt);
      nBytesReceived += nCnt;
      }
      } while (nCnt > 0);
      stream_close(pTcpStream);

      if (nBytesReceived > 0) {
      szBuffer[nBytesReceived] = '\0';
      if (maxRunInfo) printf(" %s Received RAW[%d]: '%s'", ACRONYM, strlen(szBuffer), szBuffer);
      char* pJsonBegin = strstr(szBuffer, "{");
      int iLastJsonEndChar = findLastIndex(szBuffer, '}');
      szBuffer[iLastJsonEndChar + 1] = '\0';
      if (maxRunInfo) printf(" %s Received json[%d]: '%s'", ACRONYM, strlen(pJsonBegin), pJsonBegin);
      // Process json responce
      processReceivedJson(pJsonBegin);
      // Clear buffers
      memset(szTmpBuffer, '\0', sizeof (szTmpBuffer));
      memset(szBuffer, '\0', sizeof (szBuffer));
      } else {
      if (maxRunInfo) printf(" %s Received 0 bytes", ACRONYM);
      }
      return iDpsVzAccepted;
      }

      void setOutTextMsg(int iChannel, int iRuns, char * msg) {
      current_time = getcurrenttime();
      sprintf(date_time, "%02d.%02d.%04d %02d:%02d:%02d", getday(current_time, 1),
      getmonth(current_time, 1), getyear(current_time, 1), gethour(current_time, 1),
      getminute(current_time, 1), getsecond(current_time, 1));
      sprintf(cStatusMessage, "%s (%d) %s %s", ACRONYM, iRuns, date_time, msg);
      setoutputtext(iChannel, cStatusMessage);
      }

      sleeps(SLEEPONSTART);
      int iRuns = 0;
      while (TRUE) {
      if (iDps > DPOUTMAX) {
      printf("Datapoints definitions (%d) exceeds max capacity of %d\r\n", iDps, DPOUTMAX);
      } else {
      iRuns++;
      setOutTextMsg(0, iRuns, "is running");
      if (maxRunInfo) printf(" %s - maxRunInfo: %d", progname, maxRunInfo);
      makeJsonValues();
      iDpsVzAccepted = sendJsonToRemoteHost();
      sprintf(cStatusMessage, "accepted dp %d/%d", recDpInserts, recDpReceived);
      setOutTextMsg(1, iRuns, cStatusMessage);
      }
      if (maxRunInfo > 0) {
      maxRunInfo--;
      if (maxRunInfo == 0) printf(" %s ** End of startup phase, no more messages will be send to Log **", ACRONYM);
      sprintf(cStatusMessage, "in Sleep for %ds - ", sleepstimeStart);
      setOutTextMsg(0, iRuns, cStatusMessage);
      printf(" %s CPU usage %d %%, Heap usage %d kB, Heap maximum %d kB", ACRONYM, getcpuinfo(), getheapusage(), getmaxheap());
      sleeps(sleepstimeStart); // During start we sleep only 10s for visual check in log
      } else {
      sprintf(cStatusMessage, "in Sleep for %ds", sleepstimeRun);
      setOutTextMsg(0, iRuns, cStatusMessage);
      sleeps(sleepstimeRun); // After start we run every 10 minutes.
      }
      }

      Kommentar

      • Gast

        #4
        Gehen sich die buffers aus?
        zB
        char json[JSON_SIZE]; //640
        char pTcpCmd[SD_BLOCK_SIZE] //1024

        Kommentar

        • Gast

          #5
          Hi,
          Ich habe die Größe von Json berechnet und komme auf 614 Bytes aus.
          (Der Json in meinem vorherigen Beitrag ist nicht der richtige, aber ähnlich).

          Diese Zahl kann etwas größer sein, wenn der ID-Zähler oder die gemessenen Werte länger sind.
          Der große von Json [] ist auf 640 bytes fastgelegt.
          Die Größe von pTcpCmd[] ist auf 1024 bytes festgelegt.
          Zur Laufzeit enthält pTcpCmd[] den Inhalt des POST-Headers um 143 Bytes + den Json-Wert von 614 bytes (zusammen 757), ohne einige CR/LF.
          Bei dieser Konfiguration läuft das Programm genau 1858-mal durch die Hauptschleife und gibt dann einen Speicherfehler aus.

          Ich bin wieder durch das Programm gegangen, finde aber keine verdächtigen Anweisungen die dies verursachen könnten.

          Grusse Willy

          Kommentar

          • svethi
            Lebende Foren Legende
            • 25.08.2015
            • 6292

            #6
            PicoC ist eine Katastrophe und debuggen kann man es auch nicht. Was ist mit Deinem Stream? Close schließt ihn, sagt ja aber keiner. Dass freigegeben wird. Da sind etliche String Manipulationen die im Hintergrund Speicher allozieren müssen, sagt auch keiner, dass der vom MiniServer richtig freigegeben wird. Ich habe schon Dinge in PicoC wieder rausgeschmissen, da sich der ganze Kram immer nach ein paar Tagen aufgehängt hat. Und das waren nur winzige Scripts.
            Miniserver; KNX; Vitogate; EnOcean (EnOceanPi); Loxone Air; Caldav-Kalenderanbindung; RaspberryPi und für keine Frickellösung zu schade :-)

            Kommentar

            • Jan W.
              Lox Guru
              • 30.08.2015
              • 1268

              #7
              Einen direkten Fehler anhand des Pico-C Skriptes kann ich nicht finden, allerdings vermute ich wie Svethi einen Fehler bei der Freigabe von Speicher im Pico-C Interpreter, auch wenn ich Pico-C in der Loxone Umgebung nicht ganz so pessimistisch sehe. Der Code mit lokalen Variablen, insbesondere static char sieht recht übersichtlich aus, aber hat möglicherweise ein Problem mit der Speicherverwaltung, siehe https://github.com/zsaleeba/picoc/issues/15. Im alten englischen Loxone Forum hatte ein Loxone Mitarbeiter mal Teile des Pico-C Interpreters abgelegt, die mit 2010-12-31 datiert waren. Loxone hat zwar in v9 ein bisschen was am Interpreter gemacht, so dass fehlerhafte Programme jetzt deaktiviert werden, aber ich denke nicht, dass alle Fehler, die im original Pico-C Interpreter gefunden wurden, von Loxone in den letzten Jahren behoben wurden.

              Dass die Verwendung von lokalen String mit static char die Funktion non-reentrant macht (https://stackoverflow.com/questions/...ar-string-in-c) dürfte in der Loxone Umgebung nicht relevant sein. Ich würde bei Strings/Arrays möglichst globale Variable verwenden um Speicherfehler zu vermeiden. Das Troubleshooting ist bei Speicherproblemen etwas schwierig. Im Monitor müsste man das Problem erkennen können, allerdings nicht die Ursache. Ich habe es bisher nie gebraucht, aber es gibt die Funtktion getheapusage(), mit der sich vielleicht Speicherprobleme im Code eingrenzen und bestimmten Befehlen zuordnen lassen.

              Gruß Jan
              Miniserver v14.5.12.7, 2x Ext., 2x Relay Ext., 2x Dimmer Ext., DMX Ext., 1-Wire Ext., Gira KNX Tastsensor 3 Komfort, Gira KNX Präsenzmelder, Fenster- und Türkontakte, Loxone Regen- und Windsensor, Gira Dual Q Rauchmelder vernetzt, 1x Relais-Modul
              Loxberry: SmartMeter, MS Backup, CamConnect, Weather4Lox
              Lüftung: Helios KWL EC 370W ET mit Modbus TCP - via Pico-C
              Heizung: Stiebel Eltron WPF 5 cool (Sole-Wasser WP) mit ISG, FB-Heizung mit 18 Kreisen, Erdsonde - via modbus/TCP
              Node-RED: IKEA Tradfri

              Kommentar

              • svethi
                Lebende Foren Legende
                • 25.08.2015
                • 6292

                #8
                Es kann auch einfach nur ein miserabler Garbage collector sein, man weiß es einfach nicht. Ich habe noch ein PicoC laufen, was keine Probleme macht. Dabei geht es aber nur um MiniServer interne Daten. Vielleicht ist es auch ein Problem mit der Anbindung externer Streams. Die Probleme hatte ich auch damit. Das HUE Script hingegen läuft problemlos. Es ist wirklich sehr schwer einzugrenzen.
                Miniserver; KNX; Vitogate; EnOcean (EnOceanPi); Loxone Air; Caldav-Kalenderanbindung; RaspberryPi und für keine Frickellösung zu schade :-)

                Kommentar

                • Tico
                  Lox Guru
                  • 31.08.2016
                  • 1035

                  #9
                  Als Idee, vielleicht eine Liste aller PicoC-Skripte zusammenzustellen, die ohne Absturz funktionieren? Konsistente Methoden können zeigen, was Best Practice ist, um das Speicherleck zu vermeiden.

                  Zur Information, ich benutze APC UPS Plugin für > 1 Jahr ohne Probleme. Es verwendet erfolgreich externe Streams.
                  Ich spreche kein Deutsch. Gib Google Translate die Schuld, wenn ich unverständlich bin.

                  Kommentar

                  • Gast

                    #10
                    Servus
                    Dein json_strings sample am anfang hat 747bytes
                    dein char[JSON_SIZE] reserviert 640 Bytes
                    in makeJsonValues kopierst du pTcpCmd in json

                    Kommentar

                    Lädt...