aboutsummaryrefslogtreecommitdiff
path: root/remote_plot.c
diff options
context:
space:
mode:
authorXiao Pan <xyz@flylightning.xyz>2025-04-28 20:25:40 -0700
committerXiao Pan <xyz@flylightning.xyz>2025-04-28 20:25:40 -0700
commit10e19dab46407cb9cf23923d2431cef0f2a50733 (patch)
treeba315453a9fd8dcda9090bc8752866b0de377132 /remote_plot.c
parentea551f37cc03c691e7f2c002d1ca313fbca8793c (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.c248
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;
}