📄 geozone.c
字号:
// ----------------------------------------------------------------------------// Copyright 2006-2007, Martin D. Flynn// All rights reserved// ----------------------------------------------------------------------------//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.//// ----------------------------------------------------------------------------// Description:// GPS rule module for checking for GeoZone arrival/departure// ---// Change History:// 2006/05/07 Martin D. Flynn// -Initial release// 2007/01/28 Martin D. Flynn// -WindowsCE port// -Receiving a 'GEOF_CMD_REMOVE' with no specific zone-id will remove// ALL existing geozones. This is used for purposes of reloading the// entire geozone cache.// -Qualify 'GEOZONE_FILENAME' location to directory specified by the// 'CONFIG_DIR' definition provided in 'defaults.h'// 2007/04/28 Martin D. Flynn// -Changed to 'back-date' arrival/departure point to actual point of // arrival/departure.// ----------------------------------------------------------------------------#include "stdafx.h" // TARGET_WINCE#include "custom/defaults.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include "custom/startup.h"#include "custom/log.h"#include "tools/stdtypes.h"#include "tools/gpstools.h"#include "tools/strtools.h"#include "tools/utctools.h"#include "tools/io.h"#include "base/propman.h"#include "base/statcode.h"#include "base/events.h"#include "modules/geozone.h"// ----------------------------------------------------------------------------#define ARRIVE_PRIORITY PRIORITY_NORMAL#define DEPART_PRIORITY PRIORITY_NORMAL// ----------------------------------------------------------------------------// where the geozone table will be saved#define GEOZONE_FILENAME (CONFIG_DIR_ "GEOZONE.DAT")// ----------------------------------------------------------------------------// pre-allocate the maximum number of possible geozones#ifndef MAX_GEOZONES // <-- a different value could be specified in 'defaults.h'# define MAX_GEOZONES 4000 // 80K bytes (@20 bytes/rcd)#endif// define to force all inserted ZoneIDs to be unique//#define FORCE_UNIQUE_ZONE_IDS// any ZoneID, other than 'NO_ZONE', is valid#define IS_VALID_ZONE(Z) ((Z) != NO_ZONE)// true if arrival/departure should be set-back to actual arrival/departure point#define SETBACK_POINT (utTrue)// ----------------------------------------------------------------------------// This value represents the length of the data presented in the 'Add GeoZone' command#define PACKED_GEOZONE_SIZE (14 + sizeof(GeoZoneID_t))// ----------------------------------------------------------------------------#ifdef PROTOCOL_THREAD#include "tools/threads.h"static threadMutex_t geozMutex;#define GEOZ_LOCK MUTEX_LOCK(&geozMutex);#define GEOZ_UNLOCK MUTEX_UNLOCK(&geozMutex);#else#define GEOZ_LOCK#define GEOZ_UNLOCK#endif// ----------------------------------------------------------------------------static utBool didInitialize = utFalse;static GeoZone_t geoZoneList[MAX_GEOZONES];static UInt16 maxZones = MAX_GEOZONES;static UInt16 usedZones = 0;static utBool geozIsDirty = utFalse;static GPS_t arrivePoint; // need initializationstatic GPS_t departPoint; // need initializationstatic eventAddFtn_t ftnQueueEvent = 0;// ----------------------------------------------------------------------------/* add a geofence event to the event queue */static void _queueGeofenceEvent(PacketPriority_t priority, StatusCode_t code, const GPS_t *gps, int geofID){ if (gps && ftnQueueEvent) { Event_t evRcd; evSetEventDefaults(&evRcd, code, 0L, gps); evRcd.geofenceID[0] = geofID; (*ftnQueueEvent)(priority, DEFAULT_EVENT_FORMAT, &evRcd); }}// ----------------------------------------------------------------------------void geozSetVersion(const char *v){ propSetString(PROP_GEOF_VERSION, v);}const char *geozGetVersion(){ return propGetString(PROP_GEOF_VERSION, "");}// ----------------------------------------------------------------------------/* check new GPS fix for various motion events */void geozCheckGPS(const GPS_t *oldFix, const GPS_t *newFix){ /* new fix required */ if (!newFix) { //logDEBUG(LOGSRC,"No new fix! ..."); return; } /* in GeoZone? */ //logDEBUG(LOGSRC,"Zone checking in progress ..."); GeoZone_t *inZone = geozInZone(&(newFix->point)); //if (isDebugMode() && inZone) { logDEBUG(LOGSRC,"!!!!!! In Zone %d", inZone->zoneID); } /* get zones */ GeoZoneID_t curZoneID = geozGetCurrentID(); GeoZoneID_t newZoneID = inZone? inZone->zoneID : NO_ZONE; /* GeoZone changed? */ // This zone-change-checking method only triggers an arrival/departure if // we're moving from inside the CURRENT zone to outside ALL zones, // or from outside ALL zones to inside ANY zone. // This allows creating unique ZoneIDs for overlapping sub-zones, and the // Arrival event will then indicate which specific sub-zone was entered. utBool zoneChange = (IS_VALID_ZONE(curZoneID) != IS_VALID_ZONE(newZoneID))? utTrue : utFalse; // This zon-change-checking method only triggers an arrival/departure if // were moving from inside the CURRENT zone to outside the CURRENT zone, // or from outside ALL zones to inside ANY zone. // All overlapping sub-zones should have the same ZoneID. //utBool zoneChange = (curZoneID != newZoneID); /* check zone change */ if (zoneChange) { // My 'Zone' state has changed //logDEBUG(LOGSRC,"GeoZone state changed: %u != %u", curZoneID, newZoneID); /* check departure */ if (IS_VALID_ZONE(curZoneID)) { // I was in 'curZoneID', but now I am not (ie. departing 'curZoneID') if (!gpsIsValid(&departPoint)) { // This is the first 'departure' event gpsCopy(&departPoint, newFix); // a valid 'fixtime' is assumed } // check 'departure' delay UInt16 depDelay = (UInt16)propGetUInt32(PROP_GEOF_DEPART_DELAY, 0L); if ((depDelay == 0) || ((departPoint.fixtime + (UInt32)depDelay) <= utcGetTimeSec())) { // I've now departed the zone const GPS_t *departFix = SETBACK_POINT? &departPoint : newFix; _queueGeofenceEvent(DEPART_PRIORITY, STATUS_GEOFENCE_DEPART, departFix, curZoneID); geozSetCurrentID(NO_ZONE); gpsClear(&departPoint); logINFO(LOGSRC,"Departed %u [%u]\n", curZoneID, geozGetCurrentID()); startupSaveProperties(); } else { // not yet ready to mark as 'departed' //logDEBUG(LOGSRC,"Depart in %lu seconds", ((departPoint.fixtime + (UInt32)depDelay) - utcGetTimeSec())); } } else { // I wasn't in any known zone (I'm not departing) gpsClear(&departPoint); } /* check arrival */ if (IS_VALID_ZONE(newZoneID)) { // I was not in 'newZoneID' before, but now I am (arriving 'newZoneID') if (!gpsIsValid(&arrivePoint)) { // This is the first 'arrival' event gpsCopy(&arrivePoint, newFix); // a valid 'fixtime' is assumed } // check 'arrival' delay UInt16 arrDelay = (UInt16)propGetUInt32(PROP_GEOF_ARRIVE_DELAY, 0L); if ((arrDelay == 0) || ((arrivePoint.fixtime + (UInt32)arrDelay) <= utcGetTimeSec())) { // I've now arrived in the zone const GPS_t *arriveFix = SETBACK_POINT? &arrivePoint : newFix; geozSetCurrentID(newZoneID); _queueGeofenceEvent(ARRIVE_PRIORITY, STATUS_GEOFENCE_ARRIVE, arriveFix, newZoneID); gpsClear(&arrivePoint); logINFO(LOGSRC,"Arrived %u [%u]\n", newZoneID, geozGetCurrentID()); startupSaveProperties(); } else { // not yet ready to mark as 'arrived' //logDEBUG(LOGSRC,"Arrive in %lu seconds", ((arrivePoint.fixtime + (UInt32)arrDelay) - utcGetTimeSec())); } } else { // I'm not arriving at any new zone gpsClear(&arrivePoint); } } else { // My 'Zone' state has not changed // (ie. If I'm 'out', I'm still 'out'. If I'm 'in', I'm still 'in'.) //logDEBUG(LOGSRC,"GeoZone state NOT changed: %2u / %2u\n", curZoneID, newZoneID); gpsClear(&departPoint); gpsClear(&arrivePoint); }}// ----------------------------------------------------------------------------/* return current geozone id */GeoZoneID_t geozGetCurrentID(){ return (GeoZoneID_t)propGetUInt32(PROP_GEOF_CURRENT, (UInt32)NO_ZONE);}/* set current geozone id */void geozSetCurrentID(GeoZoneID_t zoneID){ propSetUInt32(PROP_GEOF_CURRENT, (UInt32)zoneID);}/* convert GeoZone point to GPS point */static GPSPoint_t *_geozToGPSPoint(GPSPoint_t *gp, const GeoZonePoint_t *gzp){ if (gzp && gp) { gp->latitude = gzp->latitude; gp->longitude = gzp->longitude; return gp; } else { return (GPSPoint_t*)0; }}/* check new GPS fix for various motion events */static utBool _geozInZone(GeoZone_t *geoz, const GPSPoint_t *newGP){ utBool inZone = utFalse; if (geoz && newGP && IS_VALID_ZONE(geoz->zoneID)) { //logDEBUG(LOGSRC,"Target point: %.5lf / %.5lf\n", newGP->latitude, newGP->longitude); GPSPoint_t geozGP_0, geozGP_1; double deltaMeters, radiusMeters = (double)geoz->radius; switch (geoz->type) {#ifdef GEOF_SWEPT_POINT_RADIUS case GEOF_SWEPT_POINT_RADIUS: // not supported in this implementation. // default to dual point/radius below#endif case GEOF_DUAL_POINT_RADIUS: _geozToGPSPoint(&geozGP_0, &(geoz->point[0])); if (gpsPointIsValid(&geozGP_0)) { deltaMeters = gpsMetersToPoint(newGP, &geozGP_0); //logDEBUG(LOGSRC," Zone %d A (%.5lf / %.5lf) [%.1lf <= %.1lf]", geoz->zoneID, geozGP_0.latitude, geozGP_0.longitude, deltaMeters, radiusMeters); if (deltaMeters <= radiusMeters) { inZone = utTrue; break; } } _geozToGPSPoint(&geozGP_1, &(geoz->point[1])); if (gpsPointIsValid(&geozGP_1)) { deltaMeters = gpsMetersToPoint(newGP, &geozGP_1); //logDEBUG(LOGSRC," Zone %d B (%.5lf / %.5lf) [%.1lf <= %.1lf]", geoz->zoneID, geozGP_1.latitude, geozGP_1.longitude, deltaMeters, radiusMeters); if (deltaMeters <= radiusMeters) { inZone = utTrue; break; } } break; case GEOF_BOUNDED_RECT: // North of the top latitude if (newGP->latitude > (double)geoz->point[0].latitude) { break; } // South of the bottom latitude if (newGP->latitude < (double)geoz->point[1].latitude) { break; } // West of the left longitude (will fail if zone spans +/- 180 degrees) if (newGP->longitude < (double)geoz->point[0].longitude) { break; } // East of the right longitude (will fail if zone spans +/- 180 degrees) if (newGP->longitude > (double)geoz->point[1].longitude) { break; } inZone = utTrue; break; #ifdef GEOF_DELTA_RECT case GEOF_DELTA_RECT: // North of the top latitude geozGP_0.latitude = (double)(geoz->point[0].latitude + geoz->point[1].latitude); // top if (newGP->latitude > geozGP_0.latitude) { break; } // South of the bottom latitude geozGP_0.latitude = (double)(geoz->point[0].latitude - geoz->point[1].latitude); // bottom if (newGP->latitude < geozGP_0.latitude) { break; } // West of the left longitude (will fail if zone spans +/- 180 degrees) geozGP_0.longitude = (double)(geoz->point[0].longitude - geoz->point[1].longitude); // left if (newGP->longitude < geozGP_0.longitude) { break; } // East of the right longitude (will fail if zone spans +/- 180 degrees) geozGP_0.longitude = (double)(geoz->point[0].longitude + geoz->point[1].longitude); // right if (newGP->longitude > geozGP_0.longitude) { break; } inZone = utTrue; break;#endif } // switch (geoz->type) } // if (...) return inZone;}/* return the zone where the specified point is located */GeoZone_t *geozInZone(const GPSPoint_t *newGP){ /* is newGP inside GeoZone? */ GeoZone_t *gz = (GeoZone_t*)0; UInt16 i; GEOZ_LOCK { for (i = 0; i < usedZones; i++) { if (_geozInZone(&geoZoneList[i], newGP)) { gz = &geoZoneList[i]; break; } } } GEOZ_UNLOCK return gz;}// ----------------------------------------------------------------------------static void _geozClearAll(){ memset(geoZoneList, sizeof(geoZoneList), 0); geozIsDirty = (usedZones > 0)? utTrue : utFalse; usedZones = 0;}static GeoZone_t *_geozDecodeGeoZone(Buffer_t *src, GeoZone_t *gz, utBool hiRes){ // this method decodes a GeoZone as presented in the packet. UInt32 zoneID; UInt32 typeRadius; GPSPoint_t pt0, pt1; int fldCnt; if (hiRes) { // High resolution point fldCnt = binBufScanf(src, "%4u%2u%8g%8g", &zoneID, &typeRadius, &pt0, &pt1); } else { // Standard resolution point fldCnt = binBufScanf(src, "%2u%2u%6g%6g", &zoneID, &typeRadius, &pt0, &pt1); } memset(gz, 0, sizeof(GeoZone_t)); if (fldCnt == 4) { gz->zoneID = (GeoZoneID_t)zoneID; gz->type = (typeRadius >> 13) & 0x7; gz->radius = typeRadius & 0x1FFF; gz->point[0].latitude = (float)pt0.latitude; gz->point[0].longitude = (float)pt0.longitude; gz->point[1].latitude = (float)pt1.latitude; gz->point[1].longitude = (float)pt1.longitude; return gz; } else { return (GeoZone_t*)0; }}#ifdef GEOZ_INCL_PRINT_GEOZONEstatic void _geozPrintGeoZone(GeoZone_t *gz){ if (gz) { logDEBUG(LOGSRC,"GeoZone: %u (typ=%u, rad=%u) 0=%.4f/%.4f, 1=%.4f/%.4f\n", gz->zoneID, gz->type, gz->radius, gz->point[0].latitude, gz->point[0].longitude, gz->point[1].latitude, gz->point[1].longitude); }}#endif// ----------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -