diff --git a/applications/foobot/foobot_utility.pas b/applications/foobot/foobot_utility.pas index 4363b306c..643790075 100644 --- a/applications/foobot/foobot_utility.pas +++ b/applications/foobot/foobot_utility.pas @@ -41,10 +41,26 @@ const FOOBOT_DATA_START_FINISH_URL = 'https://api.foobot.io/v2/device/%s/datapoint/%s/%s/%s/'; HIGHLOWMAX = 6; + C_TIME = 0; + C_PM = 1; + C_TMP = 2; + C_HUM = 3; + C_CO2 = 4; + C_VOC = 5; + C_ALLPOLLU = 6; + C_NONE = 7; type TDataFetchType = (dfLast, dfStartEnd); TSensorType = (st_time, st_pm, st_tmp, st_hum, st_co2, st_voc, st_allpollu); + TAlertType = (at_high, at_low); + + TAlertRec = record + AlertTriggered: boolean; + AlertTime: TDateTime; + AlertType: TAlertType; + AlertValue: variant; + end; function EncodeStringBase64(const s: string): string; function FetchAuthenticationKey(aUsername, aUserPassword: string): boolean; @@ -62,6 +78,7 @@ function FetchFoobotData(DataFetchType: TDataFetchType = dfLast; // - also populates HighLow arrays function FoobotDataObjectToArrays: boolean; + // Utility functions function ResetArrays: boolean; function ResetObjects: boolean; @@ -70,15 +87,24 @@ function SaveHighLows: boolean; function LoadHighLows: boolean; var + // Used to fetch server data HttpClient: TFPHTTPClient; + // Holds identity values for multiple Foobots FoobotIdentityObject: TFoobotIdentityObject; + // Holds data for current Foobot FoobotDataObject: TFoobotDataObject; + sAuthenticationKey: string; - SensorType: TSensorType; + SensorType: TSensorType; // st_time, st_pm, st_tmp etc. + // Boolean to enable/disable serialisation of HighLows. Default = TRUE SaveLoadHighLows: boolean; + // Boolean to enable/disable Trigger functions + UseTriggers: boolean; + // Used in data fetch TheCurrentFoobot: integer; + // INIFile located in GetAppConfig(false) folder HLINI: TIniFile; - // Easier access to datapoints + // Dynamic arrays - easier access to datapoints // Call FoobotDataObjectToArrays to populate them FoobotData_time: array of TDateTime; FoobotData_pm: array of double; @@ -93,9 +119,17 @@ var FoobotDataHighTimes: array[0..HIGHLOWMAX] of variant; FoobotDataLowTimes: array[0..HIGHLOWMAX] of variant; + // [0=Low(at_low)..1=High(at_high), C_PM..C_ALLPOLLU] + // Dynamic in case of future changes (e.g. more triggers) + FooBotTriggerArray: array of array of variant; + // Set by FoobotDataObjectToArrays + AlertRec: array [C_TIME..C_ALLPOLLU] of TAlertRec; + giCount: integer; // used in initialization + implementation function SaveHighLows: boolean; + // Save values to an INI data file var sFoobotName: string; begin @@ -109,47 +143,50 @@ begin HLINI.WriteString('Foobot', 'CurrentFoobotName', sFoobotName); // Particulates - HLINI.WriteFloat(sFoobotName, 'pmHigh', double(FoobotDataHighs[1])); - HLINI.WriteDateTime(sFoobotName, 'pmHighTime', TDateTime(FoobotDataHighTimes[1])); - HLINI.WriteFloat(sFoobotName, 'pmLow', double(FoobotDataLows[1])); - HLINI.WriteDateTime(sFoobotName, 'pmLowTime', TDateTime(FoobotDataLowTimes[1])); + HLINI.WriteFloat(sFoobotName, 'pmHigh', double(FoobotDataHighs[C_PM])); + HLINI.WriteDateTime(sFoobotName, 'pmHighTime', TDateTime(FoobotDataHighTimes[C_PM])); + HLINI.WriteFloat(sFoobotName, 'pmLow', double(FoobotDataLows[C_PM])); + HLINI.WriteDateTime(sFoobotName, 'pmLowTime', TDateTime(FoobotDataLowTimes[C_PM])); // Temp - HLINI.WriteFloat(sFoobotName, 'tmpHigh', double(FoobotDataHighs[2])); - HLINI.WriteDateTime(sFoobotName, 'tmpHighTime', TDateTime(FoobotDataHighTimes[2])); - HLINI.WriteFloat(sFoobotName, 'tmpLow', double(FoobotDataLows[2])); - HLINI.WriteDateTime(sFoobotName, 'tmpLowTime', TDateTime(FoobotDataLowTimes[2])); + HLINI.WriteFloat(sFoobotName, 'tmpHigh', double(FoobotDataHighs[C_TMP])); + HLINI.WriteDateTime(sFoobotName, 'tmpHighTime', TDateTime(FoobotDataHighTimes[C_TMP])); + HLINI.WriteFloat(sFoobotName, 'tmpLow', double(FoobotDataLows[C_TMP])); + HLINI.WriteDateTime(sFoobotName, 'tmpLowTime', TDateTime(FoobotDataLowTimes[C_TMP])); // Humidity - HLINI.WriteFloat(sFoobotName, 'humHigh', double(FoobotDataHighs[3])); - HLINI.WriteDateTime(sFoobotName, 'humHighTime', TDateTime(FoobotDataHighTimes[3])); - HLINI.WriteFloat(sFoobotName, 'humLow', double(FoobotDataLows[3])); - HLINI.WriteDateTime(sFoobotName, 'humLowTime', TDateTime(FoobotDataLowTimes[3])); + HLINI.WriteFloat(sFoobotName, 'humHigh', double(FoobotDataHighs[C_HUM])); + HLINI.WriteDateTime(sFoobotName, 'humHighTime', TDateTime(FoobotDataHighTimes[C_HUM])); + HLINI.WriteFloat(sFoobotName, 'humLow', double(FoobotDataLows[C_HUM])); + HLINI.WriteDateTime(sFoobotName, 'humLowTime', TDateTime(FoobotDataLowTimes[C_HUM])); // CO2 - HLINI.WriteInteger(sFoobotName, 'co2High', integer(FoobotDataHighs[4])); - HLINI.WriteDateTime(sFoobotName, 'co2HighTime', TDateTime(FoobotDataHighTimes[4])); - HLINI.WriteInteger(sFoobotName, 'co2Low', integer(FoobotDataLows[4])); - HLINI.WriteDateTime(sFoobotName, 'co2LowTime', TDateTime(FoobotDataLowTimes[4])); + HLINI.WriteInteger(sFoobotName, 'co2High', integer(FoobotDataHighs[C_CO2])); + HLINI.WriteDateTime(sFoobotName, 'co2HighTime', TDateTime(FoobotDataHighTimes[C_CO2])); + HLINI.WriteInteger(sFoobotName, 'co2Low', integer(FoobotDataLows[C_CO2])); + HLINI.WriteDateTime(sFoobotName, 'co2LowTime', TDateTime(FoobotDataLowTimes[C_CO2])); // Volatile Compounds - HLINI.WriteInteger(sFoobotName, 'vocHigh', integer(FoobotDataHighs[5])); - HLINI.WriteDateTime(sFoobotName, 'vocHighTime', TDateTime(FoobotDataHighTimes[5])); - HLINI.WriteInteger(sFoobotName, 'vocLow', integer(FoobotDataLows[5])); - HLINI.WriteDateTime(sFoobotName, 'vocLowTime', TDateTime(FoobotDataLowTimes[5])); + HLINI.WriteInteger(sFoobotName, 'vocHigh', integer(FoobotDataHighs[C_VOC])); + HLINI.WriteDateTime(sFoobotName, 'vocHighTime', TDateTime(FoobotDataHighTimes[C_VOC])); + HLINI.WriteInteger(sFoobotName, 'vocLow', integer(FoobotDataLows[C_VOC])); + HLINI.WriteDateTime(sFoobotName, 'vocLowTime', TDateTime(FoobotDataLowTimes[C_VOC])); // All Pollution - HLINI.WriteFloat(sFoobotName, 'allpolluHigh', double(FoobotDataHighs[6])); - HLINI.WriteDateTime(sFoobotName, 'allpolluHighTime', TDateTime(FoobotDataHighTimes[6])); - HLINI.WriteFloat(sFoobotName, 'allpolluLow', double(FoobotDataLows[6])); - HLINI.WriteDateTime(sFoobotName, 'allpolluLowTime', TDateTime(FoobotDataLowTimes[6])); - Result:=TRUE; + HLINI.WriteFloat(sFoobotName, 'allpolluHigh', double(FoobotDataHighs[C_ALLPOLLU])); + HLINI.WriteDateTime(sFoobotName, 'allpolluHighTime', + TDateTime(FoobotDataHighTimes[C_ALLPOLLU])); + HLINI.WriteFloat(sFoobotName, 'allpolluLow', double(FoobotDataLows[C_ALLPOLLU])); + HLINI.WriteDateTime(sFoobotName, 'allpolluLowTime', + TDateTime(FoobotDataLowTimes[C_ALLPOLLU])); + Result := True; end; function LoadHighLows: boolean; + // Load values from an INI data file var sFoobotName: string; begin if SaveLoadHighLows = False then - begin - ShowMessage('Unable to load All-Time stats'); - Exit(False); - end; + begin + ShowMessage('Unable to load All-Time stats'); + Exit(False); + end; sFoobotName := FoobotIdentityObject.FoobotIdentityList[TheCurrentFoobot].Name; if not Assigned(HLINI) then HLINI := TIniFile.Create(ChangeFileExt(GetAppConfigFile(False), '.ini')); @@ -158,47 +195,55 @@ begin Exit(False); // Particulates - FoobotDataHighs[1] := HLINI.ReadFloat(sFoobotName, 'pmHigh', 0); - FoobotDataHighTimes[1] := HLINI.ReadDateTime(sFoobotName, 'pmHighTime', Now); - FoobotDataLows[1] := HLINI.ReadFloat(sFoobotName, 'pmLow', 0); - FoobotDataLowTimes[1] := HLINI.ReadDateTime(sFoobotName, 'pmLowTime', Now); + FoobotDataHighs[C_PM] := HLINI.ReadFloat(sFoobotName, 'pmHigh', 0); + FoobotDataHighTimes[C_PM] := HLINI.ReadDateTime(sFoobotName, 'pmHighTime', Now); + FoobotDataLows[C_PM] := HLINI.ReadFloat(sFoobotName, 'pmLow', 0); + FoobotDataLowTimes[C_PM] := HLINI.ReadDateTime(sFoobotName, 'pmLowTime', Now); // Temp - FoobotDataHighs[2] := HLINI.ReadFloat(sFoobotName, 'tmpHigh', 0); - FoobotDataHighTimes[2] := HLINI.ReadDateTime(sFoobotName, 'tmpHighTime', Now); - FoobotDataLows[2] := HLINI.ReadFloat(sFoobotName, 'tmpLow', 0); - FoobotDataLowTimes[2] := HLINI.ReadDateTime(sFoobotName, 'tmpLowTime', Now); + FoobotDataHighs[C_TMP] := HLINI.ReadFloat(sFoobotName, 'tmpHigh', 0); + FoobotDataHighTimes[C_TMP] := HLINI.ReadDateTime(sFoobotName, 'tmpHighTime', Now); + FoobotDataLows[C_TMP] := HLINI.ReadFloat(sFoobotName, 'tmpLow', 0); + FoobotDataLowTimes[C_TMP] := HLINI.ReadDateTime(sFoobotName, 'tmpLowTime', Now); // Humidity - FoobotDataHighs[3] := HLINI.ReadFloat(sFoobotName, 'humHigh', 0); - FoobotDataHighTimes[3] := HLINI.ReadDateTime(sFoobotName, 'humHighTime', Now); - FoobotDataLows[3] := HLINI.ReadFloat(sFoobotName, 'humLow', 0); - FoobotDataLowTimes[3] := HLINI.ReadDateTime(sFoobotName, 'humLowTime', Now); + FoobotDataHighs[C_HUM] := HLINI.ReadFloat(sFoobotName, 'humHigh', 0); + FoobotDataHighTimes[C_HUM] := HLINI.ReadDateTime(sFoobotName, 'humHighTime', Now); + FoobotDataLows[C_HUM] := HLINI.ReadFloat(sFoobotName, 'humLow', 0); + FoobotDataLowTimes[C_HUM] := HLINI.ReadDateTime(sFoobotName, 'humLowTime', Now); // CO2 - FoobotDataHighs[4] := HLINI.ReadInteger(sFoobotName, 'co2High', 0); - FoobotDataHighTimes[4] := HLINI.ReadDateTime(sFoobotName, 'co2HighTime', Now); - FoobotDataLows[4] := HLINI.ReadInteger(sFoobotName, 'co2Low', 0); - FoobotDataLowTimes[4] := HLINI.ReadDateTime(sFoobotName, 'co2LowTime', Now); + FoobotDataHighs[C_CO2] := HLINI.ReadInteger(sFoobotName, 'co2High', 0); + FoobotDataHighTimes[C_CO2] := HLINI.ReadDateTime(sFoobotName, 'co2HighTime', Now); + FoobotDataLows[C_CO2] := HLINI.ReadInteger(sFoobotName, 'co2Low', 0); + FoobotDataLowTimes[C_CO2] := HLINI.ReadDateTime(sFoobotName, 'co2LowTime', Now); // Volatile Compounds - FoobotDataHighs[5] := HLINI.ReadInteger(sFoobotName, 'vocHigh', 0); - FoobotDataHighTimes[5] := HLINI.ReadDateTime(sFoobotName, 'vocHighTime', Now); - FoobotDataLows[5] := HLINI.ReadInteger(sFoobotName, 'vocLow', 0); - FoobotDataLowTimes[5] := HLINI.ReadDateTime(sFoobotName, 'vocLowTime', Now); + FoobotDataHighs[C_VOC] := HLINI.ReadInteger(sFoobotName, 'vocHigh', 0); + FoobotDataHighTimes[C_VOC] := HLINI.ReadDateTime(sFoobotName, 'vocHighTime', Now); + FoobotDataLows[C_VOC] := HLINI.ReadInteger(sFoobotName, 'vocLow', 0); + FoobotDataLowTimes[C_VOC] := HLINI.ReadDateTime(sFoobotName, 'vocLowTime', Now); // All Pollution - FoobotDataHighs[6] := HLINI.ReadFloat(sFoobotName, 'allpolluHigh', 0); - FoobotDataHighTimes[6] := HLINI.ReadDateTime(sFoobotName, 'allpolluHighTime', Now); - FoobotDataLows[6] := HLINI.ReadFloat(sFoobotName, 'allpolluLow', 0); - FoobotDataLowTimes[6] := HLINI.ReadDateTime(sFoobotName, 'allpolluLowTime', Now); - Result:=TRUE; + FoobotDataHighs[C_ALLPOLLU] := HLINI.ReadFloat(sFoobotName, 'allpolluHigh', 0); + FoobotDataHighTimes[C_ALLPOLLU] := + HLINI.ReadDateTime(sFoobotName, 'allpolluHighTime', Now); + FoobotDataLows[C_ALLPOLLU] := HLINI.ReadFloat(sFoobotName, 'allpolluLow', 0); + FoobotDataLowTimes[C_ALLPOLLU] := + HLINI.ReadDateTime(sFoobotName, 'allpolluLowTime', Now); + Result := True; end; -// ToDo: Multiple Foobots? +// Function to make the Foobot data accessible +// Also sets the HighLow array values +// Also sets triggers +{ +TAlertRec = Record + Triggered:Boolean; + AlertTime:TDateTime; + AlertType:TAlertType; + AlertValue:Variant; +end; +} function FoobotDataObjectToArrays: boolean; var J, K: integer; Mydatapoint: variant; - { - dtDate, dtStart, dtEnd: TDateTime; - sStart, sEnd: string; - } iUnixSecs: int64; // ========= Internal routines start =========== procedure SetHigh(iMember: integer; aValue: variant; aDateTime: TDateTime); @@ -207,6 +252,21 @@ var begin FoobotDataHighs[iMember] := aValue; FoobotDataHighTimes[iMember] := aDateTime; + SaveHighLows; + end; + if ((UseTriggers = True) and (FooBotTriggerArray[1, iMember] <> 0)) then + begin + // Process High Trigger + // Sets AlertRec record + if (aValue > FooBotTriggerArray[1, iMember]) then + begin + AlertRec[iMember].AlertTriggered := True; + AlertRec[iMember].AlertTime := aDateTime; + AlertRec[iMember].AlertType := at_high; + AlertRec[iMember].AlertValue := aValue; + end + else + AlertRec[iMember].AlertTriggered := False; end; end; @@ -216,6 +276,21 @@ var begin FoobotDataLows[iMember] := aValue; FoobotDataLowTimes[iMember] := aDateTime; + SaveHighLows; + end; + if ((UseTriggers = True) and (FooBotTriggerArray[0, iMember] <> 0)) then + begin + // Process Low Trigger + // Sets AlertRec record + if (aValue < FooBotTriggerArray[1, iMember]) then + begin + AlertRec[iMember].AlertTriggered := True; + AlertRec[iMember].AlertTime := aDateTime; + AlertRec[iMember].AlertType := at_low; + AlertRec[iMember].AlertValue := aValue; + end + else + AlertRec[iMember].AlertTriggered := False; end; end; // ========== Internal routines end ============= @@ -238,60 +313,87 @@ begin begin Mydatapoint := FoobotDataObject.datapoints[K][J]; case J of - 0: // First field is a DateTime + C_TIME: // First field is a DateTime begin iUnixSecs := int64(Mydatapoint); SetLength(FoobotData_time, K + 1); FoobotData_time[K] := UnixToDateTime(iUnixSecs); end; - 1: // Particulate matter + C_PM: // Particulate matter begin SetLength(FoobotData_pm, K + 1); FoobotData_pm[K] := double(MyDataPoint); SetHigh(J, FoobotData_pm[K], FoobotData_time[K]); SetLow(J, FoobotData_pm[K], FoobotData_time[K]); end; - 2: // Temperature + C_TMP: // Temperature begin SetLength(FoobotData_tmp, K + 1); FoobotData_tmp[K] := double(MyDataPoint); SetHigh(J, FoobotData_tmp[K], FoobotData_time[K]); SetLow(J, FoobotData_tmp[K], FoobotData_time[K]); end; - 3: // Humidity + C_HUM: // Humidity begin SetLength(FoobotData_hum, K + 1); FoobotData_hum[K] := double(MyDataPoint); SetHigh(J, FoobotData_hum[K], FoobotData_time[K]); SetLow(J, FoobotData_hum[K], FoobotData_time[K]); end; - 4: // CO2 + C_CO2: // CO2 begin SetLength(FoobotData_co2, K + 1); FoobotData_co2[K] := integer(MyDataPoint); SetHigh(J, FoobotData_co2[K], FoobotData_time[K]); SetLow(J, FoobotData_co2[K], FoobotData_time[K]); end; - 5: // Volatile compounds + C_VOC: // Volatile compounds begin SetLength(FoobotData_voc, K + 1); FoobotData_voc[K] := integer(MyDataPoint); SetHigh(J, FoobotData_voc[K], FoobotData_time[K]); SetLow(J, FoobotData_voc[K], FoobotData_time[K]); end; - 6: // All Pollution + C_ALLPOLLU: // All Pollution begin SetLength(FoobotData_allpollu, K + 1); FoobotData_allpollu[K] := double(MyDataPoint); SetHigh(J, FoobotData_allpollu[K], FoobotData_time[K]); SetLow(J, FoobotData_allpollu[K], FoobotData_time[K]); end; + else raise Exception.Create('Error in FoobotDataObjectToArrays Case'); end; // of Case end; end; - SaveHighLows; + end; + +function SetHighTrigger(const aSensor: TSensorType; const aValue: variant): boolean; +begin + Result := False; + if UseTriggers = False then + Exit; + if aValue <> FooBotTriggerArray[1, Ord(aSensor)] then + begin + FooBotTriggerArray[1, Ord(aSensor)] := aValue; + Result := True; + end; +end; + +function SetLowTrigger(const aSensor: TSensorType; const aValue: variant): boolean; +begin + Result := False; + if UseTriggers = False then + Exit; + if aValue <> FooBotTriggerArray[0, Ord(aSensor)] then + begin + FooBotTriggerArray[0, Ord(aSensor)] := aValue; + Result := True; + end; +end; + +// Data cleaning functions function ResetHighLows: boolean; var iCount: integer; @@ -341,6 +443,7 @@ begin end; end; +// Authentication functions function EncodeStringBase64(const s: string): string; var @@ -395,10 +498,12 @@ begin Result := True; end; finally + // Empty end; end; +// This function must succeed before calling FetchFoobotData function FetchFoobotIdentity(aUsername, aSecretKey: string): boolean; var sUserNameURL: string; @@ -448,6 +553,7 @@ begin end; end; +// Function FetchFoobotIdentity must be called before this one function FetchFoobotData(DataFetchType: TDataFetchType; iCurrentFoobot, iLastIntervalSeconds, iLastAverageBySeconds: integer; iStartTimeSeconds, iEndTimeSeconds: int64; aSecretKey: string): boolean; @@ -532,8 +638,17 @@ initialization HttpClient := TFPHTTPClient.Create(nil); FoobotIdentityObject := TFoobotIdentityObject.Create; FoobotDataObject := TFoobotDataObject.Create; - SaveLoadHighLows := True; TheCurrentFoobot := 0; + SetLength(FooBotTriggerArray, 2, Succ(C_ALLPOLLU)); + SaveLoadHighLows := True; // Default + UseTriggers := False; // Defaul + for giCount := C_PM to C_ALLPOLLU do + begin + AlertRec[giCount].AlertTriggered := False; + AlertRec[giCount].AlertTime := Now; + AlertRec[giCount].AlertType := at_low; + AlertRec[giCount].AlertValue := 0; + end; end; finalization @@ -550,6 +665,7 @@ finalization SetLength(FoobotData_co2, 0); SetLength(FoobotData_voc, 0); SetLength(FoobotData_allpollu, 0); + SetLength(FooBotTriggerArray, 0, 0); end; end. diff --git a/applications/foobot/latest_stable/foobot_objects.pas b/applications/foobot/latest_stable/foobot_objects.pas index 3d10689a6..66e6e14cd 100644 --- a/applications/foobot/latest_stable/foobot_objects.pas +++ b/applications/foobot/latest_stable/foobot_objects.pas @@ -1,5 +1,5 @@ unit foobot_objects; -{ Objects for Foobot Interrogator +{ Objects for Foobot Lazarus Copyright (C)2016 Gordon Bamber minsadorada@charcodelvalle.com diff --git a/applications/foobot/monitor/foobotmonitor.lpi b/applications/foobot/monitor/foobotmonitor.lpi index 600bd236c..3cec7719c 100644 --- a/applications/foobot/monitor/foobotmonitor.lpi +++ b/applications/foobot/monitor/foobotmonitor.lpi @@ -23,6 +23,7 @@ + @@ -236,7 +237,7 @@ - + @@ -282,6 +283,13 @@ + + + + + + + @@ -322,9 +330,6 @@ - - - diff --git a/applications/foobot/monitor/foobotmonitor.lpr b/applications/foobot/monitor/foobotmonitor.lpr index 8b8daca27..02d29f2e0 100644 --- a/applications/foobot/monitor/foobotmonitor.lpr +++ b/applications/foobot/monitor/foobotmonitor.lpr @@ -15,7 +15,8 @@ uses cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset - Forms, usplash, tachartlazaruspkg, umainform, uconfigform, foobot_sensors + Forms, usplash, tachartlazaruspkg, umainform, uconfigform, foobot_sensors, + utriggersform { you can add units after this }; {$R *.res} @@ -29,6 +30,7 @@ begin Application.ProcessMessages; // process splash paint message Application.CreateForm(Tmainform, mainform); Application.CreateForm(Tconfigform, configform); + Application.CreateForm(Ttriggersform, triggersform); Application.Run; end. diff --git a/applications/foobot/monitor/foobotmonitor.lps b/applications/foobot/monitor/foobotmonitor.lps index 802bd9ebd..3f180fc99 100644 --- a/applications/foobot/monitor/foobotmonitor.lps +++ b/applications/foobot/monitor/foobotmonitor.lps @@ -3,14 +3,14 @@ - - + + - - - + + + @@ -19,10 +19,9 @@ - - - - + + + @@ -32,10 +31,10 @@ - + - + @@ -45,7 +44,7 @@ - + @@ -54,15 +53,16 @@ - + - - - - + + + + + @@ -72,15 +72,15 @@ - + - + - + @@ -89,297 +89,314 @@ - - - + + + - - - - - - + + + + + + + + + - - - - + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + + + - - - + + + - - - - - - - - - + - - - - + + + + + - - - - - + + + + + + + + + + + + - - - + + + - - - - - - - - - - + + + + + + + + + + - - - + + + - + - + - + - + - - + + - - + + - - + + - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - + - + - + + + + + diff --git a/applications/foobot/monitor/foobotmonitor.res b/applications/foobot/monitor/foobotmonitor.res index 472d06b80..0cce42ae6 100644 Binary files a/applications/foobot/monitor/foobotmonitor.res and b/applications/foobot/monitor/foobotmonitor.res differ diff --git a/applications/foobot/monitor/splashimage.jpg b/applications/foobot/monitor/splashimage.jpg index a19422e54..e80513959 100644 Binary files a/applications/foobot/monitor/splashimage.jpg and b/applications/foobot/monitor/splashimage.jpg differ diff --git a/applications/foobot/monitor/umainform.lfm b/applications/foobot/monitor/umainform.lfm index 81a92dd4d..cb7b61644 100644 --- a/applications/foobot/monitor/umainform.lfm +++ b/applications/foobot/monitor/umainform.lfm @@ -492,6 +492,20 @@ object mainform: Tmainform Caption = 'Reset All-time Highs/Lows' OnClick = mnu_optionsResetHighsLowsClick end + object mnu_optionsFoobotTriggers: TMenuItem + Caption = 'Foobot Triggers...' + OnClick = mnu_optionsFoobotTriggersClick + object mnu_options_triggersSetTriggers: TMenuItem + Caption = 'Set Trigger Values...' + OnClick = mnu_options_triggersSetTriggersClick + end + object mnu_options_triggersActivateTriggers: TMenuItem + Caption = 'Set Triggers On' + Enabled = False + RadioItem = True + OnClick = mnu_options_triggersActivateTriggersClick + end + end end object mnu_foobot: TMenuItem Caption = '&Foobot' diff --git a/applications/foobot/monitor/umainform.pas b/applications/foobot/monitor/umainform.pas index c9195668f..dfbb27cd9 100644 --- a/applications/foobot/monitor/umainform.pas +++ b/applications/foobot/monitor/umainform.pas @@ -40,7 +40,7 @@ interface uses SysUtils, TAGraph, TAIntervalSources, TASeries, foobot_sensors, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, Menus, lclIntf, - foobot_utility, uCryptIni, dateutils, uconfigform; + foobot_utility, uCryptIni, dateutils, uconfigform,utriggersform, Classes; const // Timer milliseconds @@ -123,6 +123,9 @@ type lbl_voclow: TLabel; lbl_allpollulow: TLabel; MainMenu1: TMainMenu; + mnu_options_triggersActivateTriggers: TMenuItem; + mnu_options_triggersSetTriggers: TMenuItem; + mnu_optionsFoobotTriggers: TMenuItem; mnu_foobot: TMenuItem; mnu_optionsDisplayRedLines: TMenuItem; mnu_optionsDisplayYellowLines: TMenuItem; @@ -166,7 +169,10 @@ type procedure mnu_optionsOnlineHelpClick(Sender: TObject); procedure mnu_optionsResetHighsLowsClick(Sender: TObject); procedure mnu_optionsSaveHighLowsClick(Sender: TObject); + procedure mnu_optionsFoobotTriggersClick(Sender: TObject); procedure mnu_optionsTakeReadingNowClick(Sender: TObject); + procedure mnu_options_triggersActivateTriggersClick(Sender: TObject); + procedure mnu_options_triggersSetTriggersClick(Sender: TObject); procedure mnu_SampleEvery1HourClick(Sender: TObject); procedure mnu_SampleEvery24HoursClick(Sender: TObject); procedure mnu_SampleEvery2HoursClick(Sender: TObject); @@ -240,6 +246,7 @@ begin TrayIcon1.Hint := Application.Title; DateTimeIntervalChartSource1.DateTimeFormat := 'hh:nn'; LoadConfig; + {$IFDEF DEBUGMODE}UseTriggers:=FALSE;{$ENDIF} end; procedure Tmainform.FormActivate(Sender: TObject); @@ -517,7 +524,7 @@ begin exit; ResetHighLows; SaveHighLows; - for iCount := 1 to 6 do + for iCount := C_PM to C_ALLPOLLU do UpdateHighLow(iCount); end; @@ -527,6 +534,10 @@ begin INI.WriteBool('Foobot', 'SaveLoadHighLows', SaveLoadHighLows); end; +procedure Tmainform.mnu_optionsFoobotTriggersClick(Sender: TObject); +begin +end; + procedure Tmainform.mnu_optionsTakeReadingNowClick(Sender: TObject); begin mainform.Cursor := crHourGlass; @@ -537,6 +548,25 @@ begin mainform.Cursor := crDefault; end; +procedure Tmainform.mnu_options_triggersActivateTriggersClick(Sender: TObject); +begin + mnu_options_triggersActivateTriggers.Checked:= NOT mnu_options_triggersActivateTriggers.Checked; + UseTriggers:=mnu_options_triggersActivateTriggers.Checked; + If UseTriggers then + mnu_options_triggersActivateTriggers.Caption:='Set Triggers Off' + else + mnu_options_triggersActivateTriggers.Caption:='Set Triggers On'; +end; + +procedure Tmainform.mnu_options_triggersSetTriggersClick(Sender: TObject); +begin + triggersform.ShowModal; + If triggersform.ModalResult = mrCancel then + ShowMessage('Cancel') + else + mnu_options_triggersActivateTriggers.Enabled:=TRUE; +end; + procedure Tmainform.mnu_SampleEveryHalfHourClick(Sender: TObject); begin tmr_foobot.Enabled := False; @@ -650,7 +680,7 @@ end; procedure Tmainform.UpdateHighLow(SensorNumber: integer); begin case SensorNumber of - 1: + C_PM: begin lbl_pmhigh.Caption := Format('High: %f %s', [double(FoobotDataHighs[SensorNumber]), FoobotDataObject.Units[SensorNumber]]) + @@ -661,7 +691,7 @@ begin LineEnding + 'on ' + FormatDateTime('dd/mm tt', TDateTime( FoobotDataLowTimes[SensorNumber])); end; - 2: + C_TMP: begin lbl_tmphigh.Caption := Format('High: %f %s', [double(FoobotDataHighs[SensorNumber]), FoobotDataObject.Units[SensorNumber]]) + @@ -672,7 +702,7 @@ begin LineEnding + 'on ' + FormatDateTime('dd/mm tt', TDateTime( FoobotDataLowTimes[SensorNumber])); end; - 3: + C_HUM: begin lbl_humhigh.Caption := Format('High: %f %s', [double(FoobotDataHighs[SensorNumber]), FoobotDataObject.Units[SensorNumber]]) + @@ -683,7 +713,7 @@ begin LineEnding + 'on ' + FormatDateTime('dd/mm tt', TDateTime( FoobotDataLowTimes[SensorNumber])); end; - 4: + C_CO2: begin lbl_co2high.Caption := Format('High: %f %s', [double(FoobotDataHighs[SensorNumber]), FoobotDataObject.Units[SensorNumber]]) + @@ -694,7 +724,7 @@ begin LineEnding + 'on ' + FormatDateTime('dd/mm tt', TDateTime( FoobotDataLowTimes[SensorNumber])); end; - 5: + C_VOC: begin lbl_vochigh.Caption := Format('High: %f %s', [double(FoobotDataHighs[SensorNumber]), FoobotDataObject.Units[SensorNumber]]) + @@ -705,7 +735,7 @@ begin LineEnding + 'on ' + FormatDateTime('dd/mm tt', TDateTime( FoobotDataLowTimes[SensorNumber])); end; - 6: + C_ALLPOLLU: begin lbl_allpolluhigh.Caption := Format('High: %f %s', [double(FoobotDataHighs[SensorNumber]), @@ -724,32 +754,32 @@ begin with Sender do begin case SensorNumber of - 1: + C_PM: begin Value := FoobotData_pm[0]; Caption := Format('PM (%s): ', [FoobotDataObject.Units[SensorNumber]]); end; - 2: + C_TMP: begin Value := FoobotData_tmp[0]; Caption := Format('Temp (%s): ', [FoobotDataObject.Units[SensorNumber]]); end; - 3: + C_HUM: begin Value := FoobotData_hum[0]; Caption := Format('Hum. (%s): ', [FoobotDataObject.Units[SensorNumber]]); end; - 4: + C_CO2: begin Value := FoobotData_co2[0]; Caption := Format('CO2 (%s): ', [FoobotDataObject.Units[SensorNumber]]); end; - 5: + C_VOC: begin Value := FoobotData_voc[0]; Caption := Format('VOC (%s): ', [FoobotDataObject.Units[SensorNumber]]); end; - 6: + C_ALLPOLLU: begin Value := FoobotData_allpollu[0]; Caption := Format('All (%s): ', [FoobotDataObject.Units[SensorNumber]]); @@ -774,18 +804,33 @@ begin mainform.Caption := Format('Foobot "%s" - Last reading: ', [FoobotIdentityObject.FoobotIdentityList[0].Name]) + FormatDateTime('dd/mm/yyyy - tt', FoobotData_time[0]); - UpdateGuage(as_pm, 1); - UpdateGuage(as_tmp, 2); - UpdateGuage(as_hum, 3); - UpdateGuage(as_co2, 4); - UpdateGuage(as_voc, 5); - UpdateGuage(as_allpollu, 6); + UpdateGuage(as_pm, C_PM); + UpdateGuage(as_tmp, C_TMP); + UpdateGuage(as_hum, C_HUM); + UpdateGuage(as_co2, C_CO2); + UpdateGuage(as_voc, C_VOC); + UpdateGuage(as_allpollu, C_ALLPOLLU); if not bDisplayGuagesOnly then begin for iCount := 1 to 6 do UpdateHighLow(iCount); end; GraphCurrentReading; + + // Process Trigger Alerts + If UseTriggers then + For iCount:=C_PM to C_ALLPOLLU do + If AlertRec[iCount].AlertTriggered then + If AlertRec[iCount].AlertType = at_high then + begin + ShowMessageFmt('High alert member %d (value %f) exceeded', + [iCount,Double(AlertRec[iCount].AlertValue)]); + end + else + begin + ShowMessageFmt('Low alert member %d (value %f) exceeded', + [iCount,Double(AlertRec[iCount].AlertValue)]); + end; end; end; @@ -799,6 +844,7 @@ end; procedure Tmainform.GraphCurrentReading; begin + {$IFDEF DEBUGMODE}Exit;{$ENDIF} lineseries_pm.AddXY(FoobotData_time[0], AsPercent(FoobotData_pm[0], as_pm.ValueMin, as_pm.ValueMax)); lineseries_tmp.AddXY(FoobotData_time[0], AsPercent(FoobotData_tmp[0], @@ -820,6 +866,7 @@ var iCount: integer; iStartSeconds, iEndSeconds: int64; begin + {$IFDEF DEBUGMODE}Exit;{$ENDIF} iEndSeconds := DateTimeToUnix(Now) - 3600; iStartSeconds := iEndSeconds - (2 * (24 * 3600)); // 49 hours before Now grp_chart.Caption := Format('History from %s', diff --git a/applications/foobot/monitor/usplash.lfm b/applications/foobot/monitor/usplash.lfm index e65098e72..c4a3b7a1d 100644 --- a/applications/foobot/monitor/usplash.lfm +++ b/applications/foobot/monitor/usplash.lfm @@ -1,12 +1,12 @@ object splashform: Tsplashform Left = 596 - Height = 480 + Height = 484 Top = 136 - Width = 790 + Width = 794 BorderStyle = bsNone Caption = 'splashform' - ClientHeight = 480 - ClientWidth = 790 + ClientHeight = 484 + ClientWidth = 794 DefaultMonitor = dmDesktop FormStyle = fsStayOnTop OnCreate = FormCreate @@ -16,9 +16,9 @@ object splashform: Tsplashform Scaled = True object img: TImage Left = 0 - Height = 480 + Height = 484 Top = 0 - Width = 790 + Width = 794 Align = alClient Stretch = True Transparent = True diff --git a/applications/foobot/monitor/usplash.pas b/applications/foobot/monitor/usplash.pas index aab384853..f893b3e9c 100644 --- a/applications/foobot/monitor/usplash.pas +++ b/applications/foobot/monitor/usplash.pas @@ -35,7 +35,7 @@ begin jpg:=TJPEGImage.Create; try jpg.LoadFromResourceName(HInstance,'SPLASHIMAGE'); - Img.Canvas.Draw(0,0,jpg); + img.Canvas.Draw(0,0,jpg); finally jpg.Free; end; diff --git a/applications/foobot/monitor/utriggersform.lfm b/applications/foobot/monitor/utriggersform.lfm new file mode 100644 index 000000000..029982fbc --- /dev/null +++ b/applications/foobot/monitor/utriggersform.lfm @@ -0,0 +1,49 @@ +object triggersform: Ttriggersform + Left = 546 + Height = 484 + Top = 142 + Width = 794 + BorderIcons = [biSystemMenu] + BorderStyle = bsSingle + Caption = 'triggersform' + ClientHeight = 484 + ClientWidth = 794 + DefaultMonitor = dmDesktop + FormStyle = fsStayOnTop + OnCreate = FormCreate + Position = poWorkAreaCenter + ShowInTaskBar = stNever + LCLVersion = '1.7' + Scaled = True + object grp_main: TGroupBox + Left = 0 + Height = 424 + Top = 0 + Width = 794 + Align = alTop + Caption = 'Foobot Triggers' + TabOrder = 0 + end + object cmd_OK: TBitBtn + Left = 360 + Height = 30 + Top = 440 + Width = 75 + Default = True + DefaultCaption = True + Kind = bkOK + ModalResult = 1 + TabOrder = 1 + end + object cmd_cancel: TBitBtn + Left = 704 + Height = 30 + Top = 440 + Width = 75 + Cancel = True + DefaultCaption = True + Kind = bkCancel + ModalResult = 2 + TabOrder = 2 + end +end diff --git a/applications/foobot/monitor/utriggersform.pas b/applications/foobot/monitor/utriggersform.pas new file mode 100644 index 000000000..946382ac7 --- /dev/null +++ b/applications/foobot/monitor/utriggersform.pas @@ -0,0 +1,42 @@ +unit utriggersform; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, + Buttons; + +type + + { Ttriggersform } + + Ttriggersform = class(TForm) + cmd_cancel: TBitBtn; + cmd_OK: TBitBtn; + grp_main: TGroupBox; + procedure FormCreate(Sender: TObject); + private + + public + + end; + +var + triggersform: Ttriggersform; + +implementation +Uses umainform; +{$R *.lfm} + +{ Ttriggersform } + +procedure Ttriggersform.FormCreate(Sender: TObject); +begin + Icon:=Application.Icon; + Caption:=Application.Title + ' - Set Triggers'; +end; + +end. +