Skip to content

Commit d3cc7de

Browse files
committed
backup_db experimental option|
1 parent d24196d commit d3cc7de

File tree

5 files changed

+162
-18
lines changed

5 files changed

+162
-18
lines changed

Application/WebViewer.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ bool WebViewer::Save()
165165
return false;
166166
if (!hist_day.Save(infile))
167167
return false;
168+
// Save ship database
169+
if (backupDB)
170+
{
171+
if (!ships.Save(infile))
172+
return false;
173+
}
168174
infile.close();
169175
}
170176
catch (const std::exception &e)
@@ -207,6 +213,12 @@ bool WebViewer::Load()
207213
return false;
208214
if (!hist_day.Load(infile))
209215
return false;
216+
// Load ship database if available
217+
if (infile.peek() != EOF && backupDB)
218+
{
219+
if (!ships.Load(infile))
220+
Warning() << "Server: Could not load ship database from backup";
221+
}
210222

211223
infile.close();
212224
}
@@ -685,7 +697,7 @@ void WebViewer::Request(TCP::ServerConnection &c, const std::string &response, b
685697
{
686698
std::stringstream ss(a);
687699
std::string mmsi_str;
688-
700+
689701
if (std::getline(ss, mmsi_str))
690702
{
691703
try
@@ -881,6 +893,10 @@ Setting &WebViewer::Set(std::string option, std::string arg)
881893
counter.setCutOff(cutoff);
882894
counter_session.setCutOff(cutoff);
883895
}
896+
else if (option == "BACKUP_DB")
897+
{
898+
backupDB = Util::Parse::Switch(arg);
899+
}
884900
else if (option == "SHARE_LOC")
885901
{
886902
bool b = Util::Parse::Switch(arg);

Application/WebViewer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class WebViewer : public IO::HTTPServer, public Setting
105105
bool supportPrometheus = false;
106106
bool thread_running = false;
107107
bool aboutPresent = false;
108+
bool backupDB = false;
108109

109110
std::vector<char> binary;
110111
std::vector<std::shared_ptr<MapTiles>> mapSources;

Tracking/DB.cpp

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ std::string DB::getSinglePathGeoJSON(int idx)
479479
{
480480
if (hasCoordinates)
481481
geojson += ",";
482-
482+
483483
// GeoJSON uses [longitude, latitude] format (note the order!)
484484
geojson += "[";
485485
geojson += std::to_string(paths[ptr].lon);
@@ -491,7 +491,7 @@ std::string DB::getSinglePathGeoJSON(int idx)
491491
t = paths[ptr].count;
492492
ptr = paths[ptr].next;
493493
}
494-
494+
495495
geojson += "]},\"properties\":{\"mmsi\":" + std::to_string(mmsi) + "}}";
496496
return geojson;
497497
}
@@ -993,3 +993,121 @@ void DB::Receive(const JSON::JSON *data, int len, TAG &tag)
993993

994994
Send(data, len, tag);
995995
}
996+
997+
bool DB::Save(std::ofstream &file)
998+
{
999+
std::lock_guard<std::mutex> lock(mtx);
1000+
1001+
// Write magic number and version
1002+
int magic = _DB_MAGIC;
1003+
int version = _DB_VERSION;
1004+
1005+
if (!file.write((const char *)&magic, sizeof(int)))
1006+
return false;
1007+
if (!file.write((const char *)&version, sizeof(int)))
1008+
return false;
1009+
1010+
// Write ship count first
1011+
if (!file.write((const char *)&count, sizeof(int)))
1012+
return false;
1013+
1014+
// Find the position to start writing backwards by counting valid ships
1015+
int ptr = first;
1016+
int valid_ships_seen = 0;
1017+
while (ptr != -1 && valid_ships_seen < count)
1018+
{
1019+
if (ships[ptr].mmsi != 0)
1020+
valid_ships_seen++;
1021+
if (valid_ships_seen < count)
1022+
ptr = ships[ptr].next;
1023+
}
1024+
1025+
// Now write ships by traversing backwards from that position
1026+
int ships_written = 0;
1027+
while (ptr != -1 && ships_written < count)
1028+
{
1029+
if (ships[ptr].mmsi != 0)
1030+
{
1031+
if (!file.write((const char *)&ships[ptr], sizeof(Ship)))
1032+
return false;
1033+
ships_written++;
1034+
}
1035+
ptr = ships[ptr].prev;
1036+
}
1037+
1038+
Info() << "DB: Saved " << ships_written << " ships to backup";
1039+
return true;
1040+
}
1041+
1042+
bool DB::Load(std::ifstream &file)
1043+
{
1044+
std::lock_guard<std::mutex> lock(mtx);
1045+
1046+
std::cerr << "Loading ships from backup file." << std::endl;
1047+
1048+
int magic = 0, version = 0;
1049+
1050+
// Read and verify magic number and version
1051+
if (!file.read((char *)&magic, sizeof(int)))
1052+
return false;
1053+
if (!file.read((char *)&version, sizeof(int)))
1054+
return false;
1055+
1056+
if (magic != _DB_MAGIC || version != _DB_VERSION)
1057+
{
1058+
Warning() << "DB: Invalid backup file format. Magic: " << std::hex << magic
1059+
<< ", Version: " << version;
1060+
return false;
1061+
}
1062+
1063+
// Read number of active ships
1064+
int active_ships = 0;
1065+
if (!file.read((char *)&active_ships, sizeof(int)))
1066+
return false;
1067+
1068+
if (active_ships < 0 || active_ships > Nships)
1069+
{
1070+
Warning() << "DB: Invalid ship count in backup file: " << active_ships;
1071+
return false;
1072+
}
1073+
1074+
// Load ships and validate chronological order (oldest first, then newer)
1075+
std::time_t previous_signal = 0;
1076+
for (int i = 0; i < active_ships; i++)
1077+
{
1078+
Ship temp_ship;
1079+
if (!file.read((char *)&temp_ship, sizeof(Ship)))
1080+
{
1081+
std::cout << "DB: Failed to read ship " << i << " from backup file." << std::endl;
1082+
return false;
1083+
}
1084+
1085+
// Validate chronological order - ships should be oldest first
1086+
if (i > 0 && temp_ship.last_signal < previous_signal)
1087+
{
1088+
Warning() << "DB: Ships not in chronological order in backup file. "
1089+
<< "Ship " << i << " (MMSI " << temp_ship.mmsi << ") "
1090+
<< "has timestamp " << temp_ship.last_signal
1091+
<< " which is older than previous ship timestamp " << previous_signal;
1092+
return false;
1093+
}
1094+
previous_signal = temp_ship.last_signal;
1095+
1096+
// Find or create ship entry using existing mechanisms
1097+
int ptr = findShip(temp_ship.mmsi);
1098+
if (ptr == -1)
1099+
ptr = createShip();
1100+
1101+
moveShipToFront(ptr);
1102+
1103+
// Copy ship data while preserving linked list pointers
1104+
int next_ptr = ships[ptr].next;
1105+
int prev_ptr = ships[ptr].prev;
1106+
ships[ptr] = temp_ship;
1107+
ships[ptr].next = next_ptr;
1108+
ships[ptr].prev = prev_ptr;
1109+
}
1110+
1111+
Info() << "DB: Restored " << count << " ships from backup";
1112+
return true;
1113+
}

Tracking/DB.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,12 @@ class DB : public StreamIn<JSON::JSON>,
162162
void setFilterOption(std::string &opt, std::string &arg) { filter.SetOption(opt, arg); }
163163

164164
std::string getBinaryMessagesJSON() const;
165+
166+
// Persistence functions for ship database
167+
bool Save(std::ofstream& file);
168+
bool Load(std::ifstream& file);
169+
170+
private:
171+
static const int _DB_MAGIC = 0x41495344; // "AISD" in hex
172+
static const int _DB_VERSION = 1;
165173
};

Tracking/Ships.h

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
/*
2-
Copyright(c) 2021-2025 jvde.github@gmail.com
2+
Copyright(c) 2021-2025 jvde.github@gmail.com
33
4-
This program is free software: you can redistribute it and/or modify
5-
it under the terms of the GNU General Public License as published by
6-
the Free Software Foundation, either version 3 of the License, or
7-
(at your option) any later version.
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
88
9-
This program is distributed in the hope that it will be useful,
10-
but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
GNU General Public License for more details.
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
1313
14-
You should have received a copy of the GNU General Public License
15-
along with this program. If not, see <https://www.gnu.org/licenses/>.
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
1616
*/
1717

1818
#pragma once
@@ -34,7 +34,8 @@ const int BASESTATION_MASK = (1 << 4) | (1 << 16) | (1 << 17) | (1 << 20) | (1 <
3434
const int SAR_MASK = 1 << 9;
3535
const int ATON_MASK = 1 << 21;
3636

37-
struct Ship {
37+
struct Ship
38+
{
3839
int prev, next;
3940
uint32_t mmsi;
4041
int count, msg_type, shipclass, mmsi_type, shiptype, heading, status, path_ptr;
@@ -52,9 +53,9 @@ struct Ship {
5253
int getShipTypeClassEri();
5354
int getShipTypeClass();
5455
void setType();
55-
void Serialize(std::vector<char>& v) const;
56-
bool getKML(std::string&) const;
57-
bool getGeoJSON(std::string&) const;
56+
void Serialize(std::vector<char> &v) const;
57+
bool getKML(std::string &) const;
58+
bool getGeoJSON(std::string &) const;
5859

5960
// Setters for PackedInt fields
6061
void setValidated(int val) { flags.set(0, 2, val); }

0 commit comments

Comments
 (0)