John Mitchell / lpc4088_displaymodule_GC500_2_5inch

Dependencies:   DMBasicGUI DMSupport

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers GuiLibGraph.cpp Source File

GuiLibGraph.cpp

00001 #include "GuiLibGraph.h"
00002 
00003 #include <cstring>
00004 
00005 #include <float.h>
00006 
00007 
00008 #define USE_LED_FOR_DEBUGGING
00009 
00010 #ifdef USE_LED_FOR_DEBUGGING
00011 
00012 #include "gpio_api.h"
00013 #include "wait_api.h"
00014 #include "toolchain.h"
00015 #include "mbed_interface.h"
00016 
00017 /*
00018     Draw a part of a profile - which will be a quadrilateral, vertical at left and right, horizontal at the bottom, but a sloping straight line at the top -
00019     by calling the EasyGUI GuiLib_VLine function multiple times.
00020     
00021     Args: colour of the profile to the left of the boundary
00022           colour of the profile to the right of the boundary
00023           X coords of the left and right edges of the section
00024           X coord of the colour boundary
00025           Y coord of the bottom
00026           Y coords of the top left and top right
00027           
00028     Returns true if OK, false if it failed (e.g. the coords were invalid).
00029 */
00030 extern bool DrawProfileSectionUsingGuiLibVLine(GuiConst_INTCOLOR colour1, GuiConst_INTCOLOR colour2, GuiConst_INT16S xLeft, GuiConst_INT16S xRight, GuiConst_INT16S xColourBoundary, 
00031                                         GuiConst_INT16S yBottom, GuiConst_INT16S yTopLeft, GuiConst_INT16S yTopRight);
00032 
00033 
00034 // Turn on LED 4 when we get an error (mismatched time/X coord) in the graph data (see below)
00035 static void SetLed4(bool turnLedOn)
00036 {
00037     gpio_t led_4; gpio_init_out(&led_4, LED4);
00038 
00039     if(turnLedOn) {
00040         gpio_write(&led_4, 1); // one appears to mean "turn LED 4 on"
00041     } else {
00042         gpio_write(&led_4, 0); // zero appears to turn it off
00043     }
00044 }
00045 
00046 #endif // USE_LED_FOR_DEBUGGING
00047 
00048 
00049 /*
00050     The GuiLibGraphDataSet class - encapsulates a GuiLib_GraphDataPoint array,
00051     making access and use of it more straightforward than if the caller 
00052     had to do this directly.
00053     
00054     We assume that X coordinates are time values, and Y coordinates are temperature, pressure, etc, values.
00055     ******************************************************************************************************
00056     
00057     We leave it to the caller to decide what the Y coordinate units are, but we let the caller specify
00058     the time (i.e. X axis) units. If the caller changes the units, we recalculate the X coordinates to match.
00059     The options are minutes and seconds - we default to minutes.
00060     
00061     Note that the coord values are integers (as in the easyGUI Graph item).
00062 */
00063 
00064 /*
00065     Static member function, to provide a standard way of converting the floating point coordinates we store internally (for accuracy),
00066     to the integer values required by easyGUI graphs. We always store the time value (X coordinate) as minutes, in units of 0.1 minute,
00067     as does the GC itself. We convert to integer minutes or seconds as required by the caller. 
00068     
00069     We do not concern ourselves here with the units of the Y coordinate - we assume that the caller knows what they are.
00070     
00071     Args: pointer to the GuiLib_GraphDataPoint object to receive the integer coordinates
00072           the floating point data to be converted
00073           the time unit (minutes or seconds) for the generated X coordinate
00074           
00075     No return value (we assume the conversion will always succeed).
00076 */
00077 void GuiLibGraphDataSet::ConvertFloatingDataPointToGuiLibGraphDataPoint(GuiLib_GraphDataPoint* graphDataPoint, FloatingDataPoint floatingDataPoint, TimeUnit timeUnit)
00078 {
00079     // The 'floor' mathematical function expects a double as its argument
00080     
00081     double doubleX = (double) floatingDataPoint.X;
00082     if(timeUnit == SECONDS) {
00083         doubleX *= 60.0;
00084     }
00085     
00086     double doubleY = (double) floatingDataPoint.Y;
00087     
00088     // Round, don't truncate
00089     graphDataPoint->X = (GuiConst_INT32S) floor(doubleX + 0.5);
00090     graphDataPoint->Y = (GuiConst_INT32S) floor(doubleY + 0.5);
00091 }   
00092 
00093 GuiLibGraphDataSet::GuiLibGraphDataSet()
00094 {
00095     // No data yet...
00096 
00097     theGraphDataSet = NULL;
00098     
00099     graphDataSetActualLength = 0;
00100     graphDataSetSizeInIncrements = 0;
00101     
00102     nonRoundedTotalMethodTime = 0.0f;
00103 }
00104 
00105 GuiLibGraphDataSet::~GuiLibGraphDataSet()
00106 {
00107     if(theGraphDataSet != NULL) {
00108         delete [] theGraphDataSet;
00109     }
00110 }
00111 
00112 
00113 /*
00114     Clears (i.e. deletes, removes) all the existing data (if any) for this dataset
00115 */
00116 void GuiLibGraphDataSet::ClearData(void)
00117 {
00118     if(theGraphDataSet != NULL) {
00119         delete [] theGraphDataSet;
00120         theGraphDataSet = NULL;    
00121 
00122         graphDataSetActualLength = 0;
00123         graphDataSetSizeInIncrements = 0;
00124     }
00125 }
00126     
00127 
00128 /*
00129     We extend the dataset array in steps of 'SIZE_INCREMENT'. This function does that,
00130     and copies the existing data to the extended array.
00131     
00132     It returns true if it succeeded in extending the dataset array, false otherwise.
00133 */
00134 bool GuiLibGraphDataSet::ExtendDataSetArray(void)
00135 {
00136     bool retVal = false;
00137     
00138     if(theGraphDataSet != NULL) {
00139         FloatingDataPoint* newGraphDataSet = new FloatingDataPoint[(graphDataSetSizeInIncrements + 1) * SIZE_INCREMENT];
00140         
00141         if(newGraphDataSet != NULL) {
00142             std::memcpy(newGraphDataSet, theGraphDataSet, (graphDataSetSizeInIncrements * SIZE_INCREMENT * sizeof(FloatingDataPoint)));
00143             
00144             delete [] theGraphDataSet;
00145             
00146             theGraphDataSet = newGraphDataSet;
00147             
00148             ++graphDataSetSizeInIncrements;
00149             
00150             // Leave 'graphDataSetActualLength' with whatever value it currently has.
00151             // We have only extended the array, not added any data to it.
00152             
00153             retVal = true; // Success
00154         }
00155 
00156     } else {
00157         // This is the first 'extension'
00158         theGraphDataSet = new FloatingDataPoint[SIZE_INCREMENT];
00159         
00160         if(theGraphDataSet != NULL) {
00161             graphDataSetSizeInIncrements = 1;            
00162 
00163             // We assume 'graphDataSetActualLength' is set to zero - leave it with this value.
00164             // We have only allocated memory for the array, not put any data in it.
00165             
00166             retVal = true; // Success
00167         }
00168     }
00169     
00170     return retVal;
00171 }
00172 
00173 
00174 /*
00175     Adds a datapoint with the specified X and Y values to the dataset array,
00176     making sure to keep the array elements in ascending order of X coordinate.
00177     Other member functions of this class rely on this.
00178     *************************************************
00179     
00180     Returns true for success, false for failure.
00181     
00182     Args: the X and Y values for the new datapoint
00183     
00184     Note that this function takes no account of the units specified for the X (i.e. time) axis - 
00185     we assume that the caller has already ensured that the X value is in those units.
00186 */
00187 bool GuiLibGraphDataSet::AddDataPoint(float X, float Y)
00188 {
00189     // Check that we do not already have a point at the same X coordinate - 
00190     // if so, return false
00191     for (int i = 0; i < graphDataSetActualLength; ++i) {
00192         if(theGraphDataSet[i].X == X) {
00193             return false;
00194         }
00195 
00196         if(theGraphDataSet[i].X > X) {
00197             // We have now reached a point with a greater X coord,
00198             // without finding a point with the same X coord - safe to continue
00199             break;
00200         }
00201     }
00202     
00203     if((graphDataSetActualLength + 1) > (graphDataSetSizeInIncrements * SIZE_INCREMENT)) {
00204 
00205         // We need to extend the array before adding the new datapoint
00206 
00207         if(!ExtendDataSetArray()) {
00208             return false;
00209         }
00210     }
00211             
00212     // We now have room for the new datapoint
00213     // - make sure we keep the datapoints in ascending order of X coord
00214     int positionToAdd = graphDataSetActualLength;
00215 
00216     if(graphDataSetActualLength > 0) {
00217         
00218         while(theGraphDataSet[positionToAdd - 1].X > X) {
00219             --positionToAdd;
00220             
00221             if(positionToAdd == 0) break;
00222         }
00223     
00224     
00225         // Move 'up' all the array elements 'above' positionToAdd
00226         
00227         for (int j = graphDataSetActualLength; j > positionToAdd; --j) {
00228             theGraphDataSet[j] = theGraphDataSet[j - 1];
00229         }
00230     }
00231         
00232     // We are now ready to add the new datapoint
00233 
00234     theGraphDataSet[positionToAdd].X = X;
00235     theGraphDataSet[positionToAdd].Y = Y;
00236     
00237     ++graphDataSetActualLength;
00238             
00239     return true;
00240 }
00241 
00242 /*
00243     Returns the X and Y coordinates of the datapoint at the specified index in the dataset.
00244     
00245     Returns true if we actually have a point at the index, false if not (does not set
00246     the destination variables in this case).
00247     
00248     Args: the index of the point required
00249           pointers to variables to contain the X and Y values of the specified datapoint.
00250           (It is up to the caller to make sure these are valid.)
00251 */
00252 bool GuiLibGraphDataSet::GetDataPoint(int dataPointIndex, float *X, float *Y)
00253 {
00254     if((dataPointIndex >= 0) && (dataPointIndex < graphDataSetActualLength)) {
00255         
00256         *X = theGraphDataSet[dataPointIndex].X;
00257         *Y = theGraphDataSet[dataPointIndex].Y;
00258     
00259         return true;
00260     }
00261     
00262     // 'else' index not valid
00263     return false;
00264 }
00265 
00266 
00267 /*
00268     Returns the Y coordinate of a point on the graph at the specified X coordinate.
00269     This will in general mean interpolating between two adjacent points - we assume
00270     a straight line in this case.
00271     
00272     Note that this function relies on the array elements being in ascending order
00273     of X coordinate.
00274 */
00275 float GuiLibGraphDataSet::GetYCoordAtXCoord(float X)
00276 {
00277     if(graphDataSetActualLength > 0) {
00278         
00279         if(graphDataSetActualLength > 1) {
00280         
00281             // Deal with the extreme cases first
00282             if(theGraphDataSet[0].X > X) {
00283 
00284                 // Required point is before the start of our array
00285                 
00286                 double X0 = (double) theGraphDataSet[0].X;
00287                 double Y0 = (double) theGraphDataSet[0].Y;
00288                 double X1 = (double) theGraphDataSet[1].X;
00289                 double Y1 = (double) theGraphDataSet[1].Y;
00290                 
00291                 double m = (Y1 - Y0) / (X1 - X0);
00292                 double c = Y0 - (m * X0);
00293                 
00294                 return (float)(m * (double) X) + c;
00295             }
00296             
00297             // 'else' ...
00298             if(theGraphDataSet[graphDataSetActualLength - 1].X < X) {
00299 
00300                 // Required point is after the end of our array
00301                 
00302                 double X0 = (double) theGraphDataSet[graphDataSetActualLength - 2].X;
00303                 double Y0 = (double) theGraphDataSet[graphDataSetActualLength - 2].Y;
00304                 double X1 = (double) theGraphDataSet[graphDataSetActualLength - 1].X;
00305                 double Y1 = (double) theGraphDataSet[graphDataSetActualLength - 1].Y;
00306                 
00307                 double m = (Y1 - Y0) / (X1 - X0);
00308                 double c = Y0 - (m * X0);
00309                 
00310                 return (float)(m * (double) X) + c;
00311             }
00312             
00313             // 'else' - required data point must either be coincident with one of our data points,
00314             //          or between two of them
00315             for (int i = 0; i < (graphDataSetActualLength - 1); ++i) {
00316                 
00317                 if(theGraphDataSet[i].X == X) {
00318                     // We already have a data point at the specified X coordinate
00319                     
00320                     return theGraphDataSet[i].Y;
00321                 }
00322 
00323                 // 'else'
00324                 if(theGraphDataSet[i + 1].X == X) {
00325                     // We already have a data point at the specified X coordinate
00326                     
00327                     return theGraphDataSet[i + 1].Y;
00328                 }
00329                 
00330                 // 'else'
00331                 if(theGraphDataSet[i + 1].X > X) {
00332                 
00333                     // We must interpolate between two data points
00334                     
00335                     double xValue = (double) X;
00336                     
00337                     double X0 = (double) theGraphDataSet[i].X;
00338                     double Y0 = (double) theGraphDataSet[i].Y;
00339                     double X1 = (double) theGraphDataSet[i + 1].X;
00340                     double Y1 = (double) theGraphDataSet[i + 1].Y;
00341                     
00342                     double m = (Y1 - Y0) / (X1 - X0);
00343                     double c = Y0 - (m * X0);
00344                     
00345                     return (float)((m * xValue) + c);
00346                 }
00347             }
00348         }
00349         
00350         // 'else' we only have one datapoint - return its Y coord
00351         // (i.e. treat the 'graph' as a straight line)
00352         return theGraphDataSet[0].Y;
00353     }
00354     
00355     // 'else' we have no existing data points to interpolate
00356     return 0.0f;
00357 }
00358 
00359 /*
00360     Copy our current 'actual' floating point data array,
00361     to a GuiLib_GraphDataPoint array, as required by the easyGUI Graph.
00362     
00363     The actual floating point data is always in minutes, but may well have a fractional part,
00364     since the GC measures time, in effect, in units of 0.1 minute. The GuiLib_GraphDataPoint 
00365     structure, however, uses integers, so we copy the actual data to integer numbers
00366     either in minutes or seconds, as specified by the caller. We convert the original 
00367     floating point values to integers by rounding them, using our static function
00368     'ConvertFloatingDataPointToGuiLibGraphDataPoint'.
00369     
00370     If the caller wants minutes to be the time unit, we may well end up with multiple points
00371     at the same X (i.e. time) coordinate - guard against this by combining them into one point 
00372     whose Y coordinate is the average of the duplicate points. If the caller wants seconds,
00373     we assume that we cannot possibly have multiple points at the same X coordinate.
00374     
00375     Note that we pass a pointer to the GuiLib_GraphDataPoint array back to the caller.
00376     It is up to the caller to delete this array when it is no longer needed.
00377     ************************************************************************
00378     
00379     It is also up to the caller to check that the pointer we return is not NULL before using it.
00380     ********************************************************************************************
00381     
00382     
00383     Args: the time unit (minutes or seconds) to be used in the copy 
00384           a pointer to an unsigned integer to contain the number of points in the copied array
00385     
00386     Return value: pointer to the array of GuiLib_GraphDataPoints containing the copied data
00387     
00388 */
00389 GuiLib_GraphDataPoint* GuiLibGraphDataSet::GetGraphDataPointCopy(TimeUnit timeUnit, GuiConst_INT16U *countOfCopiedPoints)
00390 {
00391     // Remember that our 'native' data points are in time (i.e. X coordinate) units of 0.1 minute
00392     
00393     // First, a straight copy, rounding the X coordinate to the unit specified
00394     GuiLib_GraphDataPoint* graphDataSetCopy = new GuiLib_GraphDataPoint[graphDataSetActualLength];
00395     
00396     if(graphDataSetCopy == NULL) {
00397         
00398         // Failed to allocate the memory we need
00399         
00400         *countOfCopiedPoints = 0;
00401         return NULL;
00402     }
00403 
00404     // 'else' memory allocated - continue
00405     
00406     for(int index = 0; index < graphDataSetActualLength; ++index) {
00407         ConvertFloatingDataPointToGuiLibGraphDataPoint(&graphDataSetCopy[index], theGraphDataSet[index], timeUnit);
00408     }
00409 
00410     if(timeUnit == SECONDS) {
00411         
00412         // The simple case - since our 'native' data points are in 'units' of 0.1 minute, 
00413         // we cannot have multiple points at the same number of seconds, i.e. with the same X coordinate - 
00414         // so just return the duplicate array, with rounded X and Y coordinate values, that we created above
00415         
00416         *countOfCopiedPoints = graphDataSetActualLength; // Tell caller how many points there are
00417         return graphDataSetCopy;
00418     }
00419     
00420     
00421     // 'else' must be minutes - now, we must guard against multiple points at the same X coordinate
00422     //                          *******************************************************************
00423     
00424     // Count how many distinct points - i.e. with different X coordinate values - there are
00425     int pointIndex;
00426     GuiConst_INT32S lastXCoord = graphDataSetCopy[0].X;
00427     int countOfDistinctPoints = 1; // We have already looked at the first point
00428     for(pointIndex = 1; pointIndex < graphDataSetActualLength; ++pointIndex) {
00429         if(graphDataSetCopy[pointIndex].X > lastXCoord) {
00430             ++countOfDistinctPoints;
00431             lastXCoord = graphDataSetCopy[pointIndex].X;
00432         } 
00433         // 'else' this point has the same X coordinate as the previous one
00434     }
00435     
00436     // Now allocate an array to contain that number of points
00437     GuiLib_GraphDataPoint* graphDataSetMinutesCopy = new GuiLib_GraphDataPoint[countOfDistinctPoints];
00438     
00439     if(graphDataSetMinutesCopy == NULL) {
00440         // Failed to allocate the memory we need
00441         delete [] graphDataSetCopy;
00442         
00443         *countOfCopiedPoints = 0;
00444         return NULL;
00445     }
00446     
00447     // 'else' memory allocated - continue
00448 
00449     // When we find two or more points with the same X coordinate in the original dataset copy array,
00450     // we create one point in the 'minutes' array, whose Y coordinate is the average of the Y coordinates
00451     // of the original points
00452     
00453     graphDataSetMinutesCopy[0] = graphDataSetCopy[0];
00454     int minutesPointIndex = 0;
00455     lastXCoord = graphDataSetCopy[0].X;
00456     int multiPointCount = 1; // Count of the number of points at the current X coordinate
00457     for(pointIndex = 1; pointIndex < graphDataSetActualLength; ++pointIndex) {
00458         if(graphDataSetCopy[pointIndex].X > lastXCoord) {
00459             
00460             if(multiPointCount > 1) {
00461                 // Set the minutes copy to the average of the Y values we have accumulated
00462                 graphDataSetMinutesCopy[minutesPointIndex].Y /= multiPointCount;
00463                 multiPointCount = 1;
00464             }
00465             
00466             ++minutesPointIndex;
00467             graphDataSetMinutesCopy[minutesPointIndex] = graphDataSetCopy[pointIndex];
00468 
00469             lastXCoord = graphDataSetCopy[pointIndex].X;
00470             
00471         } else {
00472             
00473             // This point must have the same X coordinate as the previous one - 
00474             // start accumulating the Y values, ready to take the average
00475             graphDataSetMinutesCopy[minutesPointIndex].Y += graphDataSetCopy[pointIndex].Y;
00476             ++multiPointCount;
00477         }
00478     }
00479 
00480     if(multiPointCount > 1) {
00481         graphDataSetMinutesCopy[minutesPointIndex].Y /= multiPointCount;        
00482     }
00483 
00484     delete [] graphDataSetCopy;  // No longer needed
00485       
00486     *countOfCopiedPoints = countOfDistinctPoints; // Tell caller how many points there are in the copied array
00487     return graphDataSetMinutesCopy; // Caller wants this array, not the original copy
00488 }
00489 
00490 
00491 /*
00492     Copy our current 'actual' floating point data array,
00493     to a GuiLib_GraphDataPoint array, as required by the easyGUI Graph.
00494     
00495     The actual floating point data is always in minutes, but may well have a fractional part,
00496     since the GC measures time, in effect, in units of 0.1 minute - and we have time as the X axis. 
00497     The GuiLib_GraphDataPoint structure, however, uses integers, so before copying the actual data 
00498     to integer numbers, we multiply the X values by 10.0. 
00499     
00500     Note that we pass a pointer to the GuiLib_GraphDataPoint array back to the caller.
00501     It is up to the caller to delete this array when it is no longer needed.
00502     ************************************************************************
00503     
00504     It is also up to the caller to check that the pointer we return is not NULL before using it.
00505     ********************************************************************************************
00506     
00507     
00508     Args: a pointer to an unsigned integer to contain the number of points in the copied array
00509     
00510     Return value: pointer to the array of GuiLib_GraphDataPoints containing the copied data
00511     
00512 */
00513 GuiLib_GraphDataPoint* GuiLibGraphDataSet::GetGraphDataPointCopyInTenthsOfMinutes(float yAxisScaleFactor, GuiConst_INT16U *countOfCopiedPoints)
00514 {
00515     GuiLib_GraphDataPoint* graphDataSetCopy = new GuiLib_GraphDataPoint[graphDataSetActualLength];
00516     
00517     if(graphDataSetCopy == NULL) {
00518         
00519         // Failed to allocate the memory we need
00520         
00521         *countOfCopiedPoints = 0;
00522         return NULL;
00523     }
00524 
00525     // 'else' memory allocated - continue...
00526     
00527     // The 'floor' mathematical function expects a double as its argument
00528     double doubleX;
00529     double doubleY;
00530         
00531     for(int index = 0; index < graphDataSetActualLength; ++index) {
00532 
00533         // 'floor' math(s) function requires a double argument, not float
00534         doubleX = ((double)theGraphDataSet[index].X) * 10.0;
00535         doubleY = (double)(theGraphDataSet[index].Y * yAxisScaleFactor);
00536     
00537         // Round, don't truncate
00538         graphDataSetCopy[index].X = (GuiConst_INT32S) floor(doubleX + 0.5);
00539         graphDataSetCopy[index].Y = (GuiConst_INT32S) floor(doubleY + 0.5);
00540     }
00541 
00542     *countOfCopiedPoints = graphDataSetActualLength; // Tell caller how many points there are
00543     return graphDataSetCopy;
00544 }
00545 
00546 
00547 /*
00548     Sets another GuiLibGraphDataSet object to a 'partial copy' of this one.
00549     A 'partial copy' is a copy that begins at the specified X start coordinate, and ends at the specified X end coordinate.
00550     We copy all of our coordinate pairs that lie between those two X values to the destination.
00551     If we do not have a coordinate pair exactly at the specified X start coordinate, we generate one 
00552     using our 'GetYCoordAtXCoord' member function. The same applies to the end X coordinate.
00553     
00554     Args: the X coordinate at which to start the copy
00555           the X coordinate at which to end the copy
00556           a pointer to the GuiLibGraphDataSet object which is to be the copy destination
00557 */
00558 void GuiLibGraphDataSet::MakePartialCopy(float startXCoord, float endXCoord, GuiLibGraphDataSet* copyDestination)
00559 {
00560     // First, clear the copy destination of any existing data
00561     copyDestination->ClearData();
00562      
00563 
00564     // If the end coord is less than the start coord, do nothing
00565     // - leave data clear
00566     if(endXCoord < startXCoord) {
00567         return;
00568     }
00569 
00570     if(graphDataSetActualLength > 0) {
00571         
00572         int pointIndex;
00573         for (pointIndex = 0; pointIndex < graphDataSetActualLength; ++pointIndex) {
00574             
00575             if(theGraphDataSet[pointIndex].X >= startXCoord) {
00576                 break;
00577             }
00578         }
00579         
00580         if(pointIndex < graphDataSetActualLength) {
00581             if (theGraphDataSet[pointIndex].X > startXCoord) {
00582             
00583                 // Need to add a start point at the specified start X coord
00584                 copyDestination->AddDataPoint(startXCoord, GetYCoordAtXCoord(startXCoord));
00585                     
00586             } // 'else' the start point already has the correct X coordinate - no need to add another
00587             
00588             // pointIndex already has the correct start value
00589             for (; pointIndex < graphDataSetActualLength; ++pointIndex) {
00590                 
00591                 if(theGraphDataSet[pointIndex].X <= endXCoord) {
00592                     copyDestination->AddDataPoint(theGraphDataSet[pointIndex].X, theGraphDataSet[pointIndex].Y);
00593                 } else {
00594                     break;
00595                 }
00596             }
00597         
00598             if (theGraphDataSet[pointIndex - 1].X < endXCoord) {
00599             
00600                 // Need to add a final point at the end X coord 
00601                 copyDestination->AddDataPoint(endXCoord, GetYCoordAtXCoord(endXCoord));
00602                     
00603             } // 'else' the final point already has the correct X coordinate - no need to add another
00604             
00605         } // 'else' we have no points above the start X coordinate - nothing to copy  
00606               
00607     } // 'else' we have no points to copy
00608 }
00609 
00610 /*
00611     Sets another GuiLibGraphDataSet object to an 'interpolated partial copy' of this one.
00612     This is a copy that starts at a specified X coordinate and finishes at another specified X coordinate,
00613     and has a specified interval between X coordinates. This will, in general, require interpolating 
00614     between our existing data points.
00615         
00616     Args: the X coordinate at which to start
00617           the X coordinate at which to finish
00618           the X interval - i.e. the interval between X coords
00619           a pointer to the GuiLibGraphDataSet object which is to be the copy destination
00620 */
00621 void GuiLibGraphDataSet::MakeInterpolatedPartialCopy(float startXCoord, float endXCoord, float xInterval, GuiLibGraphDataSet* copyDestination)
00622 {
00623     // First, clear the copy destination of any existing data
00624     copyDestination->ClearData();
00625          
00626     // Let the for loop limits take care of whether the start and end X coordinates are in the correct order
00627 
00628     // Now copy/generate the actual points 
00629     float xCoord;
00630     float endLimit = endXCoord + (xInterval / 2.0f); // Should just be 'endXCoord' - but allow for floating point rounding,
00631                                                     // otherwise we may omit the last point 
00632     for (xCoord = startXCoord; xCoord <= endLimit; xCoord += xInterval) {
00633         copyDestination->AddDataPoint(xCoord, GetYCoordAtXCoord(xCoord));
00634     }
00635 }
00636 
00637 /*
00638     Sets another GuiLibGraphDataSet object to an 'interpolated partial copy' of this one.
00639     This is a copy that starts at a specified X coordinate and finishes at another specified X coordinate,
00640     and has a specified interval between X coordinates. This will, in general, require interpolating 
00641     between our existing data points.
00642     
00643     Unlike the original 'MakeInterpolatedPartialCopy' above, this guarantees to add a final point at the end 
00644     of the copy range, whether this makes an exact interval or not. This means that, in a profile where 
00645     the 'interpolated partial copy' is displayed as a bar chart underneath a line (created using the 
00646     'MakePartialCopy' function), we are guaranteed to have a bar at the exact end of the line - 
00647     it will not overhang empty space.
00648         
00649     Args: the X coordinate at which to start
00650           the X coordinate at which to finish
00651           the X interval - i.e. the interval between X coords
00652           a pointer to the GuiLibGraphDataSet object which is to be the copy destination
00653 */
00654 void GuiLibGraphDataSet::MakeInterpolatedPartialCopyWithFinalPoint(float startXCoord, float endXCoord, float xInterval, GuiLibGraphDataSet* copyDestination)
00655 {
00656     // First, clear the copy destination of any existing data
00657     copyDestination->ClearData();
00658          
00659     // Let the for loop limits take care of whether the start and end X coordinates are in the correct order
00660 
00661     // Now copy/generate the actual points 
00662     float xCoord;
00663     bool needFinalPoint = true;
00664     for (xCoord = startXCoord; xCoord <= endXCoord; xCoord += xInterval) {
00665         copyDestination->AddDataPoint(xCoord, GetYCoordAtXCoord(xCoord));
00666         
00667         if(xCoord == endXCoord) {
00668             needFinalPoint = false;
00669         }
00670     }
00671     
00672     if(needFinalPoint) {
00673         // We need to add a final point at the exact end of the copy range
00674         copyDestination->AddDataPoint(endXCoord, GetYCoordAtXCoord(endXCoord));
00675     }
00676 }
00677 
00678 /*
00679     Gets the current temperature for a particular component.
00680     
00681     Args: the GC command to get the temperature for that component
00682           the usbDevice and usbHostGC instances corresponding to the GC
00683     
00684     Returns the current temperature of the component as a floating-point value, in degrees C.
00685 */
00686 float GuiLibGraphDataSet::GetComponentTemperature(char *cmd, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00687 {
00688     // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 
00689     // it is not re-entrant (and nor is the GC)
00690     while(usbHostGC->ExecutingSetDeviceReport()) {}
00691 
00692     char response[50];
00693     usbHostGC->SetDeviceReport(usbDevice, cmd, response);
00694     // We expect a response like this: "Dxxx1234" - temp in units of 1 deg c
00695     
00696     float retVal;
00697     
00698     // But check for "EPKT" first
00699     if(response[0] == 'E') {
00700         retVal = -1.0f; // ** Caller must check for this **
00701     } else {
00702         sscanf(&response[4], "%f", &retVal);
00703     }
00704     
00705     return retVal;
00706 }
00707 
00708 /*
00709     Gets, and returns, the current column temperature.
00710     
00711     Returns the current column temperature as a floating-point value, in degrees C.
00712 */
00713 float GuiLibGraphDataSet::GetColumnTemperature(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00714 {
00715 //#define GCOL_VALUE_IN_TENTHS_OF_A_DEGREE
00716 #ifdef GCOL_VALUE_IN_TENTHS_OF_A_DEGREE
00717     // This value is actually in tenths of a degree, not whole degrees as stated above
00718     return ((GetComponentTemperature("GCOL", usbDevice, usbHostGC)) * 0.1f);
00719 #undef GCOL_VALUE_IN_TENTHS_OF_A_DEGREE
00720 #else
00721     return GetComponentTemperature("GCOL", usbDevice, usbHostGC);
00722 #endif
00723 }
00724 
00725 /*
00726     Gets, and returns, the current injector temperature.
00727     
00728     Returns the current injector temperature as a floating-point value, in degrees C.
00729 */
00730 float GuiLibGraphDataSet::GetInjectorTemperature(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00731 {
00732 //#define GINJ_VALUE_IN_TENTHS_OF_A_DEGREE
00733 #ifdef GINJ_VALUE_IN_TENTHS_OF_A_DEGREE
00734     // This value is actually in tenths of a degree, not whole degrees as stated above
00735     return ((GetComponentTemperature("GINJ", usbDevice, usbHostGC)) * 0.1f);
00736 #undef GINJ_VALUE_IN_TENTHS_OF_A_DEGREE
00737 #else
00738     return GetComponentTemperature("GINJ", usbDevice, usbHostGC);
00739 #endif
00740 }
00741 
00742 /*
00743     Gets, and returns, a time value, obtained using the command passed to it.
00744     We expect that this value will be returned by the GC in units of 0.1 minute.
00745     
00746     Args: a pointer to a null-terminated string containing the command to use
00747           pointers to the USBDevice and USBHost instances corresponding to the GC
00748     
00749     Note that this code is intended to be as efficient as possible.
00750     
00751     Returns the required time as a floating-point value, in minutes - i.e. scaled from 
00752     the value returned by the GC.
00753 */
00754 float GuiLibGraphDataSet::GetTimeValue(char *cmd, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00755 {
00756     // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 
00757     // it is not re-entrant (and nor is the GC)
00758     while(usbHostGC->ExecutingSetDeviceReport()) {}
00759 
00760     char buff[10];
00761     char response[50];
00762     usbHostGC->SetDeviceReport(usbDevice, cmd, response);
00763     // We expect a response like this: "Dxxx1234" - time in units of 0.1 minute
00764     
00765     float retVal;
00766     
00767     // But check for "EPKT" first
00768     if(response[0] == 'E') {
00769         retVal = -1.0f; // ** Caller must check for this **
00770     } else {
00771         buff[0] = response[4];
00772         buff[1] = response[5];
00773         buff[2] = response[6];
00774         buff[3] = '.';
00775         buff[4] = response[7];
00776 
00777         sscanf(buff, "%f", &retVal);
00778     }
00779     
00780     return retVal;
00781 }
00782 
00783 /*
00784     Gets, and returns, the initial hold time.
00785     
00786     Returns the initial hold time as a floating-point value, in minutes.
00787 */
00788 float GuiLibGraphDataSet::GetInitialHoldTime(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00789 {
00790     return GetTimeValue("GTIM", usbDevice, usbHostGC);
00791 }
00792 
00793 /*
00794     Gets, and returns, the PTV/injector initial time.
00795     
00796     Returns the initial time as a floating-point value, in minutes.
00797 */
00798 float GuiLibGraphDataSet::GetPTVInitialTime(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00799 {
00800     return GetTimeValue("GIPT", usbDevice, usbHostGC);
00801 }
00802 
00803 /*
00804     Gets, and returns, the initial pressure.
00805     
00806     Note that this code is intended to be as efficient as possible.
00807     
00808     Returns the initial pressure as a floating-point value, in minutes.
00809 */
00810 float GuiLibGraphDataSet::GetInitialPressure(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00811 {
00812     // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 
00813     // it is not re-entrant (and nor is the GC)
00814     while(usbHostGC->ExecutingSetDeviceReport()) {}
00815 
00816     char buff[10];
00817     char response[50];
00818     usbHostGC->SetDeviceReport(usbDevice, "GPRS", response);
00819     // We expect a response like this: "DPRS1234" - pressure in units of 0.1 psi
00820     
00821     float retVal;
00822     
00823     // But check for "EPKT" first
00824     if(response[0] == 'E') {
00825         retVal = -1.0f; // ** Caller must check for this **
00826     } else {
00827         buff[0] = response[4];
00828         buff[1] = response[5];
00829         buff[2] = response[6];
00830         buff[3] = '.';
00831         buff[4] = response[7];
00832 
00833         sscanf(buff, "%f", &retVal);
00834     }
00835     
00836     return retVal;
00837 }
00838 
00839 /*
00840     Gets a value from the GC for a particular ramp. 
00841     
00842     All commands that get ramp values from the GC have the form "Gxxr", where "G" means "get", 
00843     the characters "xx" are specific to the command, and "r" is the ramp index, 0 through 9. 
00844     The GC's response will have the form "Dxxrnnnn", where "nnnn" is a four digit value - 
00845     this is the value we want. This function converts that value to a float, and returns it in that form. 
00846     It is up to the caller to convert this into the correct units for the command, including scaling it if necessary.
00847     
00848     Args: the command to get the required ramp value (the three "Gxx" characters)
00849           the ramp index (0 to 9 inclusive)
00850           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00851           
00852     Returns the ramp value obtained from the GC, as an (unscaled) floating point value
00853 */
00854 float GuiLibGraphDataSet::GetRampValue(char *cmd, int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00855 {
00856     // Check command length is valid
00857     if(strlen(cmd) != 3) {
00858         return 0.0f;
00859     }
00860     
00861     // Now the ramp index
00862     if((rampIndex < 0) || (rampIndex > 9)) {
00863         return 0.0f;
00864     }
00865     
00866     
00867     char response[GC_MESSAGE_LENGTH+2];
00868 
00869     // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 
00870     // it is not re-entrant (and nor is the GC)
00871     while(usbHostGC->ExecutingSetDeviceReport()) {}
00872 
00873     char buff[100];
00874     sprintf(buff, "%s%d", cmd, rampIndex);
00875     usbHostGC->SetDeviceReport(usbDevice, buff, response);
00876     
00877     float rampValue;
00878 
00879     // We expect a response of the form "DXXnrrrr", where "XX" is the second and third character of the command,
00880     // 'n' is the ramp index, and "rrrr" is the required ramp value
00881 
00882     // But check for "EPKT" first...
00883     if(response[0] == 'E') {
00884         rampValue = 0.0f;
00885     } else {
00886         sscanf(&response[4], "%f", &rampValue);
00887     }
00888     
00889     return rampValue;
00890 }
00891 
00892 /*
00893     Gets the temperature ramp rate for a particular ramp.
00894     
00895     Args: the ramp index (0 to 9 inclusive)
00896           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00897           
00898     Returns the temperature ramp rate obtained from the GC, in degrees C per minute.
00899 */
00900 float GuiLibGraphDataSet::GetTemperatureRampRate(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00901 {
00902     return GetRampValue("GRP", rampIndex, usbDevice, usbHostGC);
00903 }
00904 
00905 /*
00906     Gets the PTV/injector temperature ramp rate for a particular ramp.
00907     
00908     Args: the ramp index (0 to 9 inclusive)
00909           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00910           
00911     Returns the temperature ramp rate obtained from the GC, in degrees C per minute.
00912 */
00913 float GuiLibGraphDataSet::GetPTVTemperatureRampRate(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00914 {
00915     return GetRampValue("GIP", rampIndex, usbDevice, usbHostGC);
00916 }
00917 
00918 /*
00919     Gets the upper temperature for a particular ramp.
00920     
00921     Args: the ramp index (0 to 9 inclusive)
00922           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00923           
00924     Returns the upper temperature obtained from the GC, in degrees C.
00925 */
00926 float GuiLibGraphDataSet::GetRampUpperTemperature(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00927 {
00928 //#define GRC_VALUE_IN_TENTHS_OF_A_DEGREE
00929 #ifdef GRC_VALUE_IN_TENTHS_OF_A_DEGREE
00930     // This value is actually in tenths of a degree, not whole degrees as stated above
00931     return ((GetRampValue("GRC", rampIndex, usbDevice, usbHostGC)) * 0.1f);
00932 #undef GRC_VALUE_IN_TENTHS_OF_A_DEGREE
00933 #else
00934     return GetRampValue("GRC", rampIndex, usbDevice, usbHostGC);
00935 #endif
00936 }
00937 
00938 /*
00939     Gets the upper temperature for a particular PTV/injector ramp.
00940     
00941     Args: the ramp index (0 to 9 inclusive)
00942           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00943           
00944     Returns the upper temperature obtained from the GC, in degrees C.
00945 */
00946 float GuiLibGraphDataSet::GetPTVRampUpperTemperature(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00947 {
00948     return GetRampValue("GIC", rampIndex, usbDevice, usbHostGC);
00949 }
00950 
00951 /*
00952     Gets the ramp time for a particular ramp.
00953     
00954     Args: the ramp index (0 to 9 inclusive)
00955           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00956           
00957     Returns the ramp time obtained from the GC, in minutes (actually the GC returns a value in units of 0.1 minute,
00958     but this function applies that scaling to the value before returning it).
00959 */
00960 float GuiLibGraphDataSet::GetRampUpperTime(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00961 {
00962     return ((GetRampValue("GRS", rampIndex, usbDevice, usbHostGC)) * 0.1f);
00963 }
00964 
00965 /*
00966     Gets the ramp time for a particular PTV/injector ramp.
00967     
00968     Args: the ramp index (0 to 9 inclusive)
00969           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00970           
00971     Returns the ramp time obtained from the GC, in minutes (actually the GC returns a value in units of 0.1 minute,
00972     but this function applies that scaling to the value before returning it).
00973 */
00974 float GuiLibGraphDataSet::GetPTVRampUpperTime(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00975 {
00976     return ((GetRampValue("GIS", rampIndex, usbDevice, usbHostGC)) * 0.1f);
00977 }
00978 
00979 /*
00980     Gets the pressure ramp rate for a particular ramp.
00981     
00982     Args: the ramp index (0 to 9 inclusive)
00983           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00984           
00985     Returns the pressure ramp rate obtained from the GC, in psi per minute (actually the GC returns a value 
00986     in units of 0.01 psi/min, but this function applies that scaling to the value before returning it).
00987 */
00988 float GuiLibGraphDataSet::GetPressureRampRate(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
00989 {
00990     return ((GetRampValue("GPR", rampIndex, usbDevice, usbHostGC)) * 0.01f);
00991 }
00992 
00993 /*
00994     Gets the upper pressure for a particular ramp.
00995     
00996     Args: the ramp index (0 to 9 inclusive)
00997           pointers to the USBDeviceConnected and USBHostGC instances that match the GC
00998           
00999     Returns the upper pressure obtained from the GC, in psi (actually the GC returns a value 
01000     in units of 0.1 psi, but this function applies that scaling to the value before returning it).
01001 */
01002 float GuiLibGraphDataSet::GetRampUpperPressure(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
01003 {
01004     return ((GetRampValue("GPU", rampIndex, usbDevice, usbHostGC)) * 0.1f);
01005 }
01006 
01007 /*
01008     Setup this dataset to match the column ramp temperature vs time values 
01009     currently set up in the GC.
01010     
01011     Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC
01012     
01013     Returns the number of points in the updated dataset.
01014 */
01015 int GuiLibGraphDataSet::SetupFromColumnTemperatureRampValues(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
01016 {
01017     // First, delete all existing data from this dataset
01018     ClearData();
01019     
01020 
01021     // Now let the AddDataPoint function take care of extending the dataset array, etc
01022     
01023     // First point will be at the current temperature, time zero.
01024     // Second point will be at the same temperature, after the initial time.
01025 
01026     float initialTemperature = GetColumnTemperature(usbDevice, usbHostGC);
01027     float initialHoldTime = GetInitialHoldTime(usbDevice, usbHostGC);
01028 
01029     AddDataPoint(0, initialTemperature);
01030     AddDataPoint(initialHoldTime, initialTemperature);
01031     int pointCount = 2;
01032     
01033     // These values are specific to each ramp (index 0 through 9)
01034     float temperatureRampRate; // This is the rate at which the GC will increase the column temperature
01035                                // (if this is zero, this ramp is not used, and nor are any after it - 
01036                                // i.e. the previous ramp was the final one)
01037     float rampUpperTemperature;    // This is the upper (or "target") temperature
01038     float rampUpperTime; // Once the column reaches the upper/target temperature, the GC 
01039                          // will keep it at that temperature for this length of time - 
01040                          // at the end of this, the next ramp will start (unless this is the last one)
01041     float rampingTime;
01042     
01043     float totalMethodTime = initialHoldTime;
01044     
01045     float previousRampUpperTemperature = initialTemperature;
01046     
01047     // These are the coordinates we add to the graph dataset
01048     float X1, X2;
01049     float Y1, Y2;
01050     
01051 #ifdef USE_VERSION_102 // This starts at ramp index 0 - older versions start at 1
01052     for (int rampIndex = 0; rampIndex < 10; ++rampIndex) {
01053 #else
01054     for (int rampIndex = 1; rampIndex < 10; ++rampIndex) {
01055 #endif        
01056         temperatureRampRate = GetTemperatureRampRate(rampIndex, usbDevice, usbHostGC);
01057 
01058         if(temperatureRampRate <= FLT_MIN) { // Avoid rounding errors - do not test for equality with zero
01059             // No more ramps
01060             break;
01061         }
01062 
01063 
01064         rampUpperTemperature = GetRampUpperTemperature(rampIndex, usbDevice, usbHostGC);
01065         
01066         // Sanity check 
01067         if(rampUpperTemperature < previousRampUpperTemperature) {
01068             // This should never happen. We must have got our temperature units confused (or something) - give up
01069             break;
01070         }
01071 
01072         rampingTime = (rampUpperTemperature - previousRampUpperTemperature) / temperatureRampRate;
01073         
01074         totalMethodTime += rampingTime;
01075 
01076         X1 = totalMethodTime;
01077         Y1 = rampUpperTemperature;
01078         
01079         if(AddDataPoint(X1, Y1)) { 
01080             ++pointCount; 
01081         }
01082 
01083 
01084         rampUpperTime = GetRampUpperTime(rampIndex, usbDevice, usbHostGC);
01085 
01086         if(rampUpperTime > FLT_MIN) { // i.e. > 0 (avoid floating point rounding errors)
01087         
01088             totalMethodTime += rampUpperTime;
01089     
01090             X2 = totalMethodTime;
01091             Y2 = rampUpperTemperature;
01092     
01093             if(AddDataPoint(X2, Y2)) { 
01094                 ++pointCount;
01095             }
01096         }
01097         // 'Else' the ramp upper time is zero - we are not holding here - 
01098         // so do not add a second point (AddDataPoint does not allow 
01099         // two points at the same X coordinate anyway)
01100         // *** See comment [HOLDING INTERVAL] in SetupGasPressureProfileWithTimingsFromColumnMethod ***
01101                 
01102         previousRampUpperTemperature = rampUpperTemperature;
01103     }
01104     
01105     nonRoundedTotalMethodTime = totalMethodTime;
01106 
01107     return pointCount;
01108 }
01109 
01110 /*
01111     Setup this dataset to match the ramp pressure vs time values currently set up in the GC. 
01112     Compares the time values with those for a specified column method, and (assuming the time values 
01113     should match those for the column method) turns on LED 3 if they do not.
01114     
01115     Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC
01116           - pointer to a GuiLibGraphDataSet object containing the matching column method
01117     
01118     Returns the number of points in the updated dataset.
01119 */
01120 int GuiLibGraphDataSet::SetupFromPressureRampValues(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, GuiLibGraphDataSet* columnMethodDataSet)
01121 {
01122     // First, delete all existing data from this dataset
01123     ClearData();
01124     
01125     
01126     // Now let the AddDataPoint function take care of extending the dataset array, etc
01127     
01128     // First point will be at the initial pressure, time zero.
01129     // Second point will be at the same pressure, after the initial time.
01130     float initialPressure = (double) GetInitialPressure(usbDevice, usbHostGC);
01131     float initialHoldTime = (double) GetInitialHoldTime(usbDevice, usbHostGC);
01132     
01133     AddDataPoint(0, initialPressure);
01134     AddDataPoint(initialHoldTime, initialPressure);
01135     int pointCount = 2;    
01136     
01137     // These values are specific to each ramp (index 0 through 9)
01138     float pressureRampRate; // This is the rate at which the GC will increase the gas pressure
01139                             // (if this is zero, this ramp is not used, and nor are any after it - 
01140                             // i.e. the previous ramp was the final one)
01141     float rampUpperPressure;    // This is the upper (or "target") pressure
01142     float rampUpperTime; // Once the column reaches the upper/target pressure, the GC 
01143                          // will keep it at that pressure for this length of time - 
01144                          // at the end of this, the next ramp will start (unless this is the last one)
01145     float rampingTime;
01146     
01147     float totalMethodTime = initialHoldTime;
01148     
01149     float previousRampUpperPressure = initialPressure;
01150     
01151     // These are the coordinates we add to the graph dataset
01152     float X1, X2;
01153     float Y1, Y2;
01154     
01155     // These are the corresponding coordinates for the column method
01156     float columnX1, columnX2;
01157     float columnY1, columnY2;
01158     
01159 #ifdef USE_VERSION_102 // This starts at ramp index 0, older versions start at 1
01160     for (int rampIndex = 0; rampIndex < 10; ++rampIndex) {
01161 #else
01162     for (int rampIndex = 1; rampIndex < 10; ++rampIndex) {
01163 #endif   
01164         pressureRampRate = GetPressureRampRate(rampIndex, usbDevice, usbHostGC);
01165 
01166         if(pressureRampRate <= FLT_MIN) { // Avoid rounding errors - do not test for equality with zero
01167             // No more ramps
01168             break;
01169         }
01170 
01171 
01172         rampUpperPressure = GetRampUpperPressure(rampIndex, usbDevice, usbHostGC);
01173 
01174         // Sanity check 
01175         if(rampUpperPressure < previousRampUpperPressure) {
01176             // This should never happen. We must have got our pressure units confused (or something) - give up
01177             break;
01178         }
01179 
01180 
01181         rampingTime = (rampUpperPressure - previousRampUpperPressure) / pressureRampRate;
01182         
01183         totalMethodTime += rampingTime;
01184 
01185         // Round the floating point values to the nearest integer
01186         X1 = totalMethodTime;
01187         Y1 = rampUpperPressure;
01188         
01189         if(columnMethodDataSet->GetDataPoint(pointCount, &columnX1, &columnY1)) {
01190             if(X1 != columnX1) {
01191                 // Our time and the corresponding column method time do not match - 
01192                 // indicate error by turning on LED 3, and use the column method value
01193                 // (the two methods must match)
01194 #ifdef USE_LED_FOR_DEBUGGING
01195                 SetLed4(true);
01196 #endif
01197                 X1 = columnX1;
01198             }
01199         }
01200 
01201         if(AddDataPoint(X1, Y1)) { 
01202             ++pointCount; 
01203         }
01204 
01205 
01206         rampUpperTime = GetRampUpperTime(rampIndex, usbDevice, usbHostGC);
01207         
01208         totalMethodTime += rampUpperTime;
01209 
01210         X2 = totalMethodTime;
01211         Y2 = rampUpperPressure;
01212 
01213         if(columnMethodDataSet->GetDataPoint(pointCount, &columnX2, &columnY2)) {
01214             if(X2 != columnX2) {
01215                 // Our time and the corresponding column method time do not match - 
01216                 // indicate error by turning on LED 3, and use the column method value
01217                 // (the two methods must match)
01218 #ifdef USE_LED_FOR_DEBUGGING
01219                 SetLed4(true);
01220 #endif
01221                 X2 = columnX2;
01222             }
01223         }
01224 
01225         if(AddDataPoint(X2, Y2)) { 
01226             ++pointCount;
01227         }
01228         
01229         previousRampUpperPressure = rampUpperPressure;
01230     }
01231     
01232     nonRoundedTotalMethodTime = totalMethodTime;
01233     
01234 #ifdef USE_LED_FOR_DEBUGGING
01235     // Done with LED 4
01236     SetLed4(false);
01237 #endif
01238     return pointCount;
01239 }
01240 
01241 /*
01242     Setup this dataset to give a injector temperature profile that matches a column method.
01243     This will show a constant injector temperature for the same length of time 
01244     as the specified column method.
01245     
01246     Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC
01247           - pointer to a GuiLibGraphDataSet object containing the matching column method
01248     
01249     Returns the number of points in the updated dataset (this will always be 2).
01250 */
01251 int GuiLibGraphDataSet::SetupInjectorTemperatureProfileToMatchColumnMethod(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, GuiLibGraphDataSet* columnMethodDataSet)
01252 {
01253     // First, delete all existing data from this dataset
01254     ClearData();
01255     
01256 
01257     nonRoundedTotalMethodTime = columnMethodDataSet->nonRoundedTotalMethodTime;
01258     
01259     // Now let the AddDataPoint function take care of extending the dataset array, etc
01260     
01261     // First point will be at the current injector temperature, time zero.
01262     // Second point will be at the same temperature, at the end of the column method time.
01263 
01264     float injectorTemperature = GetInjectorTemperature(usbDevice, usbHostGC);
01265     float methodDuration = columnMethodDataSet->GetTotalMethodTime();
01266     
01267     AddDataPoint(0, injectorTemperature);
01268     AddDataPoint(methodDuration, injectorTemperature);
01269     int pointCount = 2;
01270 
01271     return pointCount;
01272 }
01273 
01274 
01275 /*
01276     Setup this dataset to an injector temperature profile derived purely from the 
01277     injector/PTV settings in the GC, without reference to the current column method (if any).
01278     
01279     Args: pointers to the USBDeviceConnected and USBHostGC instances that match the GC
01280     
01281     Returns the number of points in the updated dataset.
01282 */
01283 int GuiLibGraphDataSet::SetupFromPTVTemperatureRampValues(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
01284 {
01285     // First, delete all existing data from this dataset
01286     ClearData();
01287     
01288 
01289     // Now let the AddDataPoint function take care of extending the dataset array, etc
01290     
01291     // First point will be at the current PTV temperature, time zero.
01292     // Second point will be at the same temperature, after the initial time.
01293     // Note that the words "injector" and "PTV" are effectively synonymous
01294 
01295     float initialTemperature = GetInjectorTemperature(usbDevice, usbHostGC);
01296     float initialTime = GetPTVInitialTime(usbDevice, usbHostGC);
01297 
01298     AddDataPoint(0, initialTemperature);
01299     AddDataPoint(initialTime, initialTemperature);
01300     int pointCount = 2;
01301     
01302     // These values are specific to each ramp (index 0 through 9)
01303     float temperatureRampRate; // This is the rate at which the GC will increase the PTV temperature
01304                                // (if this is zero, this ramp is not used, and nor are any after it - 
01305                                // i.e. the previous ramp was the final one)
01306     float rampUpperTemperature;    // This is the upper (or "target") temperature
01307     float rampUpperTime; // Once the PTV reaches the upper/target temperature, the GC 
01308                          // will keep it at that temperature for this length of time - 
01309                          // at the end of this, the next ramp will start (unless this is the last one)
01310     float rampingTime;
01311     
01312     float totalMethodTime = initialTime;
01313     
01314     float previousRampUpperTemperature = initialTemperature;
01315     
01316     // These are the coordinates we add to the graph dataset
01317     float X1, X2;
01318     float Y1, Y2;
01319     
01320 #ifdef USE_VERSION_102 // This starts at ramp index 0 - older versions start at 1
01321     for (int rampIndex = 0; rampIndex < 10; ++rampIndex) {
01322 #else
01323     for (int rampIndex = 1; rampIndex < 10; ++rampIndex) {
01324 #endif        
01325         temperatureRampRate = GetPTVTemperatureRampRate(rampIndex, usbDevice, usbHostGC);
01326 
01327         if(temperatureRampRate <= FLT_MIN) { // Avoid rounding errors - do not test for equality with zero
01328             // No more ramps
01329             break;
01330         }
01331 
01332 
01333         rampUpperTemperature = GetPTVRampUpperTemperature(rampIndex, usbDevice, usbHostGC);
01334         
01335         // Sanity check 
01336         if(rampUpperTemperature < previousRampUpperTemperature) {
01337             // This should never happen. We must have got our temperature units confused (or something) - give up
01338             break;
01339         }
01340 
01341         rampingTime = (rampUpperTemperature - previousRampUpperTemperature) / temperatureRampRate;
01342         
01343         totalMethodTime += rampingTime;
01344 
01345         X1 = totalMethodTime;
01346         Y1 = rampUpperTemperature;
01347         
01348         if(AddDataPoint(X1, Y1)) { 
01349             ++pointCount; 
01350         }
01351 
01352 
01353         rampUpperTime = GetPTVRampUpperTime(rampIndex, usbDevice, usbHostGC);
01354 
01355         if(rampUpperTime > FLT_MIN) { // i.e. > 0 (avoid floating point rounding errors)
01356         
01357             totalMethodTime += rampUpperTime;
01358     
01359             X2 = totalMethodTime;
01360             Y2 = rampUpperTemperature;
01361     
01362             if(AddDataPoint(X2, Y2)) { 
01363                 ++pointCount;
01364             }
01365         }
01366         // 'Else' the ramp upper time is zero - we are not holding here - 
01367         // so do not add a second point (AddDataPoint does not allow 
01368         // two points at the same X coordinate anyway)
01369         
01370         previousRampUpperTemperature = rampUpperTemperature;
01371     }
01372     
01373     nonRoundedTotalMethodTime = totalMethodTime;
01374 
01375     return pointCount;
01376 }
01377 
01378 /*
01379     Setup this dataset with a gas pressure profile that matches the current column method.
01380     The timings will be taken from the matching column method - which *must* already be set up.
01381     In Constant Pressure mode (first gas ramp rate == 0), the same pressure
01382     will be maintained from start to end of the method, while in Programmed Pressure mode
01383     (first gas ramp rate > 0), the pressure ramps will each start and end at the same times 
01384     as the corresponding temperature ramps in the column method.
01385     
01386     In both cases, the total duration will be the same as the column method.
01387     
01388     Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC
01389           - pointer to a GuiLibGraphDataSet object containing the matching column method
01390     
01391     Returns the number of points in the updated dataset.
01392 */
01393 int GuiLibGraphDataSet::SetupGasPressureProfileWithTimingsFromColumnMethod(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, GuiLibGraphDataSet* columnMethodDataSet)
01394 {
01395     // First, delete all existing data from this dataset
01396     ClearData();
01397 
01398     // Now let the AddDataPoint function take care of extending the dataset array, etc
01399 
01400     int pointCount; 
01401     
01402     if(GetPressureRampRate(0, usbDevice, usbHostGC) > FLT_MIN) { // Avoid rounding errors
01403 
01404         // Programmed Pressure mode
01405             
01406         // First point will be at the initial pressure, time zero.
01407         // Second point will be at the same pressure, after the initial time.
01408         float initialPressure = (double) GetInitialPressure(usbDevice, usbHostGC);
01409         float initialHoldTime = (double) GetInitialHoldTime(usbDevice, usbHostGC);
01410         
01411         AddDataPoint(0, initialPressure);
01412         AddDataPoint(initialHoldTime, initialPressure);
01413         pointCount = 2;    
01414         
01415         // This value is specific to each ramp (index 0 through 9)
01416         float rampUpperPressure;    // This is the upper (or "target") pressure
01417         
01418         float totalMethodTime = initialHoldTime;
01419         
01420         // These are the coordinates we add to the graph dataset.
01421         // X coordinates are time values, Y coordinates are temperatures
01422         // (in the column dataset) or pressures (in this dataset).
01423         // Note that we get our time values from the column dataset - 
01424         // we do not calculate them here - we want to make sure they match
01425         float X1, Y1;
01426         float X2, Y2;
01427         
01428     #ifdef USE_VERSION_102 // This starts at ramp index 0, older versions start at 1
01429         for (int rampIndex = 0; rampIndex < 10; ++rampIndex) {
01430     #else
01431         for (int rampIndex = 1; rampIndex < 10; ++rampIndex) {
01432     #endif   
01433             rampUpperPressure = GetRampUpperPressure(rampIndex, usbDevice, usbHostGC);
01434     
01435             if(!columnMethodDataSet->GetDataPoint(pointCount, &X1, &Y1)) {
01436                 // We have run out of points in the column method
01437                 break;
01438             }
01439             
01440             AddDataPoint(X1, rampUpperPressure);
01441             totalMethodTime = X1;
01442             ++pointCount;
01443                 
01444             if(!columnMethodDataSet->GetDataPoint(pointCount, &X2, &Y2)) {
01445                 // We have run out of points in the column method
01446                 break;
01447             }
01448             
01449             if(Y2 == Y1) {
01450                 // Temperatures are the same - this is a non-zero holding interval
01451                 AddDataPoint(X2, rampUpperPressure);
01452                 totalMethodTime = X2;
01453                 ++pointCount;
01454             }
01455             // 'else' we are not holding here - do not add a second point - 
01456             // the second point here is actually the end of the next ramp,
01457             // so re-use it for the next ramp (don't increment pointCount, 
01458             // so we get the same data point again 'next time round') 
01459             // *** See comment [HOLDING INTERVAL] in SetupFromColumnTemperatureRampValues ***
01460         }
01461         
01462         nonRoundedTotalMethodTime = totalMethodTime;
01463     
01464     } else { 
01465     
01466         // Constant pressure mode
01467     
01468         nonRoundedTotalMethodTime = columnMethodDataSet->nonRoundedTotalMethodTime;
01469         
01470         // First point will be at the initial gas pressure, time zero.
01471         // Second point will be at the same pressure, at the end of the column method time.
01472     
01473         float initialPressure = (double) GetInitialPressure(usbDevice, usbHostGC);
01474         float methodDuration = columnMethodDataSet->GetTotalMethodTime();
01475         
01476         AddDataPoint(0, initialPressure);
01477         AddDataPoint(methodDuration, initialPressure);
01478         pointCount = 2;
01479     }
01480 
01481     return pointCount;
01482 }
01483     
01484 /*
01485     Returns the total time expected to be taken by the current method.
01486     (This is effectively the same as the X coordinate of the final datapoint.)
01487     
01488     Takes no arguments.
01489 */
01490 float GuiLibGraphDataSet::GetTotalMethodTime(void)
01491 {
01492     if(graphDataSetActualLength <= 0) {
01493         return 0.0f;
01494     }
01495     
01496     // 'else' we have some data
01497     float finalX;
01498     float finalY;
01499     if(GetDataPoint((graphDataSetActualLength - 1), &finalX, &finalY)) {
01500         return (finalX);
01501     }
01502     
01503     // 'else' GetDataPoint failed for some reason
01504     return 0.0f;
01505 }
01506     
01507 /*
01508     Returns the range of X coordinate values.
01509     
01510     Args: pointers to the variables to contain the minimum and maximum X values
01511           (these are integers, as required for an easyGUI graph)
01512           the time unit to be used (minutes or seconds)
01513     
01514     No return code.
01515 */
01516 void GuiLibGraphDataSet::GetXAxisRange(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax, TimeUnit timeUnit)
01517 {
01518     if(graphDataSetActualLength <= 0) {
01519         // We have no data
01520         *xMin = 0;
01521         *xMax = 0;
01522     
01523         return;
01524     }
01525     
01526     // 'else' we have some data
01527     float X1;
01528     float Y1;
01529     float X2;
01530     float Y2;
01531     GetDataPoint(0, &X1, &Y1);
01532     GetDataPoint((graphDataSetActualLength - 1), &X2, &Y2);
01533     
01534     if(timeUnit == SECONDS) {
01535         X1 *= 60.0f;
01536         X2 *= 60.0f;
01537     }
01538     
01539     *xMin = (GuiConst_INT32S) floor((double)X1 + 0.5);
01540     *xMax = (GuiConst_INT32S) floor((double)X2 + 0.5);
01541 }
01542 
01543 /*
01544     Returns the range of X coordinate values, in units of 0.1 minute.
01545     
01546     Args: pointers to the variables to contain the minimum and maximum X values
01547           (these are integers, as required for an easyGUI graph)
01548     
01549     No return code.
01550 */
01551 void GuiLibGraphDataSet::GetXAxisRangeInTenthsOfMinutes(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax)
01552 {
01553     if(graphDataSetActualLength <= 0) {
01554         // We have no data
01555         *xMin = 0;
01556         *xMax = 0;
01557     
01558         return;
01559     }
01560     
01561     // 'else' we have some data
01562     float X1;
01563     float Y1;
01564     float X2;
01565     float Y2;
01566     GetDataPoint(0, &X1, &Y1);
01567     GetDataPoint((graphDataSetActualLength - 1), &X2, &Y2);
01568     
01569     *xMin = (GuiConst_INT32S) (X1 * 10.0f);
01570     *xMax = (GuiConst_INT32S) (X2 * 10.0f);
01571 }
01572 
01573 /*
01574     Returns the range of X coordinate values - but rounded to be a whole number of 'ticks' 
01575     (i.e. the interval between marks on the X axis). 
01576     
01577     Args: pointers to the variables to contain the minimum and maximum X values
01578           the tick size
01579           the time unit
01580     
01581     No return code.
01582 */
01583 void GuiLibGraphDataSet::GetXAxisRange(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax, GuiConst_INT32S xTickSize, TimeUnit timeUnit)
01584 {
01585     GuiConst_INT32S rawXMin;
01586     GuiConst_INT32S rawXMax;
01587     
01588     GetXAxisRange(&rawXMin, &rawXMax, timeUnit);
01589     
01590     RoundAxisRangeByTickSize(xMin, xMax, rawXMin, rawXMax, xTickSize);
01591 }
01592 
01593 /*
01594     Returns the range of X coordinate values, in units of 0.1 minute - but rounded to be a whole number of 'ticks' 
01595     (i.e. the interval between marks on the X axis). 
01596     
01597     Args: pointers to the variables to contain the minimum and maximum X values
01598           the tick size
01599     
01600     No return code.
01601 */
01602 void GuiLibGraphDataSet::GetXAxisRangeInTenthsOfMinutes(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax, GuiConst_INT32S xTickSize)
01603 {
01604     GuiConst_INT32S rawXMin;
01605     GuiConst_INT32S rawXMax;
01606     
01607     GetXAxisRangeInTenthsOfMinutes(&rawXMin, &rawXMax);
01608     
01609     RoundAxisRangeByTickSize(xMin, xMax, rawXMin, rawXMax, xTickSize);
01610 }
01611 
01612 /*
01613     Returns the range of Y coordinate values.
01614     
01615     Args: pointers to the variables to contain the minimum and maximum Y values
01616           (these are integers, as required for an easyGUI graph)
01617     
01618     No return code.
01619 */
01620 void GuiLibGraphDataSet::GetYAxisRange(GuiConst_INT32S *yMin, GuiConst_INT32S *yMax)
01621 {
01622     if(graphDataSetActualLength <= 0) {
01623         // We have no data
01624         *yMin = 0;
01625         *yMax = 0;
01626     
01627         return;
01628     }
01629     
01630     // 'else' we have some data
01631     float X;
01632     float Y;
01633     float minYSoFar;
01634     float maxYSoFar;
01635     
01636     GetDataPoint(0, &X, &Y);
01637     minYSoFar = Y;
01638     maxYSoFar = Y;
01639     
01640     for (int pointIndex = 1; pointIndex < graphDataSetActualLength; ++pointIndex) {
01641         GetDataPoint(pointIndex, &X, &Y);
01642         
01643         if(Y < minYSoFar) { minYSoFar = Y; }
01644         if(Y > maxYSoFar) { maxYSoFar = Y; }
01645     }
01646     
01647     *yMin = (GuiConst_INT32S) floor((double)minYSoFar + 0.5);
01648     *yMax = (GuiConst_INT32S) floor((double)maxYSoFar + 0.5);
01649 }
01650 
01651 /*
01652     Returns the range of Y coordinate values - but rounded to be a whole number of 'ticks' 
01653     (i.e. the interval between marks on the Y axis)
01654     
01655     Args: pointers to the variables to contain the minimum and maximum X values
01656           the tick size
01657     
01658     No return code.
01659 */
01660 void GuiLibGraphDataSet::GetYAxisRange(GuiConst_INT32S *yMin, GuiConst_INT32S *yMax, GuiConst_INT32S yTickSize)
01661 {
01662     GuiConst_INT32S rawYMin;
01663     GuiConst_INT32S rawYMax;
01664     
01665     GetYAxisRange(&rawYMin, &rawYMax);
01666     
01667     RoundAxisRangeByTickSize(yMin, yMax, rawYMin, rawYMax, yTickSize);
01668 }
01669 
01670 /*
01671     Rounds an axis range to be a whole number of 'ticks' - i.e. the range starts at the last tick below the minimum value,
01672     and ends at the first tick above the maximum value.
01673     
01674     Args: pointers to the variables to contain the rounded minimum and maximum
01675           the 'raw' minimum and maximum values
01676           the tick size
01677 */
01678 void GuiLibGraphDataSet::RoundAxisRangeByTickSize(GuiConst_INT32S *axisMin, GuiConst_INT32S *axisMax, GuiConst_INT32S rawAxisMin, GuiConst_INT32S rawAxisMax, GuiConst_INT32S axisTickSize)
01679 {
01680     if(axisTickSize > 0) {
01681         float fAxisTickSize = (float) axisTickSize;
01682         
01683         float fRawAxisMin = (float) rawAxisMin;
01684         float axisTickCountMin = fRawAxisMin / fAxisTickSize;
01685         *axisMin = (GuiConst_INT32S) (fAxisTickSize * (floor (axisTickCountMin)));
01686         
01687         float fRawAxisMax = (float) rawAxisMax;
01688         float axisTickCountMax = fRawAxisMax / fAxisTickSize;
01689         *axisMax = (GuiConst_INT32S) (fAxisTickSize * (ceil (axisTickCountMax)));
01690     } else {
01691         *axisMin = rawAxisMin;
01692         *axisMax = rawAxisMax;
01693     }
01694 }
01695 
01696 
01697 /*
01698     Draws the profile directly to the display, using the GuiLib_VLine function, via the DrawProfileSectionUsingGuiLibVLine function defined in main.cpp.
01699     In general, this will be two colours, with a boundary part way across, to represent how much of the method we have executed, and how much remains.
01700           
01701     This is the private version of this function, which takes a boolean value stating whether or not 
01702     to record the parameter values. One public version takes explicit values, and tells this function to record them, 
01703     while the other takes no parameters, simply passing this function the previously recorded values
01704     (this is so that the caller does not have to keep re-calculating the same values, possibly in more than one place).
01705     
01706     Args: the display X coordinate at which to start
01707           the display Y coordinate at which to start
01708           the X and Y scale factors - i.e. the values you have to multiply our X and Y coordinates by, to get display coordinates
01709           the colour of the first section
01710           the colour of the second section
01711           the X coordinate (internal, not display) at which the second section starts. If this is < 0, we draw the entire profile in the first colour
01712           flag saying whether or not to record the parameters
01713           
01714     No return code
01715 */
01716 void GuiLibGraphDataSet::DrawUsingGuiLibVLine(GuiConst_INT16S xLeft, GuiConst_INT16S yBottom, double xScaleFactor, double yScaleFactor, 
01717                                               GuiConst_INTCOLOR firstColour, GuiConst_INTCOLOR secondColour, double xColourBoundary, bool recordParameters)
01718 {
01719     if(theGraphDataSet != NULL) {
01720         
01721         GuiConst_INT16S profileSectionLeft;
01722         GuiConst_INT16S profileSectionRight;
01723 
01724         GuiConst_INT16S profileSectionTopLeft;
01725         GuiConst_INT16S profileSectionTopRight;
01726         
01727         GuiConst_INT16S profileColourBoundary = xLeft + (GuiConst_INT16S)floor(xColourBoundary * xScaleFactor);
01728         
01729         for(int graphDataSetIndex = 0; graphDataSetIndex < (graphDataSetActualLength - 1); ++graphDataSetIndex) {
01730             
01731             profileSectionLeft  = xLeft + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex].X * xScaleFactor);
01732             profileSectionRight = xLeft + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex + 1].X * xScaleFactor);
01733                             
01734             profileSectionTopLeft  = yBottom + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex].Y * yScaleFactor);
01735             profileSectionTopRight = yBottom + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex + 1].Y * yScaleFactor);
01736 
01737             DrawProfileSectionUsingGuiLibVLine(firstColour, secondColour, profileSectionLeft, profileSectionRight, profileColourBoundary, yBottom, profileSectionTopLeft, profileSectionTopRight);
01738         }
01739     }
01740     
01741     if(recordParameters) {
01742         DrawUsingGuiLibVLineParameters.isSet           = true;
01743         DrawUsingGuiLibVLineParameters.xLeft           = xLeft; 
01744         DrawUsingGuiLibVLineParameters.yBottom         = yBottom; 
01745         DrawUsingGuiLibVLineParameters.xScaleFactor    = xScaleFactor;
01746         DrawUsingGuiLibVLineParameters.yScaleFactor    = yScaleFactor;
01747         DrawUsingGuiLibVLineParameters.firstColour     = firstColour;
01748         DrawUsingGuiLibVLineParameters.secondColour    = secondColour;
01749         DrawUsingGuiLibVLineParameters.xColourBoundary = xColourBoundary;
01750     }
01751     //&&&&&
01752 }
01753 
01754 /*
01755     Call the DrawUsingGuiLibVLine function (see above) with newly-calculated parameter values.
01756     
01757     Args: the display X coordinate at which to start
01758           the display Y coordinate at which to start
01759           the X and Y scale factors - i.e. the values you have to multiply our X and Y coordinates by, to get display coordinates
01760           the colour of the first section
01761           the colour of the second section
01762           the X coordinate (internal, not display) at which the second section starts. If this is < 0, we draw the entire profile in the first colour
01763 
01764     No return code.
01765 */
01766 void GuiLibGraphDataSet::DrawUsingGuiLibVLine(GuiConst_INT16S xLeft, GuiConst_INT16S yBottom, double xScaleFactor, double yScaleFactor, GuiConst_INTCOLOR firstColour, GuiConst_INTCOLOR secondColour, double xColourBoundary)
01767 {
01768     DrawUsingGuiLibVLine(xLeft,
01769                     yBottom,
01770                     xScaleFactor,
01771                     yScaleFactor,
01772                     firstColour,
01773                     secondColour,
01774                     xColourBoundary,
01775                     true );
01776 }
01777 
01778 /*
01779     Repeats the last call to the version of DrawUsingGuiLibVLine with parameters,
01780     using the same parameter values (if available). This is so that the caller 
01781     does not have to repeatedly calculate the same values in more than one place.
01782 */
01783 void GuiLibGraphDataSet::DrawUsingGuiLibVLine(void)
01784 {
01785     if(DrawUsingGuiLibVLineParameters.isSet) {
01786         DrawUsingGuiLibVLine(DrawUsingGuiLibVLineParameters.xLeft,
01787                         DrawUsingGuiLibVLineParameters.yBottom,
01788                         DrawUsingGuiLibVLineParameters.xScaleFactor,
01789                         DrawUsingGuiLibVLineParameters.yScaleFactor,
01790                         DrawUsingGuiLibVLineParameters.firstColour,
01791                         DrawUsingGuiLibVLineParameters.secondColour,
01792                         DrawUsingGuiLibVLineParameters.xColourBoundary,
01793                         false );
01794     }
01795     // else no values available - do nothing
01796 }
01797 
01798 
01799 
01800 /*
01801     GuiLibGraph constructor - the graph index must be specified by the caller,
01802     and must match the index number specified for the graph in easyGUI
01803     ******************************************************************
01804 */
01805 GuiLibGraph::GuiLibGraph(GuiConst_INT8U graphIndex)
01806 {
01807     GuiLib_GraphIndex = graphIndex;
01808     
01809     xAxisUnits = MINUTES;
01810     
01811     for(int i = 0; i < DATASET_INDEX_COUNT; ++i) {
01812         dataSetDataPtr[i] = NULL;
01813     }
01814     
01815     xAxisLabelData.isSet = false;
01816 }
01817 
01818 GuiLibGraph::~GuiLibGraph()
01819 {
01820     for(int i = 0; i < DATASET_INDEX_COUNT; ++i) {
01821         if(dataSetDataPtr[i] != NULL) {
01822             delete [] dataSetDataPtr[i];
01823         }
01824     }
01825 }
01826 
01827 
01828 void GuiLibGraph::SetXAxisUnits(TimeUnit newXAxisUnits)
01829 {
01830     xAxisUnits = newXAxisUnits;
01831 }
01832 
01833 TimeUnit GuiLibGraph::GetXAxisUnits(void)
01834 {
01835     return xAxisUnits;
01836 }
01837 
01838 
01839 /*
01840     Draws the axes for this graph.
01841     
01842     Encapsulates the easyGUI 'GuiLib_Graph_DrawAxes' function,
01843     passing it the index number specified for this graph
01844     
01845     Args: None (we already know the index number for this graph)
01846     
01847 */
01848 GuiConst_INT8U GuiLibGraph::DrawAxes(void)
01849 {
01850     if(xAxisUnits == SECONDS) {
01851         GuiLib_Graph_HideXAxis(GuiLib_GraphIndex, MINUTES_XAXIS_INDEX);
01852         GuiLib_Graph_ShowXAxis(GuiLib_GraphIndex, SECONDS_XAXIS_INDEX);
01853     } else {
01854         GuiLib_Graph_ShowXAxis(GuiLib_GraphIndex, MINUTES_XAXIS_INDEX);
01855         GuiLib_Graph_HideXAxis(GuiLib_GraphIndex, SECONDS_XAXIS_INDEX);
01856     }
01857     
01858     return GuiLib_Graph_DrawAxes(GuiLib_GraphIndex);
01859 }
01860 
01861 /*
01862     Since the units we are using for the time values (0.1 minute) cannot be displayed by the easyGUI graph object,
01863     which works only in integers, we are multiplying our x coordinates by 10 before passing them to the graph.
01864     This means that we cannot let the easyGUI graph label its own X axis, since the values will look incorrect
01865     to the user. We must therefore draw the values ourselves - which is what this function does.
01866           
01867     This is the private version of this function, which takes a boolean value stating whether or not 
01868     to record the parameter values. One public version takes explicit values, and tells this function to record them, 
01869     while the other takes no parameters, simply passing this function the previously recorded values
01870     (this is so that the caller does not have to keep re-calculating the same values, possibly in more than one place).
01871     
01872     Args: the start and end X axis values
01873           the tick size (i.e. the interval between successive values
01874           the X and Y coordinates of the graph
01875           the width of the graph
01876           boolean stating whether or not to record the parameter values
01877 */
01878 void GuiLibGraph::DrawXAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 
01879                      GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphW, bool recordParameters)
01880 {
01881     int intervalCount = (maxValue - minValue) / tickSize;
01882     
01883     GuiConst_INT16S xCoord = graphX + 10; // 10 is empirical - relative to the graph X coordinate, it puts the labels in the correct place
01884     GuiConst_INT16S xCoordInterval = graphW / intervalCount;
01885 
01886     GuiConst_INT16S yCoord = graphY + 15; // 15 is also empirical - relative to the graph Y coordinate, it puts the labels in the correct place
01887     
01888     const GuiConst_INT16U fontNo = GuiFont_Helv20Bold;
01889     char text[100];
01890         
01891     for(GuiConst_INT32S xValue = minValue; xValue <= maxValue; xValue += tickSize) {
01892         sprintf(text, "%d", xValue);
01893         
01894         GuiLib_DrawStr(
01895             xCoord,                    //GuiConst_INT16S X,
01896             yCoord,                    //GuiConst_INT16S Y,
01897             fontNo,                    //GuiConst_INT16U FontNo,
01898             text,                      //GuiConst_TEXT PrefixLocate *String,
01899             GuiLib_ALIGN_CENTER,       //GuiConst_INT8U Alignment, 
01900             GuiLib_PS_ON,              //GuiConst_INT8U PsWriting,
01901             GuiLib_TRANSPARENT_ON,     //GuiConst_INT8U Transparent,
01902             GuiLib_UNDERLINE_OFF,      //GuiConst_INT8U Underlining,
01903             0,                         //GuiConst_INT16S BackBoxSizeX,
01904             0,                         //GuiConst_INT16S BackBoxSizeY1,
01905             0,                         //GuiConst_INT16S BackBoxSizeY2,
01906             GuiLib_BBP_NONE,           //GuiConst_INT8U BackBorderPixels,
01907             0,                         //GuiConst_INTCOLOR ForeColor,
01908             0xFFFF                     //GuiConst_INTCOLOR BackColor
01909         ); 
01910         
01911         xCoord += xCoordInterval;
01912     }
01913     
01914     if(recordParameters) {
01915         xAxisLabelData.isSet = true;
01916         xAxisLabelData.minValue = minValue; 
01917         xAxisLabelData.maxValue = maxValue; 
01918         xAxisLabelData.tickSize = tickSize;
01919         xAxisLabelData.graphX   = graphX;
01920         xAxisLabelData.graphY   = graphY;
01921         xAxisLabelData.graphW   = graphW;
01922     }
01923 }
01924                      
01925 /*
01926     Call the DrawXAxisLabels function (see above) with newly-calculated parameter values.
01927     
01928     Args: the start and end X axis values
01929           the tick size (i.e. the interval between successive values
01930           the X and Y coordinates of the graph
01931           the width of the graph
01932 */
01933 void GuiLibGraph::DrawXAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 
01934                      GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphW)
01935 {
01936     DrawXAxisLabels(minValue,
01937                     maxValue,
01938                     tickSize,
01939                     graphX,
01940                     graphY,
01941                     graphW,
01942                     true );
01943 }
01944 
01945 /*
01946     Repeats the last call to the version of DrawXAxisLabels with parameters,
01947     using the same parameter values (if available). This is so that the caller 
01948     does not have to repeatedly calculate the same values in more than one place.
01949 */
01950 void GuiLibGraph::DrawXAxisLabels(void)
01951 {
01952     if(xAxisLabelData.isSet) {
01953         DrawXAxisLabels(xAxisLabelData.minValue,
01954                         xAxisLabelData.maxValue,
01955                         xAxisLabelData.tickSize,
01956                         xAxisLabelData.graphX,
01957                         xAxisLabelData.graphY,
01958                         xAxisLabelData.graphW,
01959                         false );
01960     }
01961     // else no values available - do nothing
01962 }
01963 
01964 
01965 /*
01966     Similarly to the X axis, we deliberately pass values that are larger than the real values to the Y axis.
01967     This is to avoid the 'stepped' appearance that the profile can have for methods with a small range of values in Y.
01968     This means that we cannot let the easyGUI graph label its own Y axis, since the values will look incorrect
01969     to the user. We must therefore draw the values ourselves - which is what this function does.
01970           
01971     This is the private version of this function, which takes a boolean value stating whether or not 
01972     to record the parameter values. One public version takes explicit values, and tells this function to record them, 
01973     while the other takes no parameters, simply passing this function the previously recorded values
01974     (this is so that the caller does not have to keep re-calculating the same values, possibly in more than one place).
01975     
01976     Args: the start and end Y axis values
01977           the tick size (i.e. the interval between successive values
01978           the X and Y coordinates of the graph
01979           the height of the graph
01980           boolean stating whether or not to record the parameter values
01981 */
01982 void GuiLibGraph::DrawYAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 
01983                      GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphH, bool recordParameters)
01984 {
01985     int intervalCount = (maxValue - minValue) / tickSize;
01986     
01987     GuiConst_INT16S xCoord = graphX; // GuiLib_ALIGN_RIGHT means that the text ends at the X coordinate
01988 
01989     GuiConst_INT16S yCoord = graphY - 2;  // 2 is also empirical - relative to the graph Y coordinate, it puts the labels in the correct place
01990     GuiConst_INT16S yCoordInterval = (graphH / intervalCount) - 2; // 2 is also empirical - otherwise, the labels get out of step with the Y axis ticks as they go up the graph
01991     
01992     const GuiConst_INT16U fontNo = GuiFont_Helv20Bold;
01993     char text[100];
01994         
01995     for(GuiConst_INT32S xValue = minValue; xValue <= maxValue; xValue += tickSize) {
01996         sprintf(text, "%d", xValue);
01997         
01998         GuiLib_DrawStr(
01999             xCoord,                    //GuiConst_INT16S X,
02000             yCoord,                    //GuiConst_INT16S Y,
02001             fontNo,                    //GuiConst_INT16U FontNo,
02002             text,                      //GuiConst_TEXT PrefixLocate *String,
02003             GuiLib_ALIGN_RIGHT,        //GuiConst_INT8U Alignment, 
02004             GuiLib_PS_ON,              //GuiConst_INT8U PsWriting,
02005             GuiLib_TRANSPARENT_ON,     //GuiConst_INT8U Transparent,
02006             GuiLib_UNDERLINE_OFF,      //GuiConst_INT8U Underlining,
02007             0,                         //GuiConst_INT16S BackBoxSizeX,
02008             0,                         //GuiConst_INT16S BackBoxSizeY1,
02009             0,                         //GuiConst_INT16S BackBoxSizeY2,
02010             GuiLib_BBP_NONE,           //GuiConst_INT8U BackBorderPixels,
02011             0,                         //GuiConst_INTCOLOR ForeColor,
02012             0xFFFF                     //GuiConst_INTCOLOR BackColor
02013         ); 
02014         
02015         yCoord -= yCoordInterval;
02016     }
02017     
02018     if(recordParameters) {
02019         yAxisLabelData.isSet = true;
02020         yAxisLabelData.minValue = minValue; 
02021         yAxisLabelData.maxValue = maxValue; 
02022         yAxisLabelData.tickSize = tickSize;
02023         yAxisLabelData.graphX   = graphX;
02024         yAxisLabelData.graphY   = graphY;
02025         yAxisLabelData.graphH   = graphH;
02026     }
02027 }
02028                      
02029 /*
02030     Call the DrawYAxisLabels function (see above) with newly-calculated parameter values.
02031     
02032     Args: the start and end Y axis values
02033           the tick size (i.e. the interval between successive values
02034           the X and Y coordinates of the graph
02035           the height of the graph
02036 */
02037 void GuiLibGraph::DrawYAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 
02038                      GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphH)
02039 {
02040     DrawYAxisLabels(minValue,
02041                     maxValue,
02042                     tickSize,
02043                     graphX,
02044                     graphY,
02045                     graphH,
02046                     true );
02047 }
02048 
02049 /*
02050     Repeats the last call to the version of DrawYAxisLabels with parameters,
02051     using the same parameter values (if available). This is so that the caller 
02052     does not have to repeatedly calculate the same values in more than one place.
02053 */
02054 void GuiLibGraph::DrawYAxisLabels(void)
02055 {
02056     if(yAxisLabelData.isSet) {
02057         DrawYAxisLabels(yAxisLabelData.minValue,
02058                         yAxisLabelData.maxValue,
02059                         yAxisLabelData.tickSize,
02060                         yAxisLabelData.graphX,
02061                         yAxisLabelData.graphY,
02062                         yAxisLabelData.graphH,
02063                         false );
02064     }
02065     // else no values available - do nothing
02066 }
02067 
02068 
02069 /*
02070     Draws the specified data set for this graph.
02071     
02072     Encapsulates the easyGUI 'GuiLib_Graph_DrawDataSet' function,
02073     passing it the index number specified for this graph,
02074     and the data set index number given by the caller.
02075     
02076     Note that a data set with that index must already have been created 
02077     in easyGUI for this graph.
02078     
02079     Args: index number of the data set
02080     
02081 */
02082 GuiConst_INT8U GuiLibGraph::DrawDataSet(GuiConst_INT8U dataSetIndex)
02083 {
02084     return GuiLib_Graph_DrawDataSet(GuiLib_GraphIndex, dataSetIndex);
02085 }
02086 
02087 /*
02088     Draws the specified data point in the specified data set for this graph.
02089     
02090     Encapsulates the easyGUI 'GuiLib_Graph_DrawDataPoint' function,
02091     passing it the index number specified for this graph,
02092     and the data set and data point index numbers given by the caller.
02093     
02094     Note that a data set with that index must already have been created 
02095     in easyGUI for this graph.
02096     
02097     Args: index number of the data set
02098           index number of the data point in that set
02099     
02100 */
02101 GuiConst_INT8U GuiLibGraph::DrawDataPoint(GuiConst_INT8U dataSetIndex, GuiConst_INT16U dataPointIndex)
02102 {
02103     return GuiLib_Graph_DrawDataPoint(GuiLib_GraphIndex, dataSetIndex, dataPointIndex);
02104 }
02105 
02106 /*
02107     Shows the specified data set for this graph.
02108     
02109     Encapsulates the easyGUI 'GuiLib_Graph_ShowDataSet' function,
02110     passing it the index number specified for this graph,
02111     and the data set index number given by the caller.
02112     
02113     Note that a data set with that index must already have been created 
02114     in easyGUI for this graph.
02115 
02116     Args: index number of the data set
02117     
02118 */
02119 GuiConst_INT8U GuiLibGraph::ShowDataSet(GuiConst_INT8U dataSetIndex)
02120 {
02121     return GuiLib_Graph_ShowDataSet(GuiLib_GraphIndex, dataSetIndex);
02122 }
02123 
02124 /*
02125     Hides the specified data set for this graph.
02126     
02127     Encapsulates the easyGUI 'GuiLib_Graph_HideDataSet' function,
02128     passing it the index number specified for this graph,
02129     and the data set index number given by the caller.
02130     
02131     Note that a data set with that index must already have been created 
02132     in easyGUI for this graph.
02133 
02134     Args: index number of the data set
02135     
02136 */
02137 GuiConst_INT8U GuiLibGraph::HideDataSet(GuiConst_INT8U dataSetIndex)
02138 {
02139     return GuiLib_Graph_HideDataSet(GuiLib_GraphIndex, dataSetIndex);
02140 }
02141 
02142 /*
02143     Sets the range of the X axis for this graph.
02144     
02145     Encapsulates the easyGUI 'GuiLib_Graph_SetXAxisRange' function,
02146     passing it the index number specified for this graph, 
02147     the X axis index number (this depends on the selected units), 
02148     and the minimum and maximum values given by the caller.
02149     
02150     Args: minimum value for the range
02151           maximum value for the range
02152         
02153 */
02154 GuiConst_INT8U GuiLibGraph::SetXAxisRange(GuiConst_INT32S minValue, GuiConst_INT32S maxValue)
02155 {
02156 #define BOTH_AT_ONCE
02157 #ifdef BOTH_AT_ONCE
02158     // The graph coordinates seem to relate to X axis zero, regardless of which one we are actually displaying -
02159     // so set both at once
02160     if(GuiLib_Graph_SetXAxisRange(GuiLib_GraphIndex, MINUTES_XAXIS_INDEX, minValue, maxValue) == 0) {
02161         // Failed
02162         return 0;
02163     }
02164     
02165     // 'else' OK
02166     return GuiLib_Graph_SetXAxisRange(GuiLib_GraphIndex, SECONDS_XAXIS_INDEX, minValue, maxValue);
02167 #else
02168     GuiConst_INT8U xAxisIndex = (xAxisUnits == SECONDS) ? SECONDS_XAXIS_INDEX : MINUTES_XAXIS_INDEX;
02169     
02170     return GuiLib_Graph_SetXAxisRange(GuiLib_GraphIndex, xAxisIndex, minValue, maxValue);
02171 #endif
02172 }
02173 
02174 /*
02175     Sets the range of the Y axis for this graph.
02176     
02177     Encapsulates the easyGUI 'GuiLib_Graph_SetYAxisRange' function,
02178     passing it the index number specified for this graph, 
02179     the Y axis index number (currently always zero), 
02180     and the minimum and maximum values for its range given by the caller.
02181     
02182     Args: minimum value for the range
02183           maximum value for the range
02184         
02185 */
02186 GuiConst_INT8U GuiLibGraph::SetYAxisRange(GuiConst_INT32S minValue, GuiConst_INT32S maxValue)
02187 {
02188     GuiConst_INT8U yAxisIndex = YAXIS_INDEX;
02189     
02190     return GuiLib_Graph_SetYAxisRange(GuiLib_GraphIndex, yAxisIndex, minValue, maxValue);
02191 }
02192 
02193 /*
02194     Sets the data for the specified data set.
02195     
02196     Args: the dataset index (this must already have been created in easyGUI)
02197           (a pointer to) a GuiLibGraphDataSet object containing the new data
02198           the time unit (minutes or seconds) to be used
02199           
02200     Note that we reject dataset index values outside the range 0-9
02201     (since we need to keep a pointer to the memory used, and currently 
02202      we only allow a fixed number of them).
02203           
02204 */
02205 GuiConst_INT8U GuiLibGraph::SetDataForGraphDataSet(GuiConst_INT8U dataSetIndex, GuiLibGraphDataSet* dataSet, TimeUnit timeUnit)
02206 {
02207     /*
02208         This is a copy of the declaration of 'GuiLib_Graph_AddDataSet' in GuiLib.h:
02209         
02210         extern GuiConst_INT8U GuiLib_Graph_AddDataSet(
02211                                            GuiConst_INT8U GraphIndex,
02212                                            GuiConst_INT8U DataSetIndex,
02213                                            GuiConst_INT8U XAxisIndex,
02214                                            GuiConst_INT8U YAxisIndex,
02215                                            GuiLib_GraphDataPoint *DataPtr,
02216                                            GuiConst_INT16U DataSize,
02217                                            GuiConst_INT16U DataCount,
02218                                            GuiConst_INT16U DataFirst);
02219                                            
02220         We supply either default values for these parameters,
02221         or get them from the GuiLibGraphDataSet object.
02222     */    
02223     
02224     if(dataSetIndex < DATASET_INDEX_COUNT) {
02225         // Make sure this data set, on this graph, has only the data we are about to 'add' to it -
02226         // get rid of any existing data
02227         GuiLib_Graph_RemoveDataSet(GuiLib_GraphIndex, dataSetIndex);
02228     
02229         if(dataSetDataPtr[dataSetIndex] != NULL) {
02230             delete [] dataSetDataPtr[dataSetIndex];
02231         }
02232     
02233         GuiConst_INT16U dataSize;
02234         GuiLib_GraphDataPoint* dataCopy = dataSet->GetGraphDataPointCopy(timeUnit, &dataSize); 
02235         
02236         if(dataCopy != NULL) {
02237             // Although the data is passed to us in an array allocated by the GuiLibGraphDataSet object,
02238             // we keep a pointer to it in our own array, so that we can delete it when it is no longer required.
02239             dataSetDataPtr[dataSetIndex] = dataCopy;
02240             
02241             // I think this easyGUI function has a misleading name - you cannot 'add' a dataset with this function - 
02242             // you can only specify the data for a dataset that already exists
02243             return GuiLib_Graph_AddDataSet(GuiLib_GraphIndex, dataSetIndex, 0, 0, dataSetDataPtr[dataSetIndex], dataSize, dataSize, 0);
02244         }
02245         // 'else' copy failed - drop through to error return
02246     }
02247     
02248     // 'else' dataset index out of range, or copy failed (see above)
02249     return 0;
02250 }
02251 
02252 
02253 /*
02254     Sets the data for the specified data set, using tenths of minutes as the X axis unit
02255     
02256     Args: the dataset index (this must already have been created in easyGUI)
02257           (a pointer to) a GuiLibGraphDataSet object containing the new data
02258           
02259     Note that we reject dataset index values outside the range 0-9
02260     (since we need to keep a pointer to the memory used, and currently 
02261      we only allow a fixed number of them).
02262           
02263 */
02264 GuiConst_INT8U GuiLibGraph::SetDataForGraphDataSetInTenthsOfMinutes(GuiConst_INT8U dataSetIndex, float yAxisScaleFactor, GuiLibGraphDataSet* dataSet)
02265 {
02266     /*
02267         This is a copy of the declaration of 'GuiLib_Graph_AddDataSet' in GuiLib.h:
02268         
02269         extern GuiConst_INT8U GuiLib_Graph_AddDataSet(
02270                                            GuiConst_INT8U GraphIndex,
02271                                            GuiConst_INT8U DataSetIndex,
02272                                            GuiConst_INT8U XAxisIndex,
02273                                            GuiConst_INT8U YAxisIndex,
02274                                            GuiLib_GraphDataPoint *DataPtr,
02275                                            GuiConst_INT16U DataSize,
02276                                            GuiConst_INT16U DataCount,
02277                                            GuiConst_INT16U DataFirst);
02278                                            
02279         We supply either default values for these parameters,
02280         or get them from the GuiLibGraphDataSet object.
02281     */    
02282     
02283     if(dataSetIndex < DATASET_INDEX_COUNT) {
02284         // Make sure this data set, on this graph, has only the data we are about to 'add' to it -
02285         // get rid of any existing data
02286         GuiLib_Graph_RemoveDataSet(GuiLib_GraphIndex, dataSetIndex);
02287     
02288         if(dataSetDataPtr[dataSetIndex] != NULL) {
02289             delete [] dataSetDataPtr[dataSetIndex];
02290         }
02291     
02292         GuiConst_INT16U dataSize;
02293         GuiLib_GraphDataPoint* dataCopy = dataSet->GetGraphDataPointCopyInTenthsOfMinutes(yAxisScaleFactor, &dataSize); 
02294         
02295         if(dataCopy != NULL) {
02296             // Although the data is passed to us in an array allocated by the GuiLibGraphDataSet object,
02297             // we keep a pointer to it in our own array, so that we can delete it when it is no longer required.
02298             dataSetDataPtr[dataSetIndex] = dataCopy;
02299             
02300             // I think this easyGUI function has a misleading name - you cannot 'add' a dataset with this function - 
02301             // you can only specify the data for a dataset that already exists
02302             return GuiLib_Graph_AddDataSet(GuiLib_GraphIndex, dataSetIndex, 0, 0, dataSetDataPtr[dataSetIndex], dataSize, dataSize, 0);
02303         }
02304         // 'else' copy failed - drop through to error return
02305     }
02306     
02307     // 'else' dataset index out of range, or copy failed (see above)
02308     return 0;
02309 }
02310 
02311 
02312 /*
02313     Sets the specified data set to consist of a single point.
02314     
02315     Args: the dataset index (this must already have been created in easyGUI)
02316           the X coordinate of the point
02317           the Y coordinate of the point
02318 */
02319 GuiConst_INT8U GuiLibGraph::SetSinglePointForGraphDataSet(GuiConst_INT8U dataSetIndex, GuiConst_INT32S X, GuiConst_INT32S Y)
02320 {
02321 //    return GuiLib_Graph_AddDataPoint(GuiLib_GraphIndex, dataSetIndex, X, Y);
02322 
02323 /*
02324         This is a copy of the declaration of 'GuiLib_Graph_AddDataSet' in GuiLib.h:
02325         
02326         extern GuiConst_INT8U GuiLib_Graph_AddDataSet(
02327                                            GuiConst_INT8U GraphIndex,
02328                                            GuiConst_INT8U DataSetIndex,
02329                                            GuiConst_INT8U XAxisIndex,
02330                                            GuiConst_INT8U YAxisIndex,
02331                                            GuiLib_GraphDataPoint *DataPtr,
02332                                            GuiConst_INT16U DataSize,
02333                                            GuiConst_INT16U DataCount,
02334                                            GuiConst_INT16U DataFirst);
02335                                            
02336         We supply either default values for these parameters,
02337         or get them from the values passed to us.
02338 */
02339     GuiLib_GraphDataPoint dataPoint;
02340     dataPoint.X = X;
02341     dataPoint.Y = Y;
02342 
02343     // I think this easyGUI function has a misleading name - you cannot 'add' a dataset with this function - 
02344     // you can only specify the data for a dataset that already exists
02345     return GuiLib_Graph_AddDataSet(GuiLib_GraphIndex, dataSetIndex, 0, 0, &dataPoint, 10, 1, 0);
02346 }
02347 
02348 /*
02349     Redraws this graph (by calling the easyGUI GuiLib_Graph_Redraw function).
02350     This redraws the entire graph, including background, axes and data sets.
02351 */
02352 GuiConst_INT8U GuiLibGraph::Redraw(void)
02353 {
02354     return GuiLib_Graph_Redraw(GuiLib_GraphIndex);
02355 }