TerrainIO.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*
  2. This program is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. /*
  14. handle disk IO for terrain code
  15. */
  16. #include <AP_HAL/AP_HAL.h>
  17. #include <AP_Common/AP_Common.h>
  18. #include <AP_Math/AP_Math.h>
  19. #include <GCS_MAVLink/GCS_MAVLink.h>
  20. #include <GCS_MAVLink/GCS.h>
  21. #include <stdio.h>
  22. #include "AP_Terrain.h"
  23. #if AP_TERRAIN_AVAILABLE
  24. #include <AP_Filesystem/AP_Filesystem.h>
  25. extern const AP_HAL::HAL& hal;
  26. /*
  27. check for blocks that need to be read from disk
  28. */
  29. void AP_Terrain::check_disk_read(void)
  30. {
  31. for (uint16_t i=0; i<cache_size; i++) {
  32. if (cache[i].state == GRID_CACHE_DISKWAIT) {
  33. disk_block.block = cache[i].grid;
  34. disk_io_state = DiskIoWaitRead;
  35. return;
  36. }
  37. }
  38. }
  39. /*
  40. check for blocks that need to be written to disk
  41. */
  42. void AP_Terrain::check_disk_write(void)
  43. {
  44. for (uint16_t i=0; i<cache_size; i++) {
  45. if (cache[i].state == GRID_CACHE_DIRTY) {
  46. disk_block.block = cache[i].grid;
  47. disk_io_state = DiskIoWaitWrite;
  48. return;
  49. }
  50. }
  51. }
  52. /*
  53. Check if we need to do disk IO for grids.
  54. */
  55. void AP_Terrain::schedule_disk_io(void)
  56. {
  57. if (enable == 0 || !allocate()) {
  58. return;
  59. }
  60. if (!timer_setup) {
  61. timer_setup = true;
  62. hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_Terrain::io_timer, void));
  63. }
  64. switch (disk_io_state) {
  65. case DiskIoIdle:
  66. // look for a block that needs reading or writing
  67. check_disk_read();
  68. if (disk_io_state == DiskIoIdle) {
  69. // still idle, check for writes
  70. check_disk_write();
  71. }
  72. break;
  73. case DiskIoDoneRead: {
  74. // a read has completed
  75. int16_t cache_idx = find_io_idx(GRID_CACHE_DISKWAIT);
  76. if (cache_idx != -1) {
  77. if (disk_block.block.bitmap != 0) {
  78. // when bitmap is zero we read an empty block
  79. cache[cache_idx].grid = disk_block.block;
  80. }
  81. cache[cache_idx].state = GRID_CACHE_VALID;
  82. cache[cache_idx].last_access_ms = AP_HAL::millis();
  83. }
  84. disk_io_state = DiskIoIdle;
  85. break;
  86. }
  87. case DiskIoDoneWrite: {
  88. // a write has completed
  89. int16_t cache_idx = find_io_idx(GRID_CACHE_DIRTY);
  90. if (cache_idx != -1) {
  91. if (cache[cache_idx].grid.bitmap == disk_block.block.bitmap) {
  92. // only mark valid if more grids haven't been added
  93. cache[cache_idx].state = GRID_CACHE_VALID;
  94. }
  95. }
  96. disk_io_state = DiskIoIdle;
  97. break;
  98. }
  99. case DiskIoWaitWrite:
  100. case DiskIoWaitRead:
  101. // waiting for io_timer()
  102. break;
  103. }
  104. }
  105. /********************************************************
  106. All the functions below this point run in the IO timer context, which
  107. is a separate thread. The code uses the state machine controlled by
  108. disk_io_state to manage who has access to the structures and to
  109. prevent race conditions.
  110. The IO timer context owns the data when disk_io_state is
  111. DiskIoWaitWrite or DiskIoWaitRead. The main thread owns the data when
  112. disk_io_state is DiskIoIdle, DiskIoDoneWrite or DiskIoDoneRead
  113. All file operations are done by the IO thread.
  114. *********************************************************/
  115. /*
  116. open the current degree file
  117. */
  118. void AP_Terrain::open_file(void)
  119. {
  120. struct grid_block &block = disk_block.block;
  121. if (fd != -1 &&
  122. block.lat_degrees == file_lat_degrees &&
  123. block.lon_degrees == file_lon_degrees) {
  124. // already open on right file
  125. return;
  126. }
  127. if (file_path == nullptr) {
  128. const char* terrain_dir = hal.util->get_custom_terrain_directory();
  129. if (terrain_dir == nullptr) {
  130. terrain_dir = HAL_BOARD_TERRAIN_DIRECTORY;
  131. }
  132. if (asprintf(&file_path, "%s/NxxExxx.DAT", terrain_dir) <= 0) {
  133. io_failure = true;
  134. file_path = nullptr;
  135. return;
  136. }
  137. }
  138. if (file_path == nullptr) {
  139. io_failure = true;
  140. return;
  141. }
  142. char *p = &file_path[strlen(file_path)-12];
  143. if (*p != '/') {
  144. io_failure = true;
  145. return;
  146. }
  147. snprintf(p, 13, "/%c%02u%c%03u.DAT",
  148. block.lat_degrees<0?'S':'N',
  149. (unsigned)MIN(abs((int32_t)block.lat_degrees), 99),
  150. block.lon_degrees<0?'W':'E',
  151. (unsigned)MIN(abs((int32_t)block.lon_degrees), 999));
  152. // create directory if need be
  153. if (!directory_created) {
  154. *p = 0;
  155. directory_created = !AP::FS().mkdir(file_path);
  156. *p = '/';
  157. if (!directory_created) {
  158. if (errno == EEXIST) {
  159. // directory already existed
  160. directory_created = true;
  161. } else {
  162. // if we didn't succeed at making the directory, then IO failed
  163. io_failure = true;
  164. return;
  165. }
  166. }
  167. }
  168. if (fd != -1) {
  169. AP::FS().close(fd);
  170. }
  171. fd = AP::FS().open(file_path, O_RDWR|O_CREAT);
  172. if (fd == -1) {
  173. #if TERRAIN_DEBUG
  174. hal.console->printf("Open %s failed - %s\n",
  175. file_path, strerror(errno));
  176. #endif
  177. io_failure = true;
  178. return;
  179. }
  180. file_lat_degrees = block.lat_degrees;
  181. file_lon_degrees = block.lon_degrees;
  182. }
  183. /*
  184. seek to the right offset for disk_block
  185. */
  186. void AP_Terrain::seek_offset(void)
  187. {
  188. struct grid_block &block = disk_block.block;
  189. // work out how many longitude blocks there are at this latitude
  190. Location loc1, loc2;
  191. loc1.lat = block.lat_degrees*10*1000*1000L;
  192. loc1.lng = block.lon_degrees*10*1000*1000L;
  193. loc2.lat = block.lat_degrees*10*1000*1000L;
  194. loc2.lng = (block.lon_degrees+1)*10*1000*1000L;
  195. // shift another two blocks east to ensure room is available
  196. loc2.offset(0, 2*grid_spacing*TERRAIN_GRID_BLOCK_SIZE_Y);
  197. const Vector2f offset = loc1.get_distance_NE(loc2);
  198. uint16_t east_blocks = offset.y / (grid_spacing*TERRAIN_GRID_BLOCK_SIZE_Y);
  199. uint32_t file_offset = (east_blocks * block.grid_idx_x +
  200. block.grid_idx_y) * sizeof(union grid_io_block);
  201. if (AP::FS().lseek(fd, file_offset, SEEK_SET) != (off_t)file_offset) {
  202. #if TERRAIN_DEBUG
  203. hal.console->printf("Seek %lu failed - %s\n",
  204. (unsigned long)file_offset, strerror(errno));
  205. #endif
  206. AP::FS().close(fd);
  207. fd = -1;
  208. io_failure = true;
  209. }
  210. }
  211. /*
  212. write out disk_block
  213. */
  214. void AP_Terrain::write_block(void)
  215. {
  216. seek_offset();
  217. if (io_failure) {
  218. return;
  219. }
  220. disk_block.block.crc = get_block_crc(disk_block.block);
  221. ssize_t ret = AP::FS().write(fd, &disk_block, sizeof(disk_block));
  222. if (ret != sizeof(disk_block)) {
  223. #if TERRAIN_DEBUG
  224. hal.console->printf("write failed - %s\n", strerror(errno));
  225. #endif
  226. AP::FS().close(fd);
  227. fd = -1;
  228. io_failure = true;
  229. } else {
  230. AP::FS().fsync(fd);
  231. #if TERRAIN_DEBUG
  232. printf("wrote block at %ld %ld ret=%d mask=%07llx\n",
  233. (long)disk_block.block.lat,
  234. (long)disk_block.block.lon,
  235. (int)ret,
  236. (unsigned long long)disk_block.block.bitmap);
  237. #endif
  238. }
  239. disk_io_state = DiskIoDoneWrite;
  240. }
  241. /*
  242. read in disk_block
  243. */
  244. void AP_Terrain::read_block(void)
  245. {
  246. seek_offset();
  247. if (io_failure) {
  248. return;
  249. }
  250. int32_t lat = disk_block.block.lat;
  251. int32_t lon = disk_block.block.lon;
  252. ssize_t ret = AP::FS().read(fd, &disk_block, sizeof(disk_block));
  253. if (ret != sizeof(disk_block) ||
  254. disk_block.block.lat != lat ||
  255. disk_block.block.lon != lon ||
  256. disk_block.block.bitmap == 0 ||
  257. disk_block.block.spacing != grid_spacing ||
  258. disk_block.block.version != TERRAIN_GRID_FORMAT_VERSION ||
  259. disk_block.block.crc != get_block_crc(disk_block.block)) {
  260. #if TERRAIN_DEBUG
  261. printf("read empty block at %ld %ld ret=%d\n",
  262. (long)lat,
  263. (long)lon,
  264. (int)ret);
  265. #endif
  266. // a short read or bad data is not an IO failure, just a
  267. // missing block on disk
  268. memset(&disk_block, 0, sizeof(disk_block));
  269. disk_block.block.lat = lat;
  270. disk_block.block.lon = lon;
  271. disk_block.block.bitmap = 0;
  272. } else {
  273. #if TERRAIN_DEBUG
  274. printf("read block at %ld %ld ret=%d mask=%07llx\n",
  275. (long)lat,
  276. (long)lon,
  277. (int)ret,
  278. (unsigned long long)disk_block.block.bitmap);
  279. #endif
  280. }
  281. disk_io_state = DiskIoDoneRead;
  282. }
  283. /*
  284. timer called to do disk IO
  285. */
  286. void AP_Terrain::io_timer(void)
  287. {
  288. if (io_failure) {
  289. // don't keep trying io, so we don't thrash the filesystem
  290. // code while flying
  291. return;
  292. }
  293. switch (disk_io_state) {
  294. case DiskIoIdle:
  295. case DiskIoDoneRead:
  296. case DiskIoDoneWrite:
  297. // nothing to do
  298. break;
  299. case DiskIoWaitWrite:
  300. // need to write out the block
  301. open_file();
  302. if (fd == -1) {
  303. return;
  304. }
  305. write_block();
  306. break;
  307. case DiskIoWaitRead:
  308. // need to read in the block
  309. open_file();
  310. if (fd == -1) {
  311. return;
  312. }
  313. read_block();
  314. break;
  315. }
  316. }
  317. #endif // AP_TERRAIN_AVAILABLE