diff options
author | Xiao Pan <xyz@flylightning.xyz> | 2025-04-28 20:25:40 -0700 |
---|---|---|
committer | Xiao Pan <xyz@flylightning.xyz> | 2025-04-28 20:25:40 -0700 |
commit | 10e19dab46407cb9cf23923d2431cef0f2a50733 (patch) | |
tree | ba315453a9fd8dcda9090bc8752866b0de377132 /remote_plot.c | |
parent | ea551f37cc03c691e7f2c002d1ca313fbca8793c (diff) |
feat: read past data from remote pi
Using linked list so I can keep adding data without worrying about
arrary max length.
I also moved old code that can only read live data to
old/remote_plot_live.c as an archive.
Diffstat (limited to 'remote_plot.c')
-rw-r--r-- | remote_plot.c | 248 |
1 files changed, 133 insertions, 115 deletions
diff --git a/remote_plot.c b/remote_plot.c index 40e11d1..64014eb 100644 --- a/remote_plot.c +++ b/remote_plot.c @@ -27,21 +27,26 @@ //#include <stdlib.h> #include <time.h> #include <unistd.h> - #include <gtk/gtk.h> +#include "list.h" +#include<stdint.h> //uint32_t + +// use double for PLFLT +#define PL_DOUBLE // max wire unsigned number is 65535 // max wire signed number maybe is -32768 to 32767 // voltage max format 6 bytes ,5.999 // temp max format 6 bytes ,-49.9 // timestamp max 10 bytes 1745742182 -// 1 timestamp, 96 voltage, 32 temp, 1 \n, 1 \0 -// 10+96*7+32*7+1+1=908 bytes -#define MAX_XFER_BUF_SIZE 778 - -#define LEN 10 -#define VOLTLEN 96 -#define TEMPLEN 32 +// 1 timestamp, 96 voltage, 32 temp, 1 \n +// 10+96*6+32*6+1=779 bytes +#define ENTRY_SIZE 779 +// 16 KiB == 16384 bytes +// 16384/ENTRY_SIZE == 21 +// 21*ENTRY_SIZE == 16359 +// https://api.libssh.org/stable/libssh_tutor_sftp.html +#define MAX_XFER_BUF_SIZE 16384/ENTRY_SIZE*ENTRY_SIZE const char *checkbutton_names[]={ "0x630_BMS_Cell_4_Voltage", @@ -177,13 +182,13 @@ const char *checkbutton_names[]={ typedef struct { ssh_session session; sftp_session sftp; - PLFLT t[LEN]; - PLFLT volt[LEN][VOLTLEN]; - PLFLT temp[LEN][TEMPLEN]; + List cans; GtkWidget *area; GtkWidget *checkbutton[VOLTLEN+TEMPLEN]; GtkWidget *volt_label; GtkWidget *temp_label; + // .csv file already read offset + uint32_t offset; }DATA; static gboolean sftp_read_sync(gpointer user_data) @@ -201,8 +206,7 @@ static gboolean sftp_read_sync(gpointer user_data) // This is to represent a loop over time // Let's try a random walk process - file = sftp_open(data->sftp, "/tmp/mycan", - access_type, 0); + file = sftp_open(data->sftp, sftp_expand_path(data->sftp,"~/.local/share/mycan.csv"), access_type, 0); if (file == NULL) { fprintf(stderr, "Can't open file for reading: %s\n", ssh_get_error(data->session)); @@ -210,18 +214,13 @@ static gboolean sftp_read_sync(gpointer user_data) return G_SOURCE_REMOVE; } - for(int i=0;i<(LEN-1);i++) - { - //data->t[i]=data->t[i+1]; - for(int j=0;j<VOLTLEN;j++) - data->volt[i][j]=data->volt[i+1][j]; - for(int j=0;j<TEMPLEN;j++) - data->temp[i][j]=data->temp[i+1][j]; - } - + if(data->offset != 0) + sftp_seek(file,data->offset); for(;;) { nbytes = sftp_read(file, buffer, sizeof(buffer)); + //printf("nbytes: %d\n",nbytes); + //printf("%d\n",MAX_XFER_BUF_SIZE); if (nbytes == 0) { break; // EOF } else if (nbytes < 0) { @@ -230,24 +229,38 @@ static gboolean sftp_read_sync(gpointer user_data) sftp_close(file); //return SSH_ERROR; return G_SOURCE_REMOVE; + } else if ((nbytes%ENTRY_SIZE) != 0) { + fprintf(stderr, "sftp read nbytes not a multiple of %d\n",ENTRY_SIZE); + sftp_close(file); + //return SSH_ERROR; + return G_SOURCE_REMOVE; } - //printf("receive: %s",buffer); - //data->buffer[LEN-1]=atof(buffer); - // would be better if check strtok return NULL or not - strtok(buffer,","); - //printf("start print receive\n"); - for(int i=0;i<VOLTLEN;i++) + // TODO: check strtok return NULL or not + //printf("before strtok\n"); + for(int i=0;i<(nbytes/ENTRY_SIZE);i++) { - data->volt[LEN-1][i]=atof(strtok(NULL,",")); - //printf("volt[%d][%d]: %g\n",LEN-1,i,data->volt[LEN-1][i]); - } - for(int i=0;i<TEMPLEN;i++) - { - data->temp[LEN-1][i]=atof(strtok(NULL,",")); - //printf("temp[%d][%d]: %g\n",LEN-1,i,data->temp[LEN-1][i]); + Item temp; + // TODO: pass endptr and check it and other things to be safe, see https://stackoverflow.com/questions/34206446 + if(i) + temp.t=strtoul(strtok(NULL,","),NULL,10); + else + temp.t=strtoul(strtok(buffer,","),NULL,10); + for(int j=0;j<VOLTLEN;j++) + temp.volt[j]=atof(strtok(NULL,",")); + for(int j=0;j<(TEMPLEN-1);j++) + temp.temp[j]=atof(strtok(NULL,",")); + temp.temp[TEMPLEN-1]=atof(strtok(NULL,"\n")); + //printf("before AddItem\n"); + if(AddItem(temp,&(data->cans))==false) + { + fprintf(stderr,"Problem allocating memory\n"); + break; + } + //printf("after AddItem\n"); } - //printf("end print receive\n"); + //printf("after strtok\n"); + data->offset+=nbytes; } rc = sftp_close(file); @@ -258,24 +271,22 @@ static gboolean sftp_read_sync(gpointer user_data) return G_SOURCE_REMOVE; } - for(int i=0;i<LEN;i++) - data->t[i]++; //printf("sftp_read_sync end\n"); { double sum,avg; - // max 29 char + 1 \0: Average temperature: 6553.5 C - char str[30]; + // max 28 char + 1 \0: Average temperature: 6553.5 C + char str[29]; sum=avg=0; for(int i=0;i<VOLTLEN;i++) - sum+=data->volt[LEN-1][i]; + sum+=data->cans.end->item.volt[i]; avg=sum/VOLTLEN; sprintf(str,"Average voltage: %.3f V",avg); gtk_label_set_text(GTK_LABEL(data->volt_label),str); sum=0; for(int i=0;i<TEMPLEN;i++) - sum+=data->temp[LEN-1][i]; + sum+=data->cans.end->item.temp[i]; avg=sum/TEMPLEN; sprintf(str,"Average temperature: %.1f C",avg); gtk_label_set_text(GTK_LABEL(data->temp_label),str); @@ -386,100 +397,102 @@ static void draw_function (GtkDrawingArea *area, int height, gpointer user_data) { - //printf("draw begin\n"); DATA *data=user_data; - PLFLT xmin = data->t[0], xmax = data->t[LEN-1]; - PLFLT ymin, ymax; - int plot_counts=0; - gboolean active_checkbutton[VOLTLEN+TEMPLEN]; + if(!ListIsEmpty(&(data->cans))) + { + //printf("draw begin\n"); + PLFLT xmin=data->cans.head->item.t; + PLFLT xmax=data->cans.end->item.t; + PLFLT ymin, ymax; + int plot_counts=0; + gboolean active_checkbutton[VOLTLEN+TEMPLEN]; - for(int i=0;i<(VOLTLEN+TEMPLEN);i++) - if((active_checkbutton[i]=gtk_check_button_get_active(GTK_CHECK_BUTTON(data->checkbutton[i])))) - plot_counts++; + for(int i=0;i<(VOLTLEN+TEMPLEN);i++) + if((active_checkbutton[i]=gtk_check_button_get_active(GTK_CHECK_BUTTON(data->checkbutton[i])))) + plot_counts++; - plsdev( "extcairo" ); + plsdev( "extcairo" ); - { - char str[12]; - sprintf(str,"%dx%d", - gtk_widget_get_width(data->area), - gtk_widget_get_height(data->area)); - plsetopt("geometry", str); - } + { + char str[12]; + sprintf(str,"%dx%d", + gtk_widget_get_width(data->area), + gtk_widget_get_height(data->area)); + plsetopt("geometry", str); + } - // change to white background and black foreground, and gray color palette - // http://plplot.org/docbook-manual/plplot-html-5.15.0/color.html - // seems should be put before plinit; or need pladv(), plvpor(), plwind() for a new picture? - // more see example 16 - // http://plplot.org/examples.php?demo=16 - // /usr/share/plplot5.15.0/examples/c/x16c.c - plspal0("cmap0_black_on_white.pal"); - plspal1("cmap1_gray.pal", 1); - - // Initialize plplot - if(plot_counts>3) - plstar(3,(plot_counts+2)/3); - else - plstar(1,plot_counts); - pl_cmd( PLESC_DEVINIT, cr ); + // change to white background and black foreground, and gray color palette + // http://plplot.org/docbook-manual/plplot-html-5.15.0/color.html + // seems should be put before plinit; or need pladv(), plvpor(), plwind() for a new picture? + // more see example 16 + // http://plplot.org/examples.php?demo=16 + // /usr/share/plplot5.15.0/examples/c/x16c.c + plspal0("cmap0_black_on_white.pal"); + plspal1("cmap1_gray.pal", 1); + + // Initialize plplot + if(plot_counts>3) + plstar(3,(plot_counts+2)/3); + else + plstar(1,plot_counts); + pl_cmd( PLESC_DEVINIT, cr ); - for(int i=0;i<(VOLTLEN+TEMPLEN);i++) - { - if(active_checkbutton[i]) + for(int i=0;i<(VOLTLEN+TEMPLEN);i++) { - PLFLT y[LEN]; - - for(int j=0;j<LEN;j++) + if(active_checkbutton[i]) { if(i<VOLTLEN) { // according to .dbc file ymin=0; ymax=6.; - y[j]=data->volt[j][i]; } else { // according to .dbc file ymin=-50; ymax=100; - y[j]=data->temp[j][i-VOLTLEN]; } - } - // Create a labelled box to hold the plot. - plenv(xmin, xmax, ymin, ymax, 0, 0); - if(i<VOLTLEN) - pllab("Time (s)","Voltage (V)",checkbutton_names[i]); - else - pllab("Time (s)","Temperature (C)",checkbutton_names[i]); - // Plot the data that was prepared above. - // plstring for scatter - // #(NNN) is Hershey font code, more see example 5, 6, 21 - // #(727) is centred X symbol; other maybe useful codes: #(728) - // hershey font code see http://plplot.org/examples.php?demo=07 ? - plstring(LEN, data->t, y, "#(727)"); - // pline for line - plline(LEN, data->t, y); + // Create a labelled box to hold the plot. + plenv(xmin, xmax, ymin, ymax, 0, 0); + if(i<VOLTLEN) + pllab("Time (s)","Voltage (V)",checkbutton_names[i]); + else + pllab("Time (s)","Temperature (C)",checkbutton_names[i]); + // Plot the data that was prepared above. + // plstring for scatter + // #(NNN) is Hershey font code, more see example 5, 6, 21 + // #(727) is centred X symbol; other maybe useful codes: #(728) + // hershey font code see http://plplot.org/examples.php?demo=07 ? + { + Node * pnode = data->cans.head; + while(pnode!=NULL) + { + if(i<VOLTLEN) + plptex(pnode->item.t,pnode->item.volt[i],1.,0.,0.5,"#(727)"); + else + plptex(pnode->item.t,pnode->item.temp[i-VOLTLEN],1.,0.,0.5,"#(727)"); + pnode=pnode->next; + } + } + } } - } - // Close PLplot library - plend(); + // Close PLplot library + plend(); + } } static void print_data (GtkWidget *widget, gpointer user_data) { DATA *data=user_data; - for(int i=0;i<LEN;i++) - { - printf("%g",data->t[i]); - for(int j=0;j<VOLTLEN;j++) - printf(",%g",data->volt[i][j]); - for(int j=0;j<TEMPLEN;j++) - printf(",%g",data->temp[i][j]); - putchar('\n'); - } + printf("%u",data->cans.end->item.t); + for(int i=0;i<VOLTLEN;i++) + printf(",%g",data->cans.end->item.volt[i]); + for(int i=0;i<TEMPLEN;i++) + printf(",%g",data->cans.end->item.temp[i]); + putchar('\n'); } static void activate (GtkApplication *app, gpointer user_data) @@ -515,7 +528,7 @@ static void activate (GtkApplication *app, gpointer user_data) gtk_widget_set_vexpand(data->area,TRUE); gtk_widget_set_hexpand(data->area,TRUE); - button = gtk_button_new_with_label ("Print data"); + button = gtk_button_new_with_label ("Print last second data"); g_signal_connect (button, "clicked", G_CALLBACK (print_data), user_data); // https://gitlab.gnome.org/GNOME/gtk/-/blob/main/demos/print-editor/print-editor.c @@ -545,6 +558,7 @@ static void activate (GtkApplication *app, gpointer user_data) gtk_window_present (GTK_WINDOW (window)); + //printf("before g_timeout_add\n"); g_timeout_add(1000,sftp_read_sync,user_data); //printf("after g_timeout_add\n"); } @@ -634,14 +648,15 @@ int main (int argc, char **argv) GtkApplication *app; int status; - for(int i=0;i<LEN;i++) + InitializeList(&(data.cans)); + if(ListIsFull(&(data.cans))) { - data.t[i]=i; - for(int j=0;j<VOLTLEN;j++) - data.volt[i][j]=0; - for(int j=0;j<TEMPLEN;j++) - data.temp[i][j]=0; + fprintf(stderr,"No memory available! Bye!\n"); + exit(1); } + data.offset=0; + + //printf("after init\n"); // if I don't handle command line //app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS); @@ -652,6 +667,7 @@ int main (int argc, char **argv) g_signal_connect (app, "activate", G_CALLBACK (activate), &data); g_signal_connect (app, "command-line", G_CALLBACK (command_line), &data); + //printf("before g_app run\n"); status = g_application_run (G_APPLICATION (app), argc, argv); g_object_unref (app); @@ -662,5 +678,7 @@ int main (int argc, char **argv) // Segmentation fault (core dumped) error, why? maybe double free? so no free is ok? //sftp_free(sftp); + EmptyTheList(&(data.cans)); + return status; } |