diff --git a/ChronoReadingManager.cpp b/ChronoReadingManager.cpp index 32cadba..a12e539 100644 --- a/ChronoReadingManager.cpp +++ b/ChronoReadingManager.cpp @@ -1,27 +1,40 @@ #include "ChronoReadingManager.h" #include "FileManager.h" +#include + +static const char* PROFILE_NS = "profile"; +static const char* GUN_ID_KEY = "gunId"; +static const char* PROJ_ID_KEY = "projId"; // Constructor implementation ChronoReadingManager::ChronoReadingManager() {} void ChronoReadingManager::initializeProfiles(){ - //load gun profiles from disk FileManager& files = FileManager::getInstance(); - //files.formatDisk(); files.loadGunProfiles(_gunProfiles); files.loadProjectileProfiles(_projectileProfiles); - //defaults + Preferences prefs; + prefs.begin(PROFILE_NS, true); + + int savedGunId = prefs.getInt(GUN_ID_KEY, -1); + int savedProjId = prefs.getInt(PROJ_ID_KEY, -1); + prefs.end(); + GunProfile gp("", GunProfile::PROFILE_TYPE_AIR_GUN_FAC); ProjectileProfile pp("", ProjectileProfile::PELLET, 0.0, 0.0); - //set current profile - /*if (_gunProfiles.size()>0){ - gp=_gunProfiles.get(0); + if (savedGunId >= 0 && savedGunId < _gunProfiles.size()) { + gp = _gunProfiles.get(savedGunId); + } else if (_gunProfiles.size() > 0) { + gp = _gunProfiles.get(0); } - if (_projectileProfiles.size()>0){ - pp=_projectileProfiles.get(0); - }*/ - _currentProfile=new ChronoProfile(gp,pp); + if (savedProjId >= 0 && savedProjId < _projectileProfiles.size()) { + pp = _projectileProfiles.get(savedProjId); + } else if (_projectileProfiles.size() > 0) { + pp = _projectileProfiles.get(0); + } + + _currentProfile = new ChronoProfile(gp, pp); } ChronoReadingManager& ChronoReadingManager::getInstance(){ static ChronoReadingManager instance; @@ -31,6 +44,10 @@ LinkedList& ChronoReadingManager::getGunProfiles(){return _gunProfil void ChronoReadingManager::setGunProfile(int id){ GunProfile p=_gunProfiles.get(id); _currentProfile->setGunProfile(p); + Preferences prefs; + prefs.begin(PROFILE_NS, false); + prefs.putInt(GUN_ID_KEY, id); + prefs.end(); } void ChronoReadingManager::addGunProfile(GunProfile profile, bool writeToDisk){ if (writeToDisk){ @@ -59,6 +76,10 @@ LinkedList& ChronoReadingManager::getProjectileProfiles(){ret void ChronoReadingManager::setProjectileProfile(int id){ ProjectileProfile pp=_projectileProfiles.get(id); _currentProfile->setProjectileProfile(pp); + Preferences prefs; + prefs.begin(PROFILE_NS, false); + prefs.putInt(PROJ_ID_KEY, id); + prefs.end(); } void ChronoReadingManager::addProjectileProfile(ProjectileProfile profile, bool writeToDisk){ if (writeToDisk){ diff --git a/ChronoWebServer.cpp b/ChronoWebServer.cpp index 4a96fd5..bdc0eae 100644 --- a/ChronoWebServer.cpp +++ b/ChronoWebServer.cpp @@ -41,10 +41,24 @@ void saveWifiCredentials(const String& ssid, const String& password) { WebServer _server(80); void (*testModeCallback)(); +String escapeJsonString(const char* src, int maxLen) { + String out; + out.reserve(maxLen * 2); + for (int i = 0; i < maxLen; i++) { + char c = src[i]; + if (c == '\0') break; + if (c == '"') { out += "\\\""; } + else if (c == '\\') { out += "\\\\"; } + else if ((unsigned char)c < 0x20) { continue; } + else { out += c; } + } + return out; +} + // Constructor implementation ChronoWebServer::ChronoWebServer() {} void ChronoWebServer::_handleRoot() { - String temp="ChronoDisplay

ChronoDisplay

Latest Reading

--
FPS
--
FPE
0
Shots
--
Avg
--
Low
--
High

Reading History

ShotFPSFPEGunPowerProjectileCalWt

Gun Profiles

NameType

Projectile Profiles

NameTypeCalWt

WiFi

Mode: ?
WiFi: --

After saving, device will restart.

OTA Update

Version: ?

Control

"; + String temp="ChronoDisplay

ChronoDisplay

Latest Reading

--
FPS
--
FPE
0
Shots
--
Avg
--
Low
--
High

Reading History

Polling: OFF
ShotFPSFPEGunPowerProjectileCalWt

Gun Profiles

NameType

Projectile Profiles

NameTypeCalWt

WiFi

Mode: ?
WiFi: --

After saving, device will restart.

OTA Update

Version: ?

Control

"; _server.send(200, "text/html", temp); } @@ -141,6 +155,173 @@ void ChronoWebServer::_handleRemoveGunProfile(){ } _server.send(200, "text/html", "ok"); } +void ChronoWebServer::_handleGetGunProfiles(){ + ChronoReadingManager& chronoReadings = ChronoReadingManager::getInstance(); + LinkedList& gunProfiles=chronoReadings.getGunProfiles(); + int count=gunProfiles.size(); + + GunProfile gp=chronoReadings.getProfile().getGunProfile(); + int currentId=0; + for(int i=0;i0) json+=","; + json += "{\"id\":"; + json += i; + json += ",\"name\":\""; + json += escapeJsonString(p.getName().c_str(), 49); + json += "\",\"type\":"; + json += p.getPowerLevel(); + json += "}"; + } + json += "],\"current\":{\"id\":"; + json += currentId; + json += ",\"name\":\""; + json += escapeJsonString(gp.getName().c_str(), 49); + json += "\",\"type\":"; + json += gp.getPowerLevel(); + json += "}}"; + + _server.send(200, "application/json", json); +} +void ChronoWebServer::_handleGetProjectileProfiles(){ + ChronoReadingManager& chronoReadings = ChronoReadingManager::getInstance(); + LinkedList& projectileProfiles=chronoReadings.getProjectileProfiles(); + int count=projectileProfiles.size(); + + ProjectileProfile pp=chronoReadings.getProfile().getProjectileProfile(); + int currentId=0; + for(int i=0;i0) json+=","; + json += "{\"id\":"; + json += i; + json += ",\"name\":\""; + json += escapeJsonString(p.getName().c_str(), 49); + json += "\",\"type\":\""; + json += escapeJsonString(p.getType().c_str(), 9); + json += "\",\"cal\":\""; + json += String(p.getCaliber(), 4); + json += "\",\"weight\":\""; + json += String(p.getWeight(), 4); + json += "\"}"; + } + json += "],\"current\":{\"id\":"; + json += currentId; + json += ",\"name\":\""; + json += escapeJsonString(pp.getName().c_str(), 49); + json += "\",\"type\":\""; + json += escapeJsonString(pp.getType().c_str(), 9); + json += "\",\"cal\":\""; + json += String(pp.getCaliber(), 4); + json += "\",\"weight\":\""; + json += String(pp.getWeight(), 4); + json += "\"}}"; + + _server.send(200, "application/json", json); +} +void ChronoWebServer::_handleGetCurrentGunProfile(){ + ChronoReadingManager& chronoReadings = ChronoReadingManager::getInstance(); + GunProfile p=chronoReadings.getProfile().getGunProfile(); + LinkedList& gunProfiles=chronoReadings.getGunProfiles(); + int id=0; + for(int i=0;i& projectileProfiles=chronoReadings.getProjectileProfiles(); + int id=0; + for(int i=0;i& readings=chronoReadings.getChronographReadings(); + int count=readings.size(); + + String json; + json.reserve(4096); + json += "["; + for (int i=count-1;i>=0;i--){ + ChronoReading r=readings.get(i); + if (count-1-i>0) json+=","; + json += "{\"shot\":"; + json += (count-i); + json += ",\"fps\":"; + json += r.getFPS(); + json += ",\"fpe\":"; + json += r.getFPE(); + json += ",\"gun\":\""; + json += escapeJsonString(r.getProfile().getGunProfile().getName().c_str(), 49); + json += "\",\"power\":"; + json += r.getProfile().getGunProfile().getPowerLevel(); + json += ",\"projectile\":\""; + json += escapeJsonString(r.getProfile().getProjectileProfile().getName().c_str(), 49); + json += "\",\"cal\":\""; + json += String(r.getProfile().getProjectileProfile().getCaliber(), 4); + json += "\",\"weight\":\""; + json += String(r.getProfile().getProjectileProfile().getWeight(), 4); + json += "\"}"; + } + json += "]"; + + _server.send(200, "application/json", json); +} void ChronoWebServer::_handleProjectileProfile(){ String temp="\ \ @@ -453,11 +634,16 @@ void ChronoWebServer::init(){ _server.on("/gunprofile/add", _handleAddGunProfile); _server.on("/gunprofile/set", _handleSetGunProfile); _server.on("/gunprofile/remove", _handleRemoveGunProfile); + _server.on("/gunprofile/get", _handleGetGunProfiles); + _server.on("/gunprofile/current", _handleGetCurrentGunProfile); _server.on("/gunprofile", _handleGunProfile); _server.on("/projectileprofile/set", _handleSetProjectileProfile); _server.on("/projectileprofile/add", _handleAddProjectileProfile); _server.on("/projectileprofile/remove", _handleRemoveProjectileProfile); + _server.on("/projectileprofile/get", _handleGetProjectileProfiles); + _server.on("/projectileprofile/current", _handleGetCurrentProjectileProfile); _server.on("/projectileprofile", _handleProjectileProfile); + _server.on("/readings", _handleGetReadings); _server.on("/export", _handleExport); _server.on("/reset", _handleReset); _server.on("/format", _handleFormat); @@ -468,6 +654,7 @@ void ChronoWebServer::init(){ _server.on("/clearwifi", HTTP_POST, _handleClearWifi); _server.on("/wifimode", HTTP_GET, _handleWifiMode); _server.on("/version", HTTP_GET, _handleVersion); + _server.on("/initdata", HTTP_GET, _handleInitData); _server.on("/", _handleRoot); _server.begin(); @@ -527,4 +714,102 @@ void ChronoWebServer::_handleWifiMode() { mode = "NONE"; } _server.send(200, "text/plain", mode); +} +void ChronoWebServer::_handleInitData(){ + String json; + json.reserve(4096); + + json += "{\"version\":{\"version\":\""; + json += CHRONO_VERSION; + json += "\",\"major\":"; + json += CHRONO_VERSION_MAJOR; + json += ",\"minor\":"; + json += CHRONO_VERSION_MINOR; + json += ",\"patch\":"; + json += CHRONO_VERSION_PATCH; + json += "},\"wifiSsid\":\""; + String ssid = getWifiSsid(); + json += escapeJsonString(ssid.c_str(), ssid.length()); + json += "\",\"wifiMode\":\""; + if (WiFi.getMode() == WIFI_STA) json += "STA"; + else if (WiFi.getMode() == WIFI_AP) json += "AP"; + else if (WiFi.getMode() == WIFI_AP_STA) json += "AP+STA"; + else json += "NONE"; + json += "\",\"gunProfiles\":["; + + ChronoReadingManager& chronoReadings = ChronoReadingManager::getInstance(); + LinkedList& gunProfiles = chronoReadings.getGunProfiles(); + int gunCount = gunProfiles.size(); + for (int i = 0; i < gunCount; i++) { + GunProfile p = gunProfiles.get(i); + if (i > 0) json += ","; + json += "{\"id\":"; + json += i; + json += ",\"name\":\""; + json += escapeJsonString(p.getName().c_str(), 49); + json += "\",\"type\":"; + json += p.getPowerLevel(); + json += "}"; + } + json += "],\"projectileProfiles\":["; + + LinkedList& projectileProfiles = chronoReadings.getProjectileProfiles(); + int projCount = projectileProfiles.size(); + for (int i = 0; i < projCount; i++) { + ProjectileProfile p = projectileProfiles.get(i); + if (i > 0) json += ","; + json += "{\"id\":"; + json += i; + json += ",\"name\":\""; + json += escapeJsonString(p.getName().c_str(), 49); + json += "\",\"type\":\""; + json += escapeJsonString(p.getType().c_str(), 9); + json += "\",\"cal\":\""; + json += String(p.getCaliber(), 4); + json += "\",\"weight\":\""; + json += String(p.getWeight(), 4); + json += "\"}"; + } + json += "],\"currentGunProfile\":{"; + + GunProfile gp = chronoReadings.getProfile().getGunProfile(); + int gunId = 0; + for (int i = 0; i < gunCount; i++) { + GunProfile g = gunProfiles.get(i); + if (g.getName() == gp.getName() && g.getPowerLevel() == gp.getPowerLevel()) { + gunId = i; + break; + } + } + json += "\"id\":"; + json += gunId; + json += ",\"name\":\""; + json += escapeJsonString(gp.getName().c_str(), 49); + json += "\",\"type\":"; + json += gp.getPowerLevel(); + json += "},\"currentProjectileProfile\":{"; + + ProjectileProfile pp = chronoReadings.getProfile().getProjectileProfile(); + int projId = 0; + for (int i = 0; i < projCount; i++) { + ProjectileProfile p = projectileProfiles.get(i); + if (p.getName() == pp.getName() && p.getType() == pp.getType() && p.getCaliber() == pp.getCaliber() && p.getWeight() == pp.getWeight()) { + projId = i; + break; + } + } + json += "\"id\":"; + json += projId; + json += ",\"name\":\""; + json += escapeJsonString(pp.getName().c_str(), 49); + json += "\",\"type\":\""; + json += escapeJsonString(pp.getType().c_str(), 9); + json += "\",\"cal\":\""; + json += String(pp.getCaliber(), 4); + json += "\",\"weight\":\""; + json += String(pp.getWeight(), 4); + json += "\"}"; + json += "}"; + + _server.send(200, "application/json", json); } \ No newline at end of file diff --git a/ChronoWebServer.h b/ChronoWebServer.h index 219576f..b69b59d 100644 --- a/ChronoWebServer.h +++ b/ChronoWebServer.h @@ -31,6 +31,11 @@ class ChronoWebServer { static void _handleAddGunProfile(); static void _handleSetGunProfile(); static void _handleRemoveGunProfile(); + static void _handleGetGunProfiles(); + static void _handleGetProjectileProfiles(); + static void _handleGetCurrentGunProfile(); + static void _handleGetCurrentProjectileProfile(); + static void _handleGetReadings(); static void _handleExport(); static void _handleReset(); static void _handleFormat(); @@ -41,6 +46,7 @@ class ChronoWebServer { static void _handleWifiSave(); static void _handleClearWifi(); static void _handleWifiMode(); + static void _handleInitData(); }; diff --git a/GunProfile.cpp b/GunProfile.cpp index 54221af..5057cae 100644 --- a/GunProfile.cpp +++ b/GunProfile.cpp @@ -1,10 +1,20 @@ #include "GunProfile.h" // Constructor implementation -GunProfile::GunProfile() {} +GunProfile::GunProfile() { + memset(_name, 0, sizeof(_name)); + _type = 0; +} GunProfile::GunProfile(String name, int type) { - strcpy(_name, name.c_str()); + memset(_name, 0, sizeof(_name)); + strncpy(_name, name.c_str(), sizeof(_name) - 1); _type=type; } -String GunProfile::getName(){return String(_name);} +String GunProfile::getName(){ + _name[sizeof(_name)-1] = '\0'; + for (int i = 0; i < sizeof(_name) - 1; i++) { + if (_name[i] == '\0') { _name[i+1] = '\0'; break; } + } + return String(_name); +} int GunProfile::getPowerLevel(){return _type;} \ No newline at end of file diff --git a/ProjectileProfile.cpp b/ProjectileProfile.cpp index 66e5e15..3d3ef3d 100644 --- a/ProjectileProfile.cpp +++ b/ProjectileProfile.cpp @@ -1,14 +1,26 @@ #include "ProjectileProfile.h" // Constructor implementation -ProjectileProfile::ProjectileProfile() {} +ProjectileProfile::ProjectileProfile() { + memset(_name, 0, sizeof(_name)); + _type = PELLET; + _caliber = 0; + _weight = 0; +} ProjectileProfile::ProjectileProfile(String name, ProjectileType type, float caliber, float weight) { - strcpy(_name, name.c_str()); + memset(_name, 0, sizeof(_name)); + strncpy(_name, name.c_str(), sizeof(_name) - 1); _type=type; _caliber=caliber; _weight=weight; } -String ProjectileProfile::getName(){return String(_name);} +String ProjectileProfile::getName(){ + _name[sizeof(_name)-1] = '\0'; + for (int i = 0; i < sizeof(_name) - 1; i++) { + if (_name[i] == '\0') { _name[i+1] = '\0'; break; } + } + return String(_name); +} float ProjectileProfile::getWeight(){return _weight;} String ProjectileProfile::getType(){if (_type==PELLET){return "Pellet";}else{return "Slug";}} float ProjectileProfile::getCaliber(){return _caliber;} \ No newline at end of file