Having seen what a GLADE file can do, let's get into the C code which utilizes it. (Some of this is repeated from earlier documentation.)
We start off with some notes about how to compile, what to include, and some housekeeping. Note that all relays are in place and wired at this point, but occupancy detectors are NOT.
The following DETECTOR info is still under construction, and will probably change soon
struct Detector_Data_Type // fast-reads/buffers individual detector data only. { char value; // read into here gint64 timestamp1; // 1st, microseconds gint64 timestamp2; // rolling next-to-latest gint64 timestamp3; // latest gint64 delta; // elapsed time, start signal to latest or end signal gint64 delta2; // elapsed time, next-to-latest or end to latest signal};struct Detector_Data_Type raw_detector_data[MAX_DETECTORS];Now, let's skip to a few subroutines.
void turn_all_relays_off(){ for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS20,i,off); for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS21,i,off); for(char i=007;((i>=000)&&(i<=007));i--)write_pin(ADDRESS22,i,off_invert);// for(char i=017;((i>=000)&&(i<=017));i--)write_pin(ADDRESS23,i,off);}We'll skip some code for now, such as subroutines to initialize the arrays and define the layout hardware. We'll skip lightly over some bare-bones callback functions. These we'll later set up to input the detector data, but for now we'll implement code to update the display and re-draw it. Then, we'll see what is needed in "main" to incorporate the GLADE interface.
Here we go with the "fast" callback subroutine which will read the block occupancy detectors.
gboolean read_raw_data (gpointer data) // every 300 milliseconds{ int i;//,code; // ,xx,xxx,j;// Process raw_data from hdwe into buffer // and set the xxx_changed flags to TRUE. // Updates pulsing progress bar, // then reads & stores raw data into a buffer and sets xxx_changed datum, // or leaves it alone in raw_data array and leaves xxx_changed datum // so display_data array can be updated appropriately later (not here). // This de-bounces detector data. gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data));Next, we'll process the display update code. This runs less frequently than the detector code, updating the display buffer every 5 seconds.
gboolean process_layout_data_for_display() {// Caled from slow (5-second) timer // via display_layout_and_status_data() timer function.// Processes input data raw_data from detector buffer into display_data array // and resets the xxx_changed flags to FALSE. Displays all // input & output signals, then check for urgent alarm and // caution conditionsint i,j; // int code; GtkLabel *dir_label; const gchar * state; char *format, *format_bkgnd; gchar *markup, *markup_bkgnd; int this_pwr_code, that_pwr_code, other_pwr_code, pwr_flag; int this_dir_code, that_dir_code, other_dir_code, dir_flag;In particular, have a look at how the color and text character are changed in the gaps, as reads, for example,
format="<span foreground=\"#eeeeee\">%s</span>"; markup=g_markup_printf_escaped(format,state); format_bkgnd="<span background=\"#990000\">%s</span>"; // red markup_bkgnd=g_markup_printf_escaped(format_bkgnd,state); gtk_label_set_text( dir_label, state); gtk_label_set_markup(dir_label, markup); gtk_label_set_markup(dir_label, markup_bkgnd);This qualifies as ugly, in my opinion, but it works. First, you set the foreground text and text color up and store it. Then you set up the background color up and store it. Then you print the characters. Then you print the background. Whew! Figuring out how to do this cost quite a bit of time, especially with no debugger program available. The Visual C debugging environment spoiled me, I confess, as did interpreted Visual Basic and whatever we were using for DEC's LISP offering. The ability to set breakpoints, run to them, change the code, back up, and try again is sorely missed.
But enough tears shed. We next need to look at the screen update callback. This is the point where, whenever the machine deems it necessary to update the screen, this callback routine is called. I've abbreviated the code for brevity, but you can see how it's done.
gboolean layout_display_callback(GtkWidget *widget, cairo_t *cr,gpointer data){ // Update the layout display from the display_data[] array global_width = gtk_widget_get_allocated_width (widget); global_height = gtk_widget_get_allocated_height (widget);// Draws the layout diagram, based on techniques best illustrated in // http://zetcode.com/gfx/cairo/basicdrawing/ // see also https://cairographics.org/FAQ/ gboolean pwr,dir; // ..............draw the layout...................... // scale to unit square (0 to 1 width and height) cairo_save(cr); // save the original scalingcairo_scale(cr,global_width, global_height);cairo_set_line_width(cr,0.01);cairo_select_font_face(cr,"Purisa",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 13);...and so on. Next,
// // undo the scale cairo_restore(cr);// layout track drawing complete// display the counter cairo_set_source_rgb(cr,.8,.8,0); // yellowcairo_select_font_face(cr,"Purisa",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr,13); cairo_move_to(cr,10,25); char str_count[50] = {0}; sprintf(str_count, "Diagram Refresh count %i", Global_Counter); cairo_show_text(cr,str_count); // display the legend. show what the alarm condition colors mean int legend_x=global_width/2.0-15; int legend_y=global_height/2.0; cairo_set_line_width(cr,5); cairo_set_source_rgb(cr,0,0,0); // black sprintf(str_count, "- -Legend- -"); cairo_move_to(cr,legend_x-5,legend_y); cairo_show_text(cr,str_count);Had enough yet? Not so fast! We need to do the status screen. This does some hard-coded checking, printing out color-coded status for each track block, starting with the occupied block forst (assuming there's only one, for now), showing for each block the status of the previous block, its status, and the next block's status. The screen capture is above, somewhere near the start of this topic.
First, some code to set the thing up.
struct rgb_struct // status color vector{double r;double g;double b;} ;// each block has three rgb, each of which has three (dir/pwr/occupied)Click here to see the details about Hooking it All Together with the main() function