TaskManager: allocate the task objects dynamically
[xcsoar.git] / src / Task / TaskFileSeeYou.cpp
blobf0be1400652c26b8d994682e4edd560d5bac77e5
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "Task/TaskFileSeeYou.hpp"
25 #include "Util/Macros.hpp"
26 #include "IO/FileLineReader.hpp"
27 #include "Engine/Waypoint/Waypoints.hpp"
28 #include "Waypoint/WaypointReaderSeeYou.hpp"
29 #include "Task/ObservationZones/LineSectorZone.hpp"
30 #include "Task/ObservationZones/AnnularSectorZone.hpp"
31 #include "Task/ObservationZones/FAISectorZone.hpp"
32 #include "Task/ObservationZones/KeyholeZone.hpp"
33 #include "Task/ObservationZones/BGAEnhancedOptionZone.hpp"
34 #include "Task/ObservationZones/BGAFixedCourseZone.hpp"
35 #include "Engine/Task/Ordered/OrderedTask.hpp"
36 #include "Engine/Task/Ordered/Points/StartPoint.hpp"
37 #include "Engine/Task/Ordered/Points/FinishPoint.hpp"
38 #include "Engine/Task/Ordered/Points/AATPoint.hpp"
39 #include "Engine/Task/Ordered/Points/ASTPoint.hpp"
40 #include "Engine/Task/Factory/AbstractTaskFactory.hpp"
41 #include "Operation/Operation.hpp"
42 #include "Units/System.hpp"
44 struct SeeYouTaskInformation {
45 /** True = RT, False = AAT */
46 bool wp_dis;
47 /** AAT task time in seconds */
48 fixed task_time;
49 /** MaxAltStart in meters */
50 fixed max_start_altitude;
52 SeeYouTaskInformation():
53 wp_dis(true), task_time(fixed(0)), max_start_altitude(fixed(0)) {}
56 struct SeeYouTurnpointInformation {
57 /** CUP file contained info for this OZ */
58 bool valid;
60 enum Style {
61 FIXED,
62 SYMMETRICAL,
63 TO_NEXT_POINT,
64 TO_PREVIOUS_POINT,
65 TO_START_POINT,
66 } style;
68 bool is_line;
69 bool reduce;
71 fixed radius1, radius2, max_altitude;
72 Angle angle1, angle2, angle12;
74 SeeYouTurnpointInformation():
75 valid(false), style(SYMMETRICAL), is_line(false), reduce(false),
76 radius1(fixed(500)), radius2(fixed(500)),
77 max_altitude(fixed(0)),
78 angle1(Angle::Zero()),
79 angle2(Angle::Zero()),
80 angle12(Angle::Zero()) {}
83 static fixed
84 ParseTaskTime(const TCHAR* str)
86 int hh = 0, mm = 0, ss = 0;
87 TCHAR* end;
88 hh = _tcstol(str, &end, 10);
89 if (str != end && _tcslen(str) > 3 && str[2] == _T(':')) {
90 mm = _tcstol(str + 3, &end, 10);
91 if (str != end && _tcslen(str + 3) > 3 && str[5] == _T(':'))
92 ss = _tcstol(str + 6, NULL, 10);
94 return fixed(ss + mm * 60 + hh * 3600);
97 static SeeYouTurnpointInformation::Style
98 ParseStyle(const TCHAR* str)
100 int style = 1;
101 TCHAR* end;
102 style = _tcstol(str, &end, 10);
103 if (str == end)
104 style = 1;
106 return (SeeYouTurnpointInformation::Style)style;
109 static Angle
110 ParseAngle(const TCHAR* str)
112 int angle = 0;
113 TCHAR* end;
114 angle = _tcstol(str, &end, 10);
115 if (str == end)
116 angle = 0;
118 return Angle::Degrees(angle);
121 static fixed
122 ParseRadius(const TCHAR* str)
124 int radius = 500;
125 TCHAR* end;
126 radius = _tcstol(str, &end, 10);
127 if (str == end)
128 radius = 500;
130 return fixed(radius);
133 static fixed
134 ParseMaxAlt(const TCHAR* str)
136 fixed maxalt = fixed(0);
137 TCHAR* end;
138 maxalt = fixed(_tcstod(str, &end));
139 if (str == end)
140 return fixed(0);
142 if (_tcslen(end) >= 2 && end[0] == _T('f') && end[1] == _T('t'))
143 maxalt = Units::ToSysUnit(maxalt, Unit::FEET);
145 return maxalt;
149 * Parses the Options parameters line from See You task file for one task
150 * @param task_info Updated with Options
151 * @param params Input array of parameters preparsed from See You task file
152 * @param n_params number parameters in the line
154 static void
155 ParseOptions(SeeYouTaskInformation *task_info, const TCHAR *params[],
156 const size_t n_params)
158 // Iterate through available task options
159 for (unsigned i = 1; i < n_params; i++) {
160 if (_tcsncmp(params[i], _T("WpDis"), 5) == 0) {
161 // Parse WpDis option
162 if (_tcslen(params[i]) > 6 &&
163 _tcsncmp(params[i] + 6, _T("False"), 5) == 0)
164 task_info->wp_dis = false;
165 } else if (_tcsncmp(params[i], _T("TaskTime"), 8) == 0) {
166 // Parse TaskTime option
167 if (_tcslen(params[i]) > 9)
168 task_info->task_time = ParseTaskTime(params[i] + 9);
174 * Parses one ObsZone line from the See You task file
175 * @param turnpoint_infos Updated with the OZ info
176 * @param params Input array of parameters preparsed from See You task file
177 * @param n_params Number parameters in the line
178 * @return OZ index from CU (0 to n-1) or -1 if no OZ found
180 static int
181 ParseOZs(SeeYouTurnpointInformation turnpoint_infos[], const TCHAR *params[],
182 unsigned n_params)
184 // Read OZ index
185 TCHAR* end;
186 const int oz_index = _tcstol(params[0] + 8, &end, 10);
187 if (params[0] + 8 == end || oz_index >= 30)
188 return -1;
190 turnpoint_infos[oz_index].valid = true;
191 // Iterate through available OZ options
192 for (unsigned i = 1; i < n_params; i++) {
193 const TCHAR *pair = params[i];
194 SeeYouTurnpointInformation &tp_info = turnpoint_infos[oz_index];
196 if (_tcsncmp(pair, _T("style"), 5) == 0) {
197 if (_tcslen(pair) > 6)
198 tp_info.style = ParseStyle(pair + 6);
199 } else if (_tcsncmp(pair, _T("R1="), 3) == 0) {
200 if (_tcslen(pair) > 3)
201 tp_info.radius1 = ParseRadius(pair + 3);
202 } else if (_tcsncmp(pair, _T("A1="), 3) == 0) {
203 if (_tcslen(pair) > 3)
204 tp_info.angle1 = ParseAngle(pair + 3);
205 } else if (_tcsncmp(pair, _T("R2="), 3) == 0) {
206 if (_tcslen(pair) > 3)
207 tp_info.radius2 = ParseRadius(pair + 3);
208 } else if (_tcsncmp(pair, _T("A2="), 3) == 0) {
209 if (_tcslen(pair) > 3)
210 tp_info.angle2 = ParseAngle(pair + 3);
211 } else if (_tcsncmp(pair, _T("A12="), 4) == 0) {
212 if (_tcslen(pair) > 3)
213 tp_info.angle12 = ParseAngle(pair + 4);
214 } else if (_tcsncmp(pair, _T("max_altitude="), 7) == 0) {
215 if (_tcslen(pair) > 7)
216 tp_info.max_altitude = ParseMaxAlt(pair + 7);
217 } else if (_tcsncmp(pair, _T("is_line"), 4) == 0) {
218 if (_tcslen(pair) > 5 && pair[5] == _T('1'))
219 tp_info.is_line = true;
220 } else if (_tcsncmp(pair, _T("reduce"), 6) == 0) {
221 if (_tcslen(pair) > 7 && pair[7] == _T('1'))
222 tp_info.reduce = true;
225 return oz_index;
229 * Parses Options and OZs from See You task file
230 * @param reader. Points to first line of task after task "Waypoint list" line
231 * @param task_info Loads this with CU task options info
232 * @param turnpoint_infos Loads this with CU task tp info
234 static void
235 ParseCUTaskDetails(FileLineReader &reader, SeeYouTaskInformation *task_info,
236 SeeYouTurnpointInformation turnpoint_infos[])
238 // Read options/observation zones
239 TCHAR params_buffer[1024];
240 const TCHAR *params[20];
241 TCHAR *line;
242 int TPIndex = 0;
243 const unsigned int max_params = ARRAY_SIZE(params);
244 while ((line = reader.ReadLine()) != NULL &&
245 line[0] != _T('\"') && line[0] != _T(',')) {
246 const size_t n_params = WaypointReaderBase::
247 ExtractParameters(line, params_buffer, params, max_params, true);
249 if (_tcscmp(params[0], _T("Options")) == 0) {
250 // Options line found
251 ParseOptions(task_info, params, n_params);
253 } else if (_tcsncmp(params[0], _T("ObsZone"), 7) == 0) {
254 // Observation zone line found
255 if (_tcslen(params[0]) <= 8)
256 continue;
258 TPIndex = ParseOZs(turnpoint_infos, params, n_params);
259 if (TPIndex == 0)
260 task_info->max_start_altitude = turnpoint_infos[TPIndex].max_altitude;
262 } // end while
265 static bool isKeyhole(const SeeYouTurnpointInformation &turnpoint_infos)
267 return (fabs(turnpoint_infos.angle1.Degrees() - fixed(45)) < fixed(2) &&
268 fabs(turnpoint_infos.radius1 - fixed(10000)) < fixed(2) &&
269 fabs(turnpoint_infos.angle2.Degrees() - fixed(180)) < fixed(2) &&
270 fabs(turnpoint_infos.radius2 - fixed(500)) < fixed(2));
273 static bool isBGAFixedCourseZone(const SeeYouTurnpointInformation &turnpoint_infos)
275 return (fabs(turnpoint_infos.angle1.Degrees() - fixed(45)) < fixed(2) &&
276 fabs(turnpoint_infos.radius1 - fixed(20000)) < fixed(2) &&
277 fabs(turnpoint_infos.angle2.Degrees() - fixed(180)) < fixed(2) &&
278 fabs(turnpoint_infos.radius2 - fixed(500)) < fixed(2));
281 static bool isBGAEnhancedOptionZone(const SeeYouTurnpointInformation
282 &turnpoint_infos)
284 return (fabs(turnpoint_infos.angle1.Degrees() - fixed(90)) < fixed(2) &&
285 fabs(turnpoint_infos.radius1 - fixed(10000)) < fixed(2) &&
286 fabs(turnpoint_infos.angle2.Degrees() - fixed(180)) < fixed(2) &&
287 fabs(turnpoint_infos.radius2 - fixed(500)) < fixed(2));
291 * Creates the correct XCSoar OZ type from the See You OZ options for the point
292 * Note: there are several rules enforced here related to the combinations
293 * and types of Zones supported by XCSoar. When XCSoar adds more zone types,
294 * the logic below will need to be updated.
295 * @param turnpoint_infos Contains the See You turnpoint and OZ info
296 * @param current point position
297 * @param number wps in task
298 * @param array of wps for each point in task
299 * @param factType The XCSoar factory type
300 * @return the XCSoar OZ
302 static ObservationZonePoint*
303 CreateOZ(const SeeYouTurnpointInformation &turnpoint_infos,
304 unsigned pos, unsigned size, const Waypoint *wps[],
305 TaskFactoryType factType)
307 ObservationZonePoint* oz = NULL;
308 const bool is_intermediate = (pos > 0) && (pos < (size - 1));
309 const Waypoint *wp = wps[pos];
311 if (!turnpoint_infos.valid)
312 return NULL;
314 if (factType == TaskFactoryType::RACING &&
315 is_intermediate && isKeyhole(turnpoint_infos))
316 oz = new KeyholeZone(wp->location);
318 else if (factType == TaskFactoryType::RACING &&
319 is_intermediate && isBGAEnhancedOptionZone(turnpoint_infos))
320 oz = new BGAEnhancedOptionZone(wp->location);
322 else if (factType == TaskFactoryType::RACING &&
323 is_intermediate && isBGAFixedCourseZone(turnpoint_infos))
324 oz = new BGAFixedCourseZone(wp->location);
326 else if (!is_intermediate && turnpoint_infos.is_line) // special case "is_line"
327 oz = new LineSectorZone(wp->location, turnpoint_infos.radius1);
329 // special case "Cylinder"
330 else if (fabs(turnpoint_infos.angle1.Degrees() - fixed(180)) < fixed(1) )
331 oz = new CylinderZone(wp->location, turnpoint_infos.radius1);
333 else if (factType == TaskFactoryType::RACING) {
335 // XCSoar does not support fixed sectors for RT
336 if (turnpoint_infos.style == SeeYouTurnpointInformation::FIXED)
337 oz = new CylinderZone(wp->location, turnpoint_infos.radius1);
338 else
339 oz = new FAISectorZone(wp->location, is_intermediate);
341 } else if (is_intermediate) { //AAT intermediate point
342 Angle A12adj;
343 assert(wps[pos + 1]);
344 assert(wps[pos - 1]);
346 switch (turnpoint_infos.style) {
347 case SeeYouTurnpointInformation::FIXED: {
348 A12adj = turnpoint_infos.angle12.Reciprocal();
349 break;
351 case SeeYouTurnpointInformation::SYMMETRICAL: {
352 const Angle ap = wps[pos - 1]->location.Bearing(wp->location);
353 const Angle an = wps[pos + 1]->location.Bearing(wp->location);
354 A12adj = ap.HalfAngle(an).Reciprocal();
355 break;
358 case SeeYouTurnpointInformation::TO_NEXT_POINT: {
359 A12adj = wps[pos + 1]->location.Bearing(wp->location);
360 break;
362 case SeeYouTurnpointInformation::TO_PREVIOUS_POINT: {
363 A12adj = wps[pos - 1]->location.Bearing(wp->location);
364 break;
366 case SeeYouTurnpointInformation::TO_START_POINT: {
367 A12adj = wps[0]->location.Bearing(wp->location);
368 break;
372 const Angle RadialStart = (A12adj - turnpoint_infos.angle1).AsBearing();
373 const Angle RadialEnd = (A12adj + turnpoint_infos.angle1).AsBearing();
375 if (turnpoint_infos.radius2 > fixed(0) &&
376 (turnpoint_infos.angle2.AsBearing().Degrees()) < fixed(1)) {
377 oz = new AnnularSectorZone(wp->location, turnpoint_infos.radius1,
378 RadialStart, RadialEnd, turnpoint_infos.radius2);
379 } else {
380 oz = new SectorZone(wp->location, turnpoint_infos.radius1,
381 RadialStart, RadialEnd);
384 } else { // catch-all
385 oz = new CylinderZone(wp->location, turnpoint_infos.radius1);
388 return oz;
392 * Creates the XCSoar turnpoint from the See You parameters for the point
393 * @param pos The position of the point in the XCSoar task
394 * @param n_waypoints Number of points in the XCSoar task
395 * @param wp The waypoint
396 * @param fact The XCSoar factory
397 * @param oz The XCSoar OZ for the point
398 * @param factType The XCSoar factory type
399 * @return The point
401 static OrderedTaskPoint*
402 CreatePoint(unsigned pos, unsigned n_waypoints, const Waypoint *wp,
403 AbstractTaskFactory& fact, ObservationZonePoint* oz,
404 const TaskFactoryType factType)
406 OrderedTaskPoint *pt = NULL;
408 if (pos == 0)
409 pt = (oz ? fact.CreateStart(oz, *wp) : fact.CreateStart(*wp));
411 else if (pos == n_waypoints - 1)
412 pt = (oz ? fact.CreateFinish(oz, *wp) : fact.CreateFinish(*wp));
414 else if (factType == TaskFactoryType::RACING)
415 pt = (oz ? fact.CreateASTPoint(oz, *wp) : fact.CreateIntermediate(*wp));
417 else
418 pt = (oz ? fact.CreateAATPoint(oz, *wp) : fact.CreateIntermediate(*wp));
420 return pt;
423 static TCHAR *
424 AdvanceReaderToTask(FileLineReader &reader, const unsigned index)
426 // Skip lines until n-th task
427 unsigned count = 0;
428 bool in_task_section = false;
429 TCHAR *line;
430 for (unsigned i = 0; (line = reader.ReadLine()) != NULL; i++) {
431 if (in_task_section) {
432 if (line[0] == _T('\"') || line[0] == _T(',')) {
433 if (count == index)
434 break;
436 count++;
438 } else if (StringIsEqualIgnoreCase(line, _T("-----Related Tasks-----"))) {
439 in_task_section = true;
442 return line;
445 OrderedTask*
446 TaskFileSeeYou::GetTask(const TaskBehaviour &task_behaviour,
447 const Waypoints *waypoints, unsigned index) const
449 // Create FileReader for reading the task
450 FileLineReader reader(path, ConvertLineReader::AUTO);
451 if (reader.error())
452 return NULL;
454 // Read waypoints from the CUP file
455 Waypoints file_waypoints;
457 WaypointReaderSeeYou waypoint_file(0);
458 NullOperationEnvironment operation;
459 waypoint_file.Parse(file_waypoints, reader, operation);
461 file_waypoints.Optimise();
463 if (!reader.Rewind())
464 return NULL;
466 TCHAR *line = AdvanceReaderToTask(reader, index);
467 if (line == NULL)
468 return NULL;
470 // Read waypoint list
471 // e.g. "Club day 4 Racing task","085PRI","083BOJ","170D_K","065SKY","0844YY", "0844YY"
472 // TASK NAME , TAKEOFF, START , TP1 , TP2 , FINISH , LANDING
473 TCHAR waypoints_buffer[1024];
474 const TCHAR *wps[30];
475 size_t n_waypoints = WaypointReaderBase::
476 ExtractParameters(line, waypoints_buffer, wps, 30, true, _T('"')) - 3;
478 SeeYouTaskInformation task_info;
479 SeeYouTurnpointInformation turnpoint_infos[30];
480 const Waypoint *waypoints_in_task[30];
482 ParseCUTaskDetails(reader, &task_info, turnpoint_infos);
484 OrderedTask *task = new OrderedTask(task_behaviour);
485 task->SetFactory(task_info.wp_dis ?
486 TaskFactoryType::RACING : TaskFactoryType::AAT);
487 AbstractTaskFactory& fact = task->GetFactory();
488 const TaskFactoryType factType = task->GetFactoryType();
490 OrderedTaskBehaviour beh = task->GetOrderedTaskBehaviour();
491 if (factType == TaskFactoryType::AAT) {
492 beh.aat_min_time = task_info.task_time;
494 if (factType == TaskFactoryType::AAT ||
495 factType == TaskFactoryType::RACING) {
496 beh.start_constraints.max_height = (unsigned)task_info.max_start_altitude;
497 beh.start_constraints.max_height_ref = AltitudeReference::MSL;
499 task->SetOrderedTaskBehaviour(beh);
501 // mark task waypoints. Skip takeoff and landing point
502 for (unsigned i = 0; i < n_waypoints; i++) {
503 const Waypoint* file_wp = file_waypoints.LookupName(wps[i + 2]);
504 if (file_wp == NULL)
505 return NULL;
507 // Try to find waypoint by name
508 const Waypoint* wp = waypoints->LookupName(file_wp->name);
510 // If waypoint by name found and closer than 10m to the original
511 if (wp != NULL &&
512 wp->location.Distance(file_wp->location) <= fixed(10)) {
513 // Use this waypoint for the task
514 waypoints_in_task[i] = wp;
515 continue;
518 // Try finding the closest waypoint to the original one
519 wp = waypoints->GetNearest(file_wp->location, fixed(10));
521 // If closest waypoint found and closer than 10m to the original
522 if (wp != NULL &&
523 wp->location.Distance(file_wp->location) <= fixed(10)) {
524 // Use this waypoint for the task
525 waypoints_in_task[i] = wp;
526 continue;
529 // Use the original waypoint
530 waypoints_in_task[i] = file_wp;
533 //now create TPs and OZs
534 for (unsigned i = 0; i < n_waypoints; i++) {
536 ObservationZonePoint* oz = CreateOZ(turnpoint_infos[i], i, n_waypoints,
537 waypoints_in_task, factType);
538 assert(waypoints_in_task[i]);
539 OrderedTaskPoint *pt = CreatePoint(i, n_waypoints, waypoints_in_task[i],
540 fact, oz, factType);
542 if (pt != NULL)
543 fact.Append(*pt, false);
545 delete pt;
547 return task;
550 unsigned
551 TaskFileSeeYou::Count()
553 // Reset internal task name memory
554 namesuffixes.clear();
556 // Open the CUP file
557 FileLineReader reader(path, ConvertLineReader::AUTO);
558 if (reader.error())
559 return 0;
561 unsigned count = 0;
562 bool in_task_section = false;
563 TCHAR *line;
564 while ((line = reader.ReadLine()) != NULL) {
565 if (in_task_section) {
566 // If the line starts with a string or "nothing" followed
567 // by a comma it is a new task definition line
568 if (line[0] == _T('\"') || line[0] == _T(',')) {
569 // If we still have space in the task name list
570 if (count < namesuffixes.capacity()) {
571 // If the task doesn't have a name inside the file
572 if (line[0] == _T(','))
573 namesuffixes.append(NULL);
574 else {
575 // Ignore starting quote (")
576 line++;
578 // Save pointer to first character
579 TCHAR *name = line;
580 // Skip characters until next quote (") or end of string
581 while (line[0] != _T('\"') && line[0] != _T('\0'))
582 line++;
584 // Replace quote (") by end of string (null)
585 line[0] = _T('\0');
587 // Append task name to the list
588 if (_tcslen(name) > 0)
589 namesuffixes.append(_tcsdup(name));
590 else
591 namesuffixes.append(NULL);
595 // Increase the task counter
596 count++;
598 } else if (StringIsEqualIgnoreCase(line, _T("-----Related Tasks-----"))) {
599 // Found the marker -> all following lines are task lines
600 in_task_section = true;
604 // Return number of tasks found in the CUP file
605 return count;