Kstars

schedulermodulestate.h
1/*
2 SPDX-FileCopyrightText: 2023 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#pragma once
8
9#include <QObject>
10#include <QProcess>
11#include "ekos/ekos.h"
12#include "indi/indiweather.h"
13#include "kstarsdatetime.h"
14#include "geolocation.h"
15#include "schedulertypes.h"
16#include "ekos/capture/capturetypes.h"
17#include <QDateTime>
18#include <QUrl>
19
20class SchedulerJob;
21
22namespace Ekos
23{
24
26class SchedulerJob;
27
28/**
29 * @class SchedulerState
30 * @brief The SchedulerState class holds all attributes defining the scheduler's state.
31 */
32class SchedulerModuleState : public QObject
33{
35
36public:
37
38
39 SchedulerModuleState();
40
41 // ////////////////////////////////////////////////////////////////////
42 // Overall scheduler state
43 // ////////////////////////////////////////////////////////////////////
44 /**
45 * @brief init Set initial conditions that need to be set before starting
46 */
47 void init();
48
49 // ////////////////////////////////////////////////////////////////////
50 // profiles and scheduler jobs
51 // ////////////////////////////////////////////////////////////////////
52
53 const QString &currentProfile() const
54 {
55 return m_currentProfile;
56 }
57 /**
58 * @brief setCurrentProfile Set the current profile name.
59 * @param newName new current profile name
60 * @param signal send an update efent if true
61 */
62 void setCurrentProfile(const QString &newName, bool signal = true);
63
64 const QStringList &profiles() const
65 {
66 return m_profiles;
67 }
68 void updateProfiles(const QStringList &newProfiles);
69
70 SchedulerJob *activeJob() const
71 {
72 return m_activeJob;
73 }
74 void setActiveJob(SchedulerJob *newActiveJob);
75
76 /**
77 * @brief Return the master jobs only, slave jobs are ignored
78 */
79 QList<SchedulerJob *> leadJobs();
80
81 /**
82 * @brief Return the slave jobs only, master jobs are ignored
83 */
84 QList<SchedulerJob *> followerJobs();
85
86 QList<SchedulerJob *> &mutlableJobs()
87 {
88 return m_jobs;
89 }
90 const QList<SchedulerJob *> &jobs() const
91 {
92 return m_jobs;
93 }
94
95 void setJobs(QList<SchedulerJob *> &newJobs)
96 {
97 m_jobs = newJobs;
98 }
99
100 /**
101 * @brief updateStage Helper function that updates the stage label of the active job.
102 */
103 void updateJobStage(SchedulerJobStage stage);
104
105 /**
106 * @brief getJSONJobs get jobs in JSON format
107 * @return
108 */
109 QJsonArray getJSONJobs();
110
111
112 // ////////////////////////////////////////////////////////////////////
113 // state attributes accessors
114 // ////////////////////////////////////////////////////////////////////
115
116
117 bool dirty() const
118 {
119 return m_dirty;
120 }
121 void setDirty(bool value)
122 {
123 m_dirty = value;
124 }
125
126 // (coarse grained) execution state of the scheduler
127 const SchedulerState &schedulerState() const
128 {
129 return m_schedulerState;
130 }
131 void setSchedulerState(const SchedulerState &newState);
132
133 const StartupState &startupState() const
134 {
135 return m_startupState;
136 }
137
138 int currentPosition() const
139 {
140 return m_currentPosition;
141 }
142 void setCurrentPosition(int newCurrentPosition);
143
144 void setStartupState(StartupState state);
145
146 const QUrl &startupScriptURL() const
147 {
148 return m_startupScriptURL;
149 }
150 void setStartupScriptURL(const QUrl &newURL)
151 {
152 m_startupScriptURL = newURL;
153 }
154
155 const ShutdownState &shutdownState() const
156 {
157 return m_shutdownState;
158 }
159 void setShutdownState(ShutdownState state);
160
161 const QUrl &shutdownScriptURL() const
162 {
163 return m_shutdownScriptURL;
164 }
165 void setShutdownScriptURL(const QUrl &newShutdownScriptURL)
166 {
167 m_shutdownScriptURL = newShutdownScriptURL;
168 }
169
170 const ParkWaitState &parkWaitState() const
171 {
172 return m_parkWaitState;
173 }
174 void setParkWaitState(ParkWaitState state);
175
176 /**
177 * @brief True if the scheduler is between iterations and delaying longer than the typical update period.
178 */
179 bool currentlySleeping()
180 {
181 return iterationTimer().isActive() && timerState() == RUN_WAKEUP;
182 }
183
184 // ////////////////////////////////////////////////////////////////////
185 // job handling
186 // ////////////////////////////////////////////////////////////////////
187
188 /**
189 * @brief removeJob Remove the job from the job list at the given position.
190 * If this is the currently active job, don't remove it and return false.
191 * @return true iff removing succeeded
192 */
193 bool removeJob(const int currentRow);
194
195 /**
196 * @brief refreshSlaveLists walk through the jobs and update the slave lists
197 */
198 void refreshFollowerLists();
199
200 /**
201 * @brief walk through the job list and find the first master job
202 */
203 SchedulerJob *findLead(int position, bool upward = true);
204
205
206 // ////////////////////////////////////////////////////////////////////
207 // Controls for the preemptive shutdown feature.
208 // ////////////////////////////////////////////////////////////////////
209 // Is the scheduler shutting down until later when it will resume a job?
210 void enablePreemptiveShutdown(const QDateTime &wakeupTime);
211 void disablePreemptiveShutdown();
212 const QDateTime &preemptiveShutdownWakeupTime() const;
213 bool preemptiveShutdown() const;
214
215
216 // ////////////////////////////////////////////////////////////////////
217 // overall EKOS state
218 // ////////////////////////////////////////////////////////////////////
219 EkosState ekosState() const
220 {
221 return m_ekosState;
222 }
223 void setEkosState(EkosState state);
224 // last communication result with EKOS
225 CommunicationStatus ekosCommunicationStatus() const
226 {
227 return m_EkosCommunicationStatus;
228 }
229 void setEkosCommunicationStatus(CommunicationStatus newEkosCommunicationStatus)
230 {
231 m_EkosCommunicationStatus = newEkosCommunicationStatus;
232 }
233 // counter for failed EKOS connection attempts
234 void resetEkosConnectFailureCount(uint8_t newEkosConnectFailureCount = 0)
235 {
236 m_ekosConnectFailureCount = newEkosConnectFailureCount;
237 }
238 bool increaseEkosConnectFailureCount();
239
240 void resetParkingCapFailureCount(uint8_t value = 0)
241 {
242 m_parkingCapFailureCount = value;
243 }
244 bool increaseParkingCapFailureCount();
245 void resetParkingMountFailureCount(uint8_t value = 0)
246 {
247 m_parkingMountFailureCount = value;
248 }
249 bool increaseParkingMountFailureCount();
250 uint8_t parkingMountFailureCount() const
251 {
252 return m_parkingMountFailureCount;
253 }
254 void resetParkingDomeFailureCount(uint8_t value = 0)
255 {
256 m_parkingDomeFailureCount = value;
257 }
258 bool increaseParkingDomeFailureCount();
259
260 int indexToUse() const
261 {
262 return m_IndexToUse;
263 }
264 void setIndexToUse(int newIndexToUse)
265 {
266 m_IndexToUse = newIndexToUse;
267 }
268
269 int healpixToUse() const
270 {
271 return m_HealpixToUse;
272 }
273 void setHealpixToUse(int newHealpixToUse)
274 {
275 m_HealpixToUse = newHealpixToUse;
276 }
277
278 CapturedFramesMap &capturedFramesCount()
279 {
280 return m_CapturedFramesCount;
281 }
282
283 void setCapturedFramesCount(const CapturedFramesMap &newCapturedFramesCount)
284 {
285 m_CapturedFramesCount = newCapturedFramesCount;
286 }
287
288 /**
289 * @brief resetFailureCounters Reset all failure counters
290 */
291 void resetFailureCounters();
292
293 // ////////////////////////////////////////////////////////////////////
294 // overall INDI state
295 // ////////////////////////////////////////////////////////////////////
296 INDIState indiState() const
297 {
298 return m_indiState;
299 }
300 void setIndiState(INDIState state);
301 // last communication result with INDI
302 CommunicationStatus indiCommunicationStatus() const
303 {
304 return m_INDICommunicationStatus;
305 }
306 void setIndiCommunicationStatus(CommunicationStatus newINDICommunicationStatus)
307 {
308 m_INDICommunicationStatus = newINDICommunicationStatus;
309 emit indiCommunicationStatusChanged(m_INDICommunicationStatus);
310 }
311 // counters for failed INDI connection attempts
312 void resetIndiConnectFailureCount(uint8_t newIndiConnectFailureCount = 0)
313 {
314 m_indiConnectFailureCount = newIndiConnectFailureCount;
315 }
316 bool increaseIndiConnectFailureCount();
317 /**
318 * @brief isINDIConnected Determines the status of the INDI connection.
319 * @return True if INDI connection is up and usable, else false.
320 */
321 bool isINDIConnected() const
322 {
323 return (indiCommunicationStatus() == Ekos::Success);
324 }
325 // ////////////////////////////////////////////////////////////////////
326 // device states
327 // ////////////////////////////////////////////////////////////////////
328 bool mountReady() const
329 {
330 return m_MountReady;
331 }
332 void setMountReady(bool readiness)
333 {
334 m_MountReady = readiness;
335 }
336 bool captureReady() const
337 {
338 return m_CaptureReady;
339 }
340 void setCaptureReady(bool readiness)
341 {
342 m_CaptureReady = readiness;
343 }
344 bool domeReady() const
345 {
346 return m_DomeReady;
347 }
348 void setDomeReady(bool readiness)
349 {
350 m_DomeReady = readiness;
351 }
352 bool capReady() const
353 {
354 return m_CapReady;
355 }
356 void setCapReady(bool readiness)
357 {
358 m_CapReady = readiness;
359 }
360
361 uint16_t captureBatch() const
362 {
363 return m_captureBatch;
364 }
365 void resetCaptureBatch()
366 {
367 m_captureBatch = 0;
368 }
369 uint16_t increaseCaptureBatch()
370 {
371 return m_captureBatch++;
372 }
373
374 uint8_t captureFailureCount() const
375 {
376 return m_captureFailureCount;
377 }
378 void resetCaptureFailureCount()
379 {
380 m_captureFailureCount = 0;
381 }
382 bool increaseCaptureFailureCount();
383
384 uint8_t focusFailureCount(const QString &trainname) const
385 {
386 return m_focusFailureCount[trainname];
387 }
388 void resetFocusFailureCount(const QString &trainname)
389 {
390 m_focusFailureCount[trainname] = 0;
391 }
392 void resetFocusFailureCount()
393 {
394 m_focusFailureCount.clear();
395 }
396 bool increaseFocusFailureCount(const QString &trainname);
397
398 bool increaseAllFocusFailureCounts();
399
400 bool autofocusCompleted(const QString &trainname) const;
401 void setAutofocusCompleted(const QString &trainname, bool value);
402 bool autofocusCompleted() const;
403
404 void resetAutofocusCompleted()
405 {
406 m_autofocusCompleted.clear();
407 }
408
409 uint8_t guideFailureCount() const
410 {
411 return m_guideFailureCount;
412 }
413 void resetGuideFailureCount()
414 {
415 m_guideFailureCount = 0;
416 }
417 bool increaseGuideFailureCount();
418
419 uint8_t alignFailureCount() const
420 {
421 return m_alignFailureCount;
422 }
423 void resetAlignFailureCount()
424 {
425 m_alignFailureCount = 0;
426 }
427 bool increaseAlignFailureCount();
428
429 int restartGuidingInterval() const
430 {
431 return m_restartGuidingInterval;
432 }
433
434 const KStarsDateTime &restartGuidingTime() const
435 {
436 return m_restartGuidingTime;
437 }
438
439 ISD::Weather::Status weatherStatus() const
440 {
441 return m_weatherStatus;
442 }
443 void setWeatherStatus(ISD::Weather::Status newWeatherStatus)
444 {
445 m_weatherStatus = newWeatherStatus;
446 }
447
448 // ////////////////////////////////////////////////////////////////////
449 // Timers and time
450 // ////////////////////////////////////////////////////////////////////
451 // Returns milliseconds since startCurrentOperationTImer() was called.
452 qint64 getCurrentOperationMsec() const;
453 // Starts the above operation timer.
454 // TODO. It would be better to make this a class and give each operation its own timer.
455 // TODO. These should be disabled once no longer relevant.
456 // These are implement with a KStarsDateTime instead of a QTimer type class
457 // so that the simulated clock can be used.
458 void startCurrentOperationTimer();
459
460 // Controls for the guiding timer, which restarts guiding after failure.
461 void cancelGuidingTimer();
462 bool isGuidingTimerActive();
463 void startGuidingTimer(int milliseconds);
464
465 /** @brief Setter used in testing to fix the local time. Otherwise getter gets from KStars instance. */
466 /** @{ */
467 static KStarsDateTime getLocalTime();
468 static void setLocalTime(KStarsDateTime *time)
469 {
470 storedLocalTime = time;
471 }
472 static bool hasLocalTime()
473 {
474 return storedLocalTime != nullptr;
475 }
476
477 /** @} */
478
479
480 // ////////////////////////////////////////////////////////////////////
481 // Astronomical calculations
482 // ////////////////////////////////////////////////////////////////////
483 /**
484 * @brief calculateDawnDusk find the next astronomical dawn and dusk after the current date and time of observation
485 */
486 static void calculateDawnDusk(QDateTime const &when, QDateTime &nDawn, QDateTime &nDusk);
487
488 /**
489 * @brief calculateDawnDusk Calculate dawn and dusk times for today
490 */
491 void calculateDawnDusk();
492
493 static QDateTime Dawn()
494 {
495 return m_Dawn;
496 }
497 static QDateTime Dusk()
498 {
499 return m_Dusk;
500 }
501 static QDateTime PreDawnDateTime()
502 {
503 return m_PreDawnDateTime;
504 }
505
506 /** @brief Setter used in testing to fix the geo location. Otherwise getter gets from KStars instance. */
507 /** @{ */
508 static const GeoLocation *getGeo();
509 static void setGeo(GeoLocation *geo)
510 {
511 storedGeo = geo;
512 }
513 static bool hasGeo();
514
515 // ////////////////////////////////////////////////////////////////////
516 // Scheduler iterations
517 // ////////////////////////////////////////////////////////////////////
518
519 // Setup the parameters for the next scheduler iteration.
520 // When milliseconds is not passed in, it uses m_UpdatePeriodMs.
521 void setupNextIteration(SchedulerTimerState nextState);
522 void setupNextIteration(SchedulerTimerState nextState, int milliseconds);
523
524 SchedulerTimerState timerState() const
525 {
526 return m_timerState;
527 }
528
529 void setTimerState(SchedulerTimerState newTimerState)
530 {
531 m_timerState = newTimerState;
532 }
533
534 QTimer &iterationTimer()
535 {
536 return m_iterationTimer;
537 }
538
539 bool iterationSetup() const
540 {
541 return m_iterationSetup;
542 }
543 void setIterationSetup(bool setup)
544 {
545 m_iterationSetup = setup;
546 }
547
548 qint64 startMSecs() const
549 {
550 return m_startMSecs;
551 }
552 void setStartMSecs(qint64 value)
553 {
554 m_startMSecs = value;
555 }
556 int increaseSchedulerIteration()
557 {
558 return ++m_schedulerIteration;
559 }
560 void resetSchedulerIteration()
561 {
562 m_schedulerIteration = 0;
563 }
564
565 int timerInterval() const
566 {
567 return m_timerInterval;
568 }
569 void setTimerInterval(int value)
570 {
571 m_timerInterval = value;
572 }
573
574 void setUpdatePeriodMs(int ms)
575 {
576 m_UpdatePeriodMs = ms;
577 }
578 int updatePeriodMs() const
579 {
580 return m_UpdatePeriodMs;
581 }
582
583 uint sequenceExecutionCounter() const
584 {
585 return m_sequenceExecutionCounter;
586 }
587 void resetSequenceExecutionCounter()
588 {
589 m_sequenceExecutionCounter = 1;
590 }
591 void increaseSequenceExecutionCounter()
592 {
593 m_sequenceExecutionCounter++;
594 }
595
596 static uint maxFailureAttempts();
597
598 QStringList &logText()
599 {
600 return m_logText;
601 }
602 QString getLogText()
603 {
604 return logText().join("\n");
605 }
606 void clearLog();
607
608 /**
609 * @brief checkRepeatSequence Check if the entire job sequence might be repeated
610 * @return true if the checkbox is set and the number of iterations is below the
611 * configured threshold
612 */
613 bool checkRepeatSequence();
614
615 void resetSolverIteration()
616 {
617 m_solverIteration = 0;
618 }
619 uint32_t increaseSolverIteration()
620 {
621 return ++m_solverIteration;
622 }
623
624signals:
625 // ////////////////////////////////////////////////////////////////////
626 // communication with the UI
627 // ////////////////////////////////////////////////////////////////////
628 // State change of EKOS
629 void ekosStateChanged(EkosState state);
630 // State change of INDI
631 void indiStateChanged(INDIState state);
632 // High level INDI state changes
633 void indiCommunicationStatusChanged(CommunicationStatus status);
634 // overall scheduler state changed
635 void schedulerStateChanged(SchedulerState state);
636 // startup state
637 void startupStateChanged(StartupState state);
638 // shutdown state
639 void shutdownStateChanged(ShutdownState state);
640 // parking state
641 void parkWaitStateChanged(ParkWaitState state);
642 // profiles updated
643 void profilesChanged();
644 // current profile changed
645 void currentProfileChanged();
646 // new log text for the module log window
647 void newLog(const QString &text);
648 // current position in the job list changed
649 void currentPositionChanged(int pos);
650 // job stage of the current job changed
651 void jobStageChanged(SchedulerJobStage stage);
652 // night time calculation updated
653 void updateNightTime(SchedulerJob const * job = nullptr);
654
655
656private:
657 // ////////////////////////////////////////////////////////////////////
658 // Scheduler jobs
659 // ////////////////////////////////////////////////////////////////////
660 // List of all jobs as entered by the user or file
662 // Active master job
663 SchedulerJob *m_activeJob { nullptr };
664
665 // ////////////////////////////////////////////////////////////////////
666 // state attributes
667 // ////////////////////////////////////////////////////////////////////
668 // coarse grained state describing the general execution state
669 SchedulerState m_schedulerState { SCHEDULER_IDLE };
670 // states of the scheduler startup
671 StartupState m_startupState { STARTUP_IDLE };
672 // Startup script URL
673 QUrl m_startupScriptURL;
674 // states of the scheduler shutdown
675 ShutdownState m_shutdownState { SHUTDOWN_IDLE };
676 // current position on the job list - necessary if there is no line selected in the
677 // UI, for example after deleting a row.
678 int m_currentPosition { -1 };
679 // Shutdown script URL
680 QUrl m_shutdownScriptURL;
681 // states of parking
682 ParkWaitState m_parkWaitState { PARKWAIT_IDLE };
683 // current profile
684 QString m_currentProfile;
685 // all profiles
686 QStringList m_profiles;
687 /// Store all log strings
688 QStringList m_logText;
689 // Was job modified and needs saving?
690 bool m_dirty { false };
691
692 // EKOS state describing whether EKOS is running (remember that the scheduler
693 // does not need EKOS running).
694 EkosState m_ekosState { EKOS_IDLE };
695 // Execution state of INDI
696 INDIState m_indiState { INDI_IDLE };
697 // Last communication result with EKOS and INDI
698 CommunicationStatus m_EkosCommunicationStatus { Ekos::Idle };
699 CommunicationStatus m_INDICommunicationStatus { Ekos::Idle };
700
701 // device readiness
702 bool m_MountReady { false };
703 bool m_CaptureReady { false };
704 bool m_DomeReady { false };
705 bool m_CapReady { false };
706
707 // Restricts (the internal solver) to using the index and healpix
708 // from the previous solve, if that solve was successful, when
709 // doing the pointing check. -1 means no restriction.
710 int m_IndexToUse { -1 };
711 int m_HealpixToUse { -1 };
712
713 // Check if initial autofocus is completed and do not run autofocus until
714 // there is a change is telescope position/alignment.
715 QMap <QString, bool> m_autofocusCompleted;
716
717 // Used when solving position every nth capture.
718 uint32_t m_solverIteration {0};
719
720
721 // Keep watch of weather status
722 ISD::Weather::Status m_weatherStatus { ISD::Weather::WEATHER_IDLE };
723
724 // ////////////////////////////////////////////////////////////////////
725 // counters
726 // ////////////////////////////////////////////////////////////////////
727 // count for job sequence iteration
728 uint m_sequenceExecutionCounter { 1 };
729 // Keep track of INDI connection failures
730 uint8_t m_indiConnectFailureCount { 0 };
731 // Keep track of Ekos connection failures
732 uint8_t m_ekosConnectFailureCount { 0 };
733 // failures parking dust cap
734 uint8_t m_parkingCapFailureCount { 0 };
735 // failures parking mount
736 uint8_t m_parkingMountFailureCount { 0 };
737 // failures parking dome
738 uint8_t m_parkingDomeFailureCount { 0 };
739 // How many repeated job batches did we complete thus far?
740 uint16_t m_captureBatch { 0 };
741 // Keep track of Ekos capture module failures
742 uint8_t m_captureFailureCount { 0 };
743 // Keep track of Ekos focus module failures
744 QMap <QString, uint8_t> m_focusFailureCount;
745 // Keep track of Ekos guide module failures
746 uint8_t m_guideFailureCount { 0 };
747 // Keep track of Ekos align module failures
748 uint8_t m_alignFailureCount { 0 };
749 // frames count for all signatures
750 CapturedFramesMap m_CapturedFramesCount;
751
752 // ////////////////////////////////////////////////////////////////////
753 // Scheduler iterations
754 // ////////////////////////////////////////////////////////////////////
755
756 // The type of scheduler iteration that should be run next.
757 SchedulerTimerState m_timerState { RUN_NOTHING };
758 // Variable keeping the number of millisconds the scheduler should wait
759 // after the current scheduler iteration.
760 int m_timerInterval { -1 };
761 // Whether the scheduler has been setup for the next iteration,
762 // that is, whether timerInterval and timerState have been set this iteration.
763 bool m_iterationSetup { false };
764 // The timer used to wakeup the scheduler between iterations.
765 QTimer m_iterationTimer;
766 // Counter for how many scheduler iterations have been processed.
767 int m_schedulerIteration { 0 };
768 // The time when the scheduler first started running iterations.
769 qint64 m_startMSecs { 0 };
770 // This is the time between typical scheduler iterations.
771 // The time can be modified for testing.
772 int m_UpdatePeriodMs = 1000;
773
774 // ////////////////////////////////////////////////////////////////////
775 // time and timers
776 // ////////////////////////////////////////////////////////////////////
777 // constants for current dawn and dusk
778 /// Store next dawn to calculate dark skies range
779 static QDateTime m_Dawn;
780 /// Store next dusk to calculate dark skies range
781 static QDateTime m_Dusk;
782 /// Pre-dawn is where we stop all jobs, it is a user-configurable value before Dawn.
783 static QDateTime m_PreDawnDateTime;
784 // Generic time to track timeout of current operation in progress.
785 // Used by startCurrentOperationTimer() and getCurrentOperationMsec().
786 KStarsDateTime currentOperationTime;
787 bool currentOperationTimeStarted { false };
788 // Delay for restarting the guider
789 int m_restartGuidingInterval { -1 };
790 KStarsDateTime m_restartGuidingTime;
791 // Used in testing, instead of KStars::Instance() resources
792 static KStarsDateTime *storedLocalTime;
793 // The various preemptiveShutdown states are controlled by this one variable.
794 QDateTime m_preemptiveShutdownWakeupTime;
795
796 // These are used in testing, instead of KStars::Instance() resources
797 static GeoLocation *storedGeo;
798
799};
800} // Ekos namespace
The SchedulerProcess class holds the entire business logic for controlling the execution of the EKOS ...
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
The SchedulerState class holds all attributes defining the scheduler's state.
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:83
QMap< QString, uint16_t > CapturedFramesMap
mapping signature --> frames count
void clear()
Q_OBJECTQ_OBJECT
QString join(QChar separator) const const
bool isActive() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:56:00 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.