diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/meson.build | 2 | ||||
-rw-r--r-- | src/server/gtk.c | 216 | ||||
-rw-r--r-- | src/server/gtk.h | 1 | ||||
-rw-r--r-- | src/server/gtk.o | bin | 15864 -> 0 bytes | |||
-rw-r--r-- | src/server/gtklayout.ui | 333 | ||||
-rw-r--r-- | src/server/gtklayout_flash.ui | 371 | ||||
-rw-r--r-- | src/server/meson.build | 1 | ||||
-rw-r--r-- | src/server/util.c | 142 | ||||
-rw-r--r-- | src/server/util.h | 11 | ||||
-rw-r--r-- | src/tempmodule/configuration.c | 33 | ||||
-rw-r--r-- | src/tempmodule/configuration.h | 28 | ||||
l--------- | src/tempmodule/datatypes.h | 1 | ||||
-rw-r--r-- | src/tempmodule/indicator.c | 15 | ||||
-rw-r--r-- | src/tempmodule/indicator.h | 3 | ||||
-rw-r--r-- | src/tempmodule/main.ino | 12 | ||||
-rw-r--r-- | src/tempmodule/meson.build | 5 | ||||
-rw-r--r-- | src/tempmodule/volatile_ops.c | 38 | ||||
-rw-r--r-- | src/tempmodule/volatile_ops.h | 36 |
18 files changed, 1118 insertions, 130 deletions
diff --git a/src/meson.build b/src/meson.build index eba583d..5453018 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,4 +2,4 @@ subdir('server') subdir('tempmodule') sources = dp_sources -extra = dp_extra +extra = [dp_extra] diff --git a/src/server/gtk.c b/src/server/gtk.c index 36b36bb..f48d1d9 100644 --- a/src/server/gtk.c +++ b/src/server/gtk.c @@ -20,6 +20,7 @@ #include <gtk/gtk.h> #include <time.h> +#include <stdlib.h> #include <string.h> #include "util.h" #include "gtk.h" @@ -37,20 +38,45 @@ struct gtkui { GObject *voltage; GObject *reslabel; GObject *voltlabel; - GObject *showmorebutton; - GObject *button_on; - GObject *button_off; + GObject *show_more; + GObject *show_less; GObject *ticker; GError *error; int showmore; }; +struct gtkui_flash { + GtkBuilder *builder; + GObject *window; + GObject *ssid; + GObject *password; + GObject *htemp; + GObject *mhtemp; + GObject *mltemp; + GObject *ltemp; + GObject *flashdialog; + GObject *dialog_ok; + GObject *dialog_text; + const char *confdata[6]; + int close_with_dialog; +}; + static struct settings settings; static struct gtkui gtkui; +static struct gtkui_flash gtkui_flash; static int gtkui_update(void *data); static void gtkui_die(void *data); -static void gtkui_togglemore(void); +static int strtokel(const char * const s); +void gtkui_togglemore(void); +void gtkui_flashwindow_create(void); +void gtkui_flashwindow_destroy(void); +void gtkui_flashwindow_clean(void); +void gtkui_issue_flash_request(void); +void gtkui_readhtemp(void); +void gtkui_readmhtemp(void); +void gtkui_readmltemp(void); +void gtkui_readltemp(void); int gtkui_init(int *argc, char ***argv, int nd, struct timespec period) { @@ -66,10 +92,7 @@ int gtkui_init(int *argc, char ***argv, int nd, struct timespec period) gtkui.builder = gtk_builder_new(); - if (!gtk_builder_add_from_file(gtkui.builder, DATA_DIR "/gtklayout.ui", >kui.error)) { - ret = ERROR; - goto fail; - } + gtkui.builder = gtk_builder_new_from_file(DATA_DIR "/gtklayout.ui"); gtkui.window = gtk_builder_get_object(gtkui.builder, "window"); gtkui.tempvalue = gtk_builder_get_object(gtkui.builder, "tempvalue"); @@ -77,12 +100,13 @@ int gtkui_init(int *argc, char ***argv, int nd, struct timespec period) gtkui.voltage = gtk_builder_get_object(gtkui.builder, "voltage"); gtkui.reslabel = gtk_builder_get_object(gtkui.builder, "reslabel"); gtkui.voltlabel = gtk_builder_get_object(gtkui.builder, "voltlabel"); - gtkui.showmorebutton = gtk_builder_get_object(gtkui.builder, "showmore"); - gtkui.button_on = gtk_builder_get_object(gtkui.builder, "button_on"); - gtkui.button_off = gtk_builder_get_object(gtkui.builder, "button_off"); + gtkui.show_more = gtk_builder_get_object(gtkui.builder, "show_more"); + gtkui.show_less = gtk_builder_get_object(gtkui.builder, "show_less"); gtkui.ticker = gtk_builder_get_object(gtkui.builder, "ticker"); - g_signal_connect(gtkui.window, "destroy", G_CALLBACK(gtk_main_quit), NULL); - g_signal_connect(gtkui.showmorebutton, "toggled", G_CALLBACK(gtkui_togglemore), NULL); + gtk_builder_connect_signals(gtkui.builder, NULL); + g_object_unref(G_OBJECT(gtkui.builder)); + gtkui.builder = NULL; + gtkui.showmore = 1; /* we are setting this so we can toggle this off on the following call */ gtkui_togglemore(); @@ -137,13 +161,165 @@ static void gtkui_die(void *data) return; } -static void gtkui_togglemore(void) +static int strtokel(const char * const s) +{ + int ret; + + ret = atoi(s); + ret += 273; + ret *= 10; + + return ret; +} + +void gtkui_togglemore(void) { gtkui.showmore = !gtkui.showmore; - gtkui.showmore ? gtk_button_set_image(GTK_BUTTON(gtkui.showmorebutton), GTK_WIDGET(gtkui.button_on)) : - gtk_button_set_image(GTK_BUTTON(gtkui.showmorebutton), GTK_WIDGET(gtkui.button_off)); - gtk_widget_set_visible (GTK_WIDGET(gtkui.resistance), gtkui.showmore); - gtk_widget_set_visible (GTK_WIDGET(gtkui.voltage), gtkui.showmore); - gtk_widget_set_visible (GTK_WIDGET(gtkui.reslabel), gtkui.showmore); - gtk_widget_set_visible (GTK_WIDGET(gtkui.voltlabel), gtkui.showmore); + + /* this actually switches them around their state */ + gtk_widget_set_visible(GTK_WIDGET(gtkui.show_more), !gtkui.showmore); + gtk_widget_set_visible(GTK_WIDGET(gtkui.show_less), gtkui.showmore); + + gtk_widget_set_visible(GTK_WIDGET(gtkui.resistance), gtkui.showmore); + gtk_widget_set_visible(GTK_WIDGET(gtkui.voltage), gtkui.showmore); + gtk_widget_set_visible(GTK_WIDGET(gtkui.reslabel), gtkui.showmore); + gtk_widget_set_visible(GTK_WIDGET(gtkui.voltlabel), gtkui.showmore); +} + +void gtkui_flashwindow_create(void) +{ + if (gtkui_flash.window) { + goto fail; + } + + gtkui_flash.builder = gtk_builder_new_from_file(DATA_DIR "/gtklayout_flash.ui"); + gtkui_flash.window = gtk_builder_get_object(gtkui_flash.builder, "flashwindow"); + gtkui_flash.ssid = gtk_builder_get_object(gtkui_flash.builder, "ssid_entry"); + gtkui_flash.password = gtk_builder_get_object(gtkui_flash.builder, "pass_entry"); + gtkui_flash.htemp = gtk_builder_get_object(gtkui_flash.builder, "htemp_entry"); + gtkui_flash.mhtemp = gtk_builder_get_object(gtkui_flash.builder, "mhtemp_entry"); + gtkui_flash.mltemp = gtk_builder_get_object(gtkui_flash.builder, "mltemp_entry"); + gtkui_flash.ltemp = gtk_builder_get_object(gtkui_flash.builder, "ltemp_entry"); + gtkui_flash.flashdialog = gtk_builder_get_object(gtkui_flash.builder, "flashdialog"); + gtkui_flash.dialog_ok = gtk_builder_get_object(gtkui_flash.builder, "dialog_ok"); + gtkui_flash.dialog_text = gtk_builder_get_object(gtkui_flash.builder, "dialog_text"); + gtk_builder_connect_signals(gtkui_flash.builder, NULL); + g_object_unref(G_OBJECT(gtkui_flash.builder)); + gtkui_flash.builder = NULL; + +fail: + return; +} + +void gtkui_window_destroy(void *window) +{ + gtk_window_close(GTK_WINDOW(window)); +} + +void gtkui_flashwindow_clean(void) +{ + gtkui_flash.window = NULL; +} + +void gtkui_issue_flash_request(void) +{ + static const struct configuration def = {{{ + PADDING_START, + "ssid", /* ssid */ + "password", /* password */ + {3330, 3230, 3130, 3030}, /* calibration */ + PADDING_END + }}}; + + struct configuration conf; + size_t i; + size_t len; + const char *tmp; + + gtk_widget_set_visible(GTK_WIDGET(gtkui_flash.flashdialog), 1); + + for (i = 0; i < sizeof (gtkui_flash.confdata); ++i) { + switch (i) { + case 0: + tmp = gtkui_flash.confdata[i] ? gtkui_flash.confdata[i] : def.ssid; + len = strlen(tmp) > (sizeof(conf.ssid) - 1) ? sizeof(conf.ssid) - 1 : strlen(tmp); + memcpy(conf.ssid, tmp, len); + conf.ssid[len] = '\0'; + break; + case 1: + tmp = gtkui_flash.confdata[i] ? gtkui_flash.confdata[i] : def.password; + len = strlen(tmp) > (sizeof(conf.password) - 1) ? sizeof(conf.password) - 1 : strlen(tmp); + memcpy(conf.password, tmp, len); + conf.password[len] = '\0'; + break; + case 2: + case 3: + case 4: + case 5: + tmp = gtkui_flash.confdata[i] ? (char *) strlen(gtkui_flash.confdata[i]) : NULL; + conf.calibration[i - 2] = tmp ? strtokel(gtkui_flash.confdata[i]) : def.calibration[i -2]; + break; + default: + break; + } + + } + + write_settings(DATA_DIR "/fw.bin", &conf); +} + +void gtkui_readssid(void) +{ + gtkui_flash.confdata[0] = gtk_entry_get_text(GTK_ENTRY(gtkui_flash.ssid)); +} + +void gtkui_readpass(void) +{ + gtkui_flash.confdata[1] = gtk_entry_get_text(GTK_ENTRY(gtkui_flash.password)); +} + +void gtkui_readhtemp(void) +{ + gtkui_flash.confdata[2] = gtk_entry_get_text(GTK_ENTRY(gtkui_flash.htemp)); +} + +void gtkui_readmhtemp(void) +{ + gtkui_flash.confdata[3] = gtk_entry_get_text(GTK_ENTRY(gtkui_flash.mhtemp)); +} + +void gtkui_readmltemp(void) +{ + gtkui_flash.confdata[4] = gtk_entry_get_text(GTK_ENTRY(gtkui_flash.mltemp)); +} + +void gtkui_readltemp(void) +{ + gtkui_flash.confdata[5] = gtk_entry_get_text(GTK_ENTRY(gtkui_flash.ltemp)); +} + +void gtkui_manhandle_flashdialog(void *dialog) +{ + gtk_label_set_markup(GTK_LABEL(gtkui_flash.dialog_text), "Writing to flash... "); + if (gtkui_flash.close_with_dialog) { + gtk_widget_destroy(GTK_WIDGET(dialog)); + /* we also want to close tha parent */ + gtk_window_close(GTK_WINDOW(gtkui_flash.window)); + } else { + gtk_widget_set_visible(GTK_WIDGET(dialog), 0); + } +} + +void gtkui_dialog_done(int status) +{ + static const char * const text[2] = {"done", "FAIL"}; + static const char * const color[2] = {"#4E9A06", "#EF2929"}; + char msg[128]; + + status = status ? 1 : status; + gtkui_flash.close_with_dialog = !status; + sprintf(msg, "Writing to flash... <span foreground=\"%s\" font_weight=\"bold\">%s!</span>", color[status], text[status]); + + gtk_widget_set_sensitive(GTK_WIDGET(gtkui_flash.dialog_ok), 1); + gtk_label_set_markup(GTK_LABEL(gtkui_flash.dialog_text), msg); } diff --git a/src/server/gtk.h b/src/server/gtk.h index 0c47b0b..ad739e5 100644 --- a/src/server/gtk.h +++ b/src/server/gtk.h @@ -27,5 +27,6 @@ int gtkui_init(int *argc, char ***argv, int nd, struct timespec period); int gtkui_loop(void); +void gtkui_dialog_done(int status); #endif /* GTK_H_INCLUDED */ diff --git a/src/server/gtk.o b/src/server/gtk.o Binary files differdeleted file mode 100644 index 0c2098d..0000000 --- a/src/server/gtk.o +++ /dev/null diff --git a/src/server/gtklayout.ui b/src/server/gtklayout.ui index 69fd947..e6d3f5a 100644 --- a/src/server/gtklayout.ui +++ b/src/server/gtklayout.ui @@ -2,12 +2,17 @@ <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> - <object class="GtkImage" id="button_off"> + <object class="GtkImage" id="gtk-flash"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="stock">gtk-properties</property> + </object> + <object class="GtkImage" id="gtk-no-stock"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="stock">gtk-no</property> </object> - <object class="GtkImage" id="button_on"> + <object class="GtkImage" id="gtk-yes-stock"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="stock">gtk-yes</property> @@ -15,112 +20,260 @@ <object class="GtkWindow" id="window"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="title" translatable="yes">Coffeetemp</property> <property name="resizable">False</property> + <signal name="destroy" handler="gtk_main_quit" swapped="no"/> <child> - <object class="GtkGrid"> + <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="orientation">vertical</property> <child> - <object class="GtkLabel" id="ticker"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">0</property> - </packing> - </child> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"> Temperature: </property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="tempvalue"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="top_attach">0</property> - </packing> - </child> - <child> - <object class="GtkToggleButton" id="showmore"> - <property name="label" translatable="yes">show all</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="image">button_on</property> - <property name="always_show_image">True</property> - <property name="draw_indicator">True</property> - </object> - <packing> - <property name="left_attach">3</property> - <property name="top_attach">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="reslabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"> Resistence: </property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="resistance"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="voltage"> + <object class="GtkMenuBar" id="menu"> <property name="visible">True</property> <property name="can_focus">False</property> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkImageMenuItem"> + <property name="label">gtk-quit</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <property name="always_show_image">True</property> + <signal name="activate" handler="gtk_main_quit" swapped="no"/> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkImageMenuItem"> + <property name="label">_Reflash</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="image">gtk-flash</property> + <property name="use_stock">False</property> + <property name="always_show_image">True</property> + <signal name="activate" handler="gtkui_flashwindow_create" swapped="no"/> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_View</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkImageMenuItem" id="show_more"> + <property name="label">Show _more</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="image">gtk-yes-stock</property> + <property name="use_stock">False</property> + <property name="always_show_image">True</property> + <signal name="activate" handler="gtkui_togglemore" swapped="no"/> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="show_less"> + <property name="label">Show _less</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="image">gtk-no-stock</property> + <property name="use_stock">False</property> + <property name="always_show_image">True</property> + <signal name="activate" handler="gtkui_togglemore" swapped="no"/> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkMenuItem"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkImageMenuItem"> + <property name="label">gtk-about</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <property name="always_show_image">True</property> + </object> + </child> + </object> + </child> + </object> + </child> </object> <packing> - <property name="left_attach">2</property> - <property name="top_attach">2</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> </packing> </child> <child> - <object class="GtkLabel" id="voltlabel"> + <object class="GtkGrid"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes"> Voltage: </property> + <child> + <object class="GtkLabel" id="ticker"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_left">4</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes"> Temperature: </property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="tempvalue"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="reslabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes"> Resistence: </property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="resistance"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="voltage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="voltlabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes"> Voltage: </property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> </object> <packing> - <property name="left_attach">1</property> - <property name="top_attach">2</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> </packing> </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> </object> </child> </object> diff --git a/src/server/gtklayout_flash.ui b/src/server/gtklayout_flash.ui new file mode 100644 index 0000000..9972a4e --- /dev/null +++ b/src/server/gtklayout_flash.ui @@ -0,0 +1,371 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.20.0 --> +<interface> + <requires lib="gtk+" version="3.20"/> + <object class="GtkImage" id="cancel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="stock">gtk-cancel</property> + </object> + <object class="GtkImage" id="ok"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="stock">gtk-apply</property> + </object> + <object class="GtkWindow" id="flashwindow"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="title" translatable="yes">coffeetemp – write flash</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">True</property> + <child> + <object class="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="destroy" handler="gtkui_flashwindow_clean" swapped="no"/> + <child> + <object class="GtkEntry" id="ssid_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="max_length">32</property> + <signal name="changed" handler="gtkui_readssid" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="pass_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="max_length">32</property> + <property name="visibility">False</property> + <property name="input_purpose">password</property> + <signal name="changed" handler="gtkui_readpass" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="htemp_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="max_length">6</property> + <property name="input_purpose">digits</property> + <signal name="changed" handler="gtkui_readhtemp" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">SSID:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">Password:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">High Temp:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">Mid-high Temp:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="mhtemp_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="max_length">6</property> + <property name="input_purpose">digits</property> + <signal name="changed" handler="gtkui_readmhtemp" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">°C</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">°C</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">3</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancelbutton"> + <property name="label" translatable="yes">Cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="image">cancel</property> + <signal name="clicked" handler="gtk_window_close" object="flashwindow" swapped="no"/> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkButton" id="flashbutton"> + <property name="label" translatable="yes">Write Flash</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="image">ok</property> + <signal name="clicked" handler="gtkui_issue_flash_request" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">6</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">Low Temp:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="ltemp_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="max_length">6</property> + <property name="input_purpose">digits</property> + <signal name="changed" handler="gtkui_readltemp" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">2</property> + <property name="margin_bottom">2</property> + <property name="label" translatable="yes">°C</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">1</property> + <property name="margin_bottom">1</property> + <property name="label" translatable="yes">°C</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">5</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">2</property> + <property name="margin_right">2</property> + <property name="margin_top">1</property> + <property name="margin_bottom">1</property> + <property name="label" translatable="yes">Mid-low Temp:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="mltemp_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="max_length">6</property> + <property name="input_purpose">digits</property> + <signal name="changed" handler="gtkui_readmltemp" swapped="no"/> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + <object class="GtkDialog" id="flashdialog"> + <property name="can_focus">False</property> + <property name="title" translatable="yes">Flashing...</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="destroy_with_parent">True</property> + <property name="type_hint">dialog</property> + <property name="skip_taskbar_hint">True</property> + <property name="transient_for">flashwindow</property> + <property name="attached_to">flashwindow</property> + <signal name="close" handler="gtkui_manhandle_flashdialog" swapped="no"/> + <signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/> + <child internal-child="vbox"> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child internal-child="action_area"> + <object class="GtkButtonBox"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="dialog_ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="gtkui_manhandle_flashdialog" object="flashdialog" swapped="yes"/> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="dialog_text"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_left">3</property> + <property name="margin_right">3</property> + <property name="margin_top">3</property> + <property name="margin_bottom">3</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Writing to flash... </property> + <property name="width_chars">25</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> +</interface> diff --git a/src/server/meson.build b/src/server/meson.build index c95f9a9..2b7af61 100644 --- a/src/server/meson.build +++ b/src/server/meson.build @@ -14,6 +14,7 @@ dp_filenames = [ dp_extra_filenames = [ 'gtklayout.ui', + 'gtklayout_flash.ui', ] dp_sources = files(dp_filenames) diff --git a/src/server/util.c b/src/server/util.c index bb494c8..4aee886 100644 --- a/src/server/util.c +++ b/src/server/util.c @@ -18,12 +18,32 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <gtk/gtk.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <endian.h> #include <string.h> +#include <stdlib.h> +#include <stdio.h> #include "util.h" #include "net.h" +#include "gtk.h" + +struct child_data { + char **argv; + int fd; + char *buf; +}; static float digest_temp(const short int rawtemp); static float digest_volt(const short int rawvolt); +static int find_settings_region(char *start, char *end, char *buf, size_t n); +static int flash(char *buf, size_t address, size_t size, char* port); +void reap_dead_children(int pid, int status, void *data); /* * raw data is in 0.1°K per 1. Subtract 2730 to get Celsius. @@ -57,3 +77,125 @@ int refresh_data(int nd, struct tempmodule_state *state) return ret; } + + +int write_settings(char *imagename, const struct configuration * const conf) +{ + /* the largest flash size supported by ESP-8266 is 16MiB, so let's just use that. */ + static const size_t flash_size = 16 * 1024 * 1024; + char pattern_a[4] = PADDING_START; + char pattern_b[4] = PADDING_END; + ssize_t buf_used; + ssize_t offset; + char *buf = NULL; + int fd; + int ret = ERROR; + + fd = open(imagename, O_RDONLY); + if (fd < 0) { + goto fail; + } + buf = malloc(flash_size); + buf_used = read(fd, buf, flash_size); + close(fd); + if (buf_used < sizeof(struct configuration)) { + goto fail; + } + + offset = find_settings_region(pattern_a, pattern_b, buf, buf_used); + if (offset == ERROR) { + goto fail; + }; + + memcpy(buf + offset, conf, sizeof(struct configuration)); + /* make sure the block written is aligned on 4k boundry + * and is 4k of size. + * + * see: + * https://github.com/espressif/esptool/issues/306 + */ + offset &= ~(0xfff); + ret = flash(buf + offset, offset, 4 * 1024, NULL); + +fail: + free(buf); + return ret; +} + +static int find_settings_region(char *start, char *end, char *buf, size_t n) +{ + int ret = ERROR; + char *a; + + if (!(a = memmem(buf, n, start, 4))) { + goto fail; + } + + if ((a - buf + sizeof(struct configuration)) > n) { + goto fail; + } + + if (memcmp(a + sizeof(struct configuration) - 4, end, 4)) { + goto fail; + } + + ret = a - buf; +fail: + return ret; +} + +static int flash(char *buf, size_t address, size_t size, char* port) +{ + static const char esptool[] = "esptool.py"; + static const char command[] = "write_flash"; + struct child_data *cdata; + char tmpfn[] = "espXXXXXX"; /* temporary file name template */ + char *fifn; /* flash image file name */ + char textbuf[32]; + char *argv[7] = {0}; + int ret = ERROR; + pid_t pid; + int fd; + size_t i = 1; + + fd = mkstemp(tmpfn); + write(fd, buf, size); + fifn = malloc(0x100); + memset(fifn, '\0', 0x100); + sprintf(textbuf, "/proc/self/fd/%d", fd); + if (readlink(textbuf, fifn, 0x100) == -1) { + exit(TMPFILE); + } + + argv[0] = strdup(esptool); + if (port) { + argv[i++] = strdup("--port"); + argv[i++] = strdup(port); + } + argv[i++] = strdup(command); + sprintf(textbuf, "%#.4lx", address); + argv[i++] = strdup(textbuf); + argv[i++] = strdup(fifn); + argv[i] = NULL; + + cdata = malloc(sizeof(*cdata)); + cdata->argv = argv; + cdata->fd = fd; + cdata->buf = fifn; + + g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL, NULL, NULL, NULL); + g_child_watch_add(pid, reap_dead_children, cdata); + return ret; +} + +void reap_dead_children(int pid, int status, void *data) +{ + struct child_data *a; + + a = data; + free(a->buf); + close(a->fd); + free(data); + g_spawn_close_pid(pid); + gtkui_dialog_done(status); +} diff --git a/src/server/util.h b/src/server/util.h index 9c0f907..ef5cde2 100644 --- a/src/server/util.h +++ b/src/server/util.h @@ -21,13 +21,17 @@ #ifndef UTIL_H_INCLUDED #define UTIL_H_INCLUDED +#include "datatypes.h" + #define arrsize(a) (sizeof(a)/sizeof(*a)) enum response { ERROR = -1, - A_OK = 0, - NONEWDATA = 1, - DEAD = 2 + A_OK = 0, /* would be "OK", but clashes with some lib definitions */ + NONEWDATA, + DEAD, + NO_ESPTOOL, + TMPFILE, }; struct tempmodule_state { @@ -51,5 +55,6 @@ struct tempmodule_state { }; int refresh_data(int nd, struct tempmodule_state *state); +int write_settings(char *imagename, const struct configuration * const conf); #endif /* UTIL_H_INCLUDED */ diff --git a/src/tempmodule/configuration.c b/src/tempmodule/configuration.c new file mode 100644 index 0000000..76acde8 --- /dev/null +++ b/src/tempmodule/configuration.c @@ -0,0 +1,33 @@ +/* + * Hot Beverage Companion – configuration data + * + * Copyright (C) 2018 Gediminas Jakutis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 + * of the License. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* We want the padding data to be detectible in the binary + * so we could find out the boundries of inner workings. + * We also want to detect byte order from the padding. + */ + +#include "datatypes.h" +volatile const struct configuration configuration = {{{ + PADDING_START, + "ssid", /* ssid */ + "password", /* password */ + {3430, 3330, 3230, 3130}, /* calibration */ + PADDING_END +}}}; diff --git a/src/tempmodule/configuration.h b/src/tempmodule/configuration.h new file mode 100644 index 0000000..dfe1264 --- /dev/null +++ b/src/tempmodule/configuration.h @@ -0,0 +1,28 @@ +/* + * Hot Beverage Companion – configuration data + * + * Copyright (C) 2018 Gediminas Jakutis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 + * of the License. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef CONFIGURATION_INCLUDED +#define CONFIGURATION_INCLUDED + +#include "datatypes.h" + +extern volatile const struct configuration configuration; + +#endif /* CONFIGURATION_INCLUDED */ diff --git a/src/tempmodule/datatypes.h b/src/tempmodule/datatypes.h new file mode 120000 index 0000000..3a91016 --- /dev/null +++ b/src/tempmodule/datatypes.h @@ -0,0 +1 @@ +../../include/datatypes.h
\ No newline at end of file diff --git a/src/tempmodule/indicator.c b/src/tempmodule/indicator.c index 2968958..16df495 100644 --- a/src/tempmodule/indicator.c +++ b/src/tempmodule/indicator.c @@ -19,6 +19,8 @@ */ #include "indicator.h" +#include "configuration.h" +#include "volatile_ops.h" #define HIGH 1 #define LOW 0 @@ -38,8 +40,9 @@ static const unsigned int greenled = 4; /* D2 */ static const unsigned int blueled = 14; /* D5 */ static void indicator_set_state(unsigned int red, unsigned int green, unsigned int blue); +static void indicator_calibrate(int const * const calibration); -void indicator_init(int temp, unsigned int const * const calibration) +void indicator_init(int temp) { static const int default_calibration[] = {3430, 3330, 3230, 3130}; @@ -49,11 +52,7 @@ void indicator_init(int temp, unsigned int const * const calibration) state.current_temp = temp; - if (calibration) { - indicator_calibrate(calibration); - } else { - indicator_calibrate(default_calibration); - } + indicator_calibrate(configuration.calibration); } @@ -79,9 +78,9 @@ void indicator_update(const int temp, const unsigned int on) } } -void indicator_calibrate(int const * const calibration) +static void indicator_calibrate(int const * const calibration) { - memcpy(state.calibration, calibration, sizeof(state.calibration)); + volatile_memcpy(state.calibration, calibration, sizeof(state.calibration)); indicator_update(state.current_temp, 1); } diff --git a/src/tempmodule/indicator.h b/src/tempmodule/indicator.h index 99f4e1c..23ac87d 100644 --- a/src/tempmodule/indicator.h +++ b/src/tempmodule/indicator.h @@ -25,9 +25,8 @@ extern "C" { #endif -void indicator_init(int temp, unsigned int const * const calibration); +void indicator_init(int temp); void indicator_update(const int temp, const unsigned int on); -void indicator_calibrate(int const * const calibration); #ifdef __cplusplus } diff --git a/src/tempmodule/main.ino b/src/tempmodule/main.ino index 29ebc11..914b3ff 100644 --- a/src/tempmodule/main.ino +++ b/src/tempmodule/main.ino @@ -25,6 +25,8 @@ #include <limits.h> #include "indicator.h" #include "temperature.h" +#include "configuration.h" +#include "volatile_ops.h" #define arrsize(a) (sizeof(a)/sizeof(*a)) @@ -69,7 +71,7 @@ void setup(void) pinMode(thermistorvcc, OUTPUT); digitalWrite(thermistorvcc, LOW); - indicator_init(0, NULL); + indicator_init(0); WiFi.mode(WIFI_OFF); WiFi.forceSleepBegin(); @@ -90,12 +92,15 @@ void setup(void) void loop(void) { - static const char * const ssid = "SSID"; - static const char * const password = "password"; + char ssid[32]; + char password[32]; static unsigned int ticker = 0; short int data[3]; size_t i; + volatile_memcpy(ssid, configuration.ssid, sizeof(configuration.ssid)); + volatile_memcpy(password, configuration.password, sizeof(configuration.ssid)); + udp_init_packet(ip, port); if (wifidesired && !wifistate) { @@ -193,7 +198,6 @@ static void wifi_disconnect(void) wifiled_off(); Udp.stop(); WiFi.disconnect(1); - WiFi.mode(WIFI_OFF); WiFi.forceSleepBegin(); yield(); wifistate = 0; diff --git a/src/tempmodule/meson.build b/src/tempmodule/meson.build index e974884..b2c88b9 100644 --- a/src/tempmodule/meson.build +++ b/src/tempmodule/meson.build @@ -1,3 +1,4 @@ +fw_filename = 'fw.bin' if get_option('fwbuild') espmake = find_program('espmake') @@ -5,9 +6,9 @@ if get_option('fwbuild') fw_filenames = ['main.ino', 'indicator.c', 'temperature.c', 'indicator.h', 'temperature.h'] fw_sources = files(fw_filenames) fw = custom_target('fw', - output : 'fw.bin', + output : fw_filename, input : fw_sources, - command : [espmake, '-C', sourcedir, '&&', 'cp', '/tmp/mkESP/main_d1_mini/main.bin', '@OUTDIR@/fw.bin']) + command : [espmake, '-C', sourcedir, '&&', 'cp', '/tmp/mkESP/main_d1_mini/main.bin', '@OUTDIR@/' + fw_filename], install : true, install_dir : resource_dir) if get_option('fwflash') esptool = find_program('esptool.py') diff --git a/src/tempmodule/volatile_ops.c b/src/tempmodule/volatile_ops.c new file mode 100644 index 0000000..432955a --- /dev/null +++ b/src/tempmodule/volatile_ops.c @@ -0,0 +1,38 @@ +/* + * Hot Beverage Companion – volatile data ops + * + * Copyright (C) 2018 Gediminas Jakutis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 + * of the License. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "volatile_ops.h" + +void *volatile_memcpy(void *dest, volatile const void * const src, size_t n) +{ + char *d; + volatile const char *s; + + if (!n) { + return dest; + } + + d = dest; + s = src; + + while (n--) { + d[n] = s[n]; + } +} diff --git a/src/tempmodule/volatile_ops.h b/src/tempmodule/volatile_ops.h new file mode 100644 index 0000000..7a165b9 --- /dev/null +++ b/src/tempmodule/volatile_ops.h @@ -0,0 +1,36 @@ +/* + * Hot Beverage Companion – volatile data ops + * + * Copyright (C) 2018 Gediminas Jakutis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 + * of the License. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef VOLATILE_OPS_INCLUDED +#define VOLATILE_OPS_INCLUDED + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void *volatile_memcpy(void *dest, volatile const void * const src, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* VOLATILE_OPS_INCLUDED */ |