~ubuntu-branches/debian/sid/calligraplan/sid

« back to all changes in this revision

Viewing changes to src/libs/kernel/kptschedule.cpp

  • Committer: Package Import Robot
  • Author(s): Pino Toscano
  • Date: 2018-02-01 18:20:19 UTC
  • Revision ID: package-import@ubuntu.com-20180201182019-1qo7qaim5wejm5k9
Tags: upstream-3.1.0
ImportĀ upstreamĀ versionĀ 3.1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the KDE project
 
2
 Copyright (C) 2005 - 2011, 2012 Dag Andersen <danders@get2net.dk>
 
3
 Copyright (C) 2016 Dag Andersen <danders@get2net.dk>
 
4
 
 
5
 This library is free software; you can redistribute it and/or
 
6
 modify it under the terms of the GNU Library General Public
 
7
 License as published by the Free Software Foundation; either
 
8
 version 2 of the License, or (at your option) any later version.
 
9
 
 
10
 This library is distributed in the hope that it will be useful,
 
11
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 Library General Public License for more details.
 
14
 
 
15
 You should have received a copy of the GNU Library General Public License
 
16
 along with this library; see the file COPYING.LIB.  If not, write to
 
17
 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
18
 Boston, MA 02110-1301, USA.
 
19
*/
 
20
 
 
21
#include "kptschedule.h"
 
22
 
 
23
#include "kptappointment.h"
 
24
#include "kptdatetime.h"
 
25
#include "kptduration.h"
 
26
#include "kptnode.h"
 
27
#include "kptproject.h"
 
28
#include "kpttask.h"
 
29
#include "kptxmlloaderobject.h"
 
30
#include "kptschedulerplugin.h"
 
31
#include "kptdebug.h"
 
32
 
 
33
#include <KoXmlReader.h>
 
34
 
 
35
#include <KLocalizedString>
 
36
 
 
37
#include <QStringList>
 
38
 
 
39
 
 
40
namespace KPlato
 
41
{
 
42
 
 
43
class ScheduleManager;
 
44
 
 
45
Schedule::Log::Log( const Node *n, int sev, const QString &msg, int ph )
 
46
    : node( n ), resource( 0 ), message( msg ), severity( sev ), phase( ph )
 
47
{
 
48
    Q_ASSERT( n );
 
49
//     debugPlan<<*this<<nodeId;
 
50
}
 
51
 
 
52
Schedule::Log::Log( const Node *n, const Resource *r, int sev, const QString &msg, int ph )
 
53
    : node( n ), resource( r ), message( msg ), severity( sev ), phase( ph )
 
54
{
 
55
    Q_ASSERT( r );
 
56
//     debugPlan<<*this<<resourceId;
 
57
}
 
58
 
 
59
Schedule::Log::Log( const Log &other )
 
60
{
 
61
    node = other.node;
 
62
    resource = other.resource;
 
63
    message = other.message;
 
64
    severity = other.severity;
 
65
    phase = other.phase;
 
66
}
 
67
 
 
68
Schedule::Log &Schedule::Log::operator=( const Schedule::Log &other )
 
69
{
 
70
    node = other.node;
 
71
    resource = other.resource;
 
72
    message = other.message;
 
73
    severity = other.severity;
 
74
    phase = other.phase;
 
75
    return *this;
 
76
}
 
77
 
 
78
Schedule::Schedule()
 
79
        : m_type( Expected ),
 
80
        m_id( 0 ),
 
81
        m_deleted( false ),
 
82
        m_parent( 0 ),
 
83
        m_obstate( OBS_Parent ),
 
84
        m_calculationMode( Schedule::Scheduling ),
 
85
        notScheduled( true )
 
86
{
 
87
    initiateCalculation();
 
88
}
 
89
 
 
90
Schedule::Schedule( Schedule *parent )
 
91
        : m_type( Expected ),
 
92
        m_id( 0 ),
 
93
        m_deleted( false ),
 
94
        m_parent( parent ),
 
95
        m_obstate( OBS_Parent ),
 
96
        m_calculationMode( Schedule::Scheduling ),
 
97
        notScheduled( true )
 
98
{
 
99
 
 
100
    if ( parent ) {
 
101
        m_name = parent->name();
 
102
        m_type = parent->type();
 
103
        m_id = parent->id();
 
104
    }
 
105
    initiateCalculation();
 
106
    //debugPlan<<"("<<this<<") Name: '"<<name<<"' Type="<<type<<" id="<<id;
 
107
}
 
108
 
 
109
Schedule::Schedule( const QString& name, Type type, long id )
 
110
        : m_name( name ),
 
111
        m_type( type ),
 
112
        m_id( id ),
 
113
        m_deleted( false ),
 
114
        m_parent( 0 ),
 
115
        m_obstate( OBS_Parent ),
 
116
        m_calculationMode( Schedule::Scheduling ),
 
117
        notScheduled( true )
 
118
{
 
119
    //debugPlan<<"("<<this<<") Name: '"<<name<<"' Type="<<type<<" id="<<id;
 
120
    initiateCalculation();
 
121
}
 
122
 
 
123
Schedule::~Schedule()
 
124
{
 
125
}
 
126
 
 
127
void Schedule::setParent( Schedule *parent )
 
128
{
 
129
    m_parent = parent;
 
130
}
 
131
 
 
132
void Schedule::setDeleted( bool on )
 
133
{
 
134
    //debugPlan<<"deleted="<<on;
 
135
    m_deleted = on;
 
136
    //changed( this ); don't do this!
 
137
}
 
138
 
 
139
bool Schedule::isDeleted() const
 
140
{
 
141
    return m_parent == 0 ? m_deleted : m_parent->isDeleted();
 
142
}
 
143
 
 
144
void Schedule::setType( const QString& type )
 
145
{
 
146
    m_type = Expected;
 
147
    if ( type == "Expected" )
 
148
        m_type = Expected;
 
149
}
 
150
 
 
151
QString Schedule::typeToString( bool translate ) const
 
152
{
 
153
    if ( translate ) {
 
154
        return i18n( "Expected" );
 
155
    } else {
 
156
        return "Expected";
 
157
    }
 
158
}
 
159
 
 
160
QStringList Schedule::state() const
 
161
{
 
162
    QStringList lst;
 
163
    if ( m_deleted )
 
164
        lst << SchedulingState::deleted();
 
165
    if ( notScheduled )
 
166
        lst << SchedulingState::notScheduled();
 
167
    if ( constraintError )
 
168
        lst << SchedulingState::constraintsNotMet();
 
169
    if ( resourceError )
 
170
        lst << SchedulingState::resourceNotAllocated();
 
171
    if ( resourceNotAvailable )
 
172
        lst << SchedulingState::resourceNotAvailable();
 
173
    if ( resourceOverbooked )
 
174
        lst << SchedulingState::resourceOverbooked();
 
175
    if ( effortNotMet )
 
176
        lst << SchedulingState::effortNotMet();
 
177
    if ( schedulingError )
 
178
        lst << SchedulingState::schedulingError();
 
179
    if ( lst.isEmpty() )
 
180
        lst << SchedulingState::scheduled();
 
181
    return lst;
 
182
}
 
183
 
 
184
bool Schedule::isBaselined() const
 
185
{
 
186
    if ( m_parent ) {
 
187
        return m_parent->isBaselined();
 
188
    }
 
189
    return false;
 
190
}
 
191
 
 
192
bool Schedule::usePert() const
 
193
{
 
194
    if ( m_parent ) {
 
195
        return m_parent->usePert();
 
196
    }
 
197
    return false;
 
198
}
 
199
 
 
200
void Schedule::setAllowOverbookingState( Schedule::OBState state )
 
201
{
 
202
    m_obstate = state;
 
203
}
 
204
 
 
205
Schedule::OBState Schedule::allowOverbookingState() const
 
206
{
 
207
    return m_obstate;
 
208
}
 
209
 
 
210
bool Schedule::allowOverbooking() const
 
211
{
 
212
    if ( m_obstate == OBS_Parent && m_parent ) {
 
213
        return m_parent->allowOverbooking();
 
214
    }
 
215
    return m_obstate == OBS_Allow;
 
216
}
 
217
 
 
218
bool Schedule::checkExternalAppointments() const
 
219
{
 
220
    if ( m_parent ) {
 
221
        return m_parent->checkExternalAppointments();
 
222
    }
 
223
    return false;
 
224
}
 
225
 
 
226
void Schedule::setScheduled( bool on )
 
227
{
 
228
    notScheduled = !on;
 
229
    changed( this );
 
230
}
 
231
 
 
232
Duration Schedule::effort( const DateTimeInterval &interval ) const
 
233
{
 
234
    return interval.second - interval.first;
 
235
}
 
236
 
 
237
DateTimeInterval Schedule::available( const DateTimeInterval &interval ) const
 
238
{
 
239
    return DateTimeInterval( interval.first, interval.second );
 
240
}
 
241
 
 
242
void Schedule::initiateCalculation()
 
243
{
 
244
    resourceError = false;
 
245
    resourceOverbooked = false;
 
246
    resourceNotAvailable = false;
 
247
    constraintError = false;
 
248
    schedulingError = false;
 
249
    inCriticalPath = false;
 
250
    effortNotMet = false;
 
251
    workStartTime = DateTime();
 
252
    workEndTime = DateTime();
 
253
}
 
254
 
 
255
void Schedule::calcResourceOverbooked()
 
256
{
 
257
    resourceOverbooked = false;
 
258
    foreach( Appointment *a, m_appointments ) {
 
259
        if ( a->resource() ->isOverbooked( a->startTime(), a->endTime() ) ) {
 
260
            resourceOverbooked = true;
 
261
            break;
 
262
        }
 
263
    }
 
264
}
 
265
 
 
266
DateTimeInterval Schedule::firstBookedInterval( const DateTimeInterval &interval, const Schedule *node  ) const
 
267
{
 
268
    QList<Appointment*> lst = m_appointments;
 
269
    switch ( m_calculationMode ) {
 
270
        case CalculateForward: lst = m_forward; break;
 
271
        case CalculateBackward: lst = m_backward; break;
 
272
        default: break;
 
273
    }
 
274
    foreach ( Appointment *a, lst ) {
 
275
        if ( a->node() == node ) {
 
276
            AppointmentIntervalList i = a->intervals( interval.first, interval.second );
 
277
            if ( i.isEmpty() ) {
 
278
                break;
 
279
            }
 
280
            return DateTimeInterval( i.map().values().first().startTime(), i.map().values().first().endTime() );
 
281
        }
 
282
    }
 
283
    return DateTimeInterval();
 
284
}
 
285
 
 
286
QStringList Schedule::overbookedResources() const
 
287
{
 
288
    QStringList rl;
 
289
    foreach( Appointment *a, m_appointments ) {
 
290
        if ( a->resource() ->isOverbooked( a->startTime(), a->endTime() ) ) {
 
291
            rl += a->resource() ->resource() ->name();
 
292
        }
 
293
    }
 
294
    return rl;
 
295
}
 
296
 
 
297
QStringList Schedule::resourceNameList() const
 
298
{
 
299
    return QStringList();
 
300
}
 
301
 
 
302
QList<Resource*> Schedule::resources() const
 
303
{
 
304
    return QList<Resource*>();
 
305
}
 
306
 
 
307
bool Schedule::loadXML( const KoXmlElement &sch, XMLLoaderObject & )
 
308
{
 
309
    m_name = sch.attribute( "name" );
 
310
    setType( sch.attribute( "type" ) );
 
311
    m_id = sch.attribute( "id" ).toLong();
 
312
 
 
313
    return true;
 
314
}
 
315
 
 
316
void Schedule::saveXML( QDomElement &element ) const
 
317
{
 
318
    QDomElement sch = element.ownerDocument().createElement( "schedule" );
 
319
    element.appendChild( sch );
 
320
    saveCommonXML( sch );
 
321
}
 
322
 
 
323
void Schedule::saveCommonXML( QDomElement &element ) const
 
324
{
 
325
    //debugPlan<<m_name<<" save schedule";
 
326
    element.setAttribute( "name", m_name );
 
327
    element.setAttribute( "type", typeToString() );
 
328
    element.setAttribute( "id", QString::number(qlonglong( m_id )) );
 
329
}
 
330
 
 
331
void Schedule::saveAppointments( QDomElement &element ) const
 
332
{
 
333
    //debugPlan;
 
334
    QListIterator<Appointment*> it = m_appointments;
 
335
    while ( it.hasNext() ) {
 
336
        it.next() ->saveXML( element );
 
337
    }
 
338
}
 
339
 
 
340
void Schedule::insertForwardNode( Node *node )
 
341
{
 
342
    if ( m_parent ) {
 
343
        m_parent->insertForwardNode( node );
 
344
    }
 
345
}
 
346
 
 
347
void Schedule::insertBackwardNode( Node *node )
 
348
{
 
349
    if ( m_parent ) {
 
350
        m_parent->insertBackwardNode( node );
 
351
    }
 
352
}
 
353
 
 
354
// used (directly) when appointment wants to attatch itself again
 
355
bool Schedule::attatch( Appointment *appointment )
 
356
{
 
357
    int mode = appointment->calculationMode();
 
358
    //debugPlan<<appointment<<mode;
 
359
    if ( mode == Scheduling ) {
 
360
        if ( m_appointments.indexOf( appointment ) != -1 ) {
 
361
            errorPlan << "Appointment already exists" << endl;
 
362
            return false;
 
363
        }
 
364
        m_appointments.append( appointment );
 
365
        //if (resource()) debugPlan<<appointment<<" For resource '"<<resource()->name()<<"'"<<" count="<<m_appointments.count();
 
366
        //if (node()) debugPlan<<"("<<this<<")"<<appointment<<" For node '"<<node()->name()<<"'"<<" count="<<m_appointments.count();
 
367
        return true;
 
368
    }
 
369
    if ( mode == CalculateForward ) {
 
370
        if ( m_forward.indexOf( appointment ) != -1 ) {
 
371
            errorPlan << "Appointment already exists" << endl;
 
372
            return false;
 
373
        }
 
374
        m_forward.append( appointment );
 
375
        //if (resource()) debugPlan<<"For resource '"<<resource()->name()<<"'";
 
376
        //if (node()) debugPlan<<"For node '"<<node()->name()<<"'";
 
377
        return true;
 
378
    }
 
379
    if ( mode == CalculateBackward ) {
 
380
        if ( m_backward.indexOf( appointment ) != -1 ) {
 
381
            errorPlan << "Appointment already exists" << endl;
 
382
            return false;
 
383
        }
 
384
        m_backward.append( appointment );
 
385
        //if (resource()) debugPlan<<"For resource '"<<resource()->name()<<"'";
 
386
        //if (node()) debugPlan<<"For node '"<<node()->name()<<"'";
 
387
        return true;
 
388
    }
 
389
    errorPlan<<"Unknown mode: "<<m_calculationMode<<endl;
 
390
    return false;
 
391
}
 
392
 
 
393
// used to add new schedules
 
394
bool Schedule::add( Appointment *appointment )
 
395
{
 
396
    //debugPlan<<this;
 
397
    appointment->setCalculationMode( m_calculationMode );
 
398
    return attatch( appointment );
 
399
}
 
400
 
 
401
void Schedule::takeAppointment( Appointment *appointment, int mode )
 
402
{
 
403
    Q_UNUSED(mode);
 
404
    //debugPlan<<"("<<this<<")"<<mode<<":"<<appointment<<","<<appointment->calculationMode();
 
405
    int i = m_forward.indexOf( appointment );
 
406
    if ( i != -1 ) {
 
407
        m_forward.removeAt( i );
 
408
        Q_ASSERT( mode == CalculateForward );
 
409
    }
 
410
    i = m_backward.indexOf( appointment );
 
411
    if ( i != -1 ) {
 
412
        m_backward.removeAt( i );
 
413
        Q_ASSERT( mode == CalculateBackward );
 
414
    }
 
415
    i = m_appointments.indexOf( appointment );
 
416
    if ( i != -1 ) {
 
417
        m_appointments.removeAt( i );
 
418
        Q_ASSERT( mode == Scheduling );
 
419
    }
 
420
}
 
421
 
 
422
Appointment *Schedule::findAppointment( Schedule *resource, Schedule *node, int mode )
 
423
{
 
424
    //debugPlan<<this<<" ("<<resourceError<<","<<node<<")"<<mode;
 
425
    if ( mode == Scheduling ) {
 
426
        foreach( Appointment *a,  m_appointments ) {
 
427
            if ( a->node() == node && a->resource() == resource ) {
 
428
                return a;
 
429
            }
 
430
        }
 
431
        return 0;
 
432
    } else if ( mode == CalculateForward ) {
 
433
        foreach( Appointment *a,  m_forward ) {
 
434
            if ( a->node() == node && a->resource() == resource ) {
 
435
                return a;
 
436
            }
 
437
        }
 
438
    } else if ( mode == CalculateBackward ) {
 
439
        foreach( Appointment *a,  m_backward ) {
 
440
            if ( a->node() == node && a->resource() == resource ) {
 
441
                Q_ASSERT( mode == CalculateBackward );
 
442
                return a;
 
443
            }
 
444
        }
 
445
    } else {
 
446
        Q_ASSERT( false ); // unknown mode
 
447
    }
 
448
    return 0;
 
449
}
 
450
 
 
451
DateTime Schedule::appointmentStartTime() const
 
452
{
 
453
    DateTime dt;
 
454
    foreach ( const Appointment *a, m_appointments ) {
 
455
        if ( ! dt.isValid() || dt > a->startTime() ) {
 
456
            dt = a->startTime();
 
457
        }
 
458
    }
 
459
    return dt;
 
460
}
 
461
DateTime Schedule::appointmentEndTime() const
 
462
{
 
463
    DateTime dt;
 
464
    foreach ( const Appointment *a, m_appointments ) {
 
465
        if ( ! dt.isValid() || dt > a->endTime() ) {
 
466
            dt = a->endTime();
 
467
        }
 
468
    }
 
469
    return dt;
 
470
}
 
471
 
 
472
bool Schedule::hasAppointments( int which ) const
 
473
{
 
474
    if ( which == CalculateForward ) {
 
475
        return m_forward.isEmpty();
 
476
    } else if ( which == CalculateBackward ) {
 
477
        return m_backward.isEmpty();
 
478
    }
 
479
    return m_appointments.isEmpty();
 
480
}
 
481
 
 
482
QList<Appointment*> Schedule::appointments( int which ) const
 
483
{
 
484
    if ( which == CalculateForward ) {
 
485
        return m_forward;
 
486
    } else if ( which == CalculateBackward ) {
 
487
        return m_backward;
 
488
    }
 
489
    return m_appointments;
 
490
}
 
491
 
 
492
Appointment Schedule::appointmentIntervals( int which, const DateTimeInterval &interval ) const
 
493
{
 
494
    Appointment app;
 
495
    if ( which == Schedule::CalculateForward ) {
 
496
        //debugPlan<<"list == CalculateForward";
 
497
        foreach ( Appointment *a, m_forward ) {
 
498
            app += interval.isValid() ? a->extractIntervals( interval ) : *a;
 
499
        }
 
500
        return app;
 
501
    } else if ( which == Schedule::CalculateBackward ) {
 
502
        //debugPlan<<"list == CalculateBackward";
 
503
        foreach ( Appointment *a, m_backward ) {
 
504
            app += interval.isValid() ? a->extractIntervals( interval ) : *a;
 
505
        }
 
506
        //debugPlan<<"list == CalculateBackward:"<<m_backward.count();
 
507
        return app;
 
508
    }
 
509
    foreach ( Appointment *a, m_appointments ) {
 
510
        app += interval.isValid() ? a->extractIntervals( interval ) : *a;
 
511
    }
 
512
    return app;
 
513
}
 
514
 
 
515
void Schedule::copyAppointments( Schedule::CalculationMode from, Schedule::CalculationMode to )
 
516
{
 
517
    switch ( to ) {
 
518
        case Scheduling:
 
519
            m_appointments.clear();
 
520
            switch( from ) {
 
521
                case CalculateForward:
 
522
                    m_appointments = m_forward;
 
523
                    break;
 
524
                case CalculateBackward:
 
525
                    m_appointments = m_backward;
 
526
                    break;
 
527
                default:
 
528
                    break;
 
529
            }
 
530
            break;
 
531
        case CalculateForward: break;
 
532
        case CalculateBackward: break;
 
533
    }
 
534
}
 
535
 
 
536
EffortCostMap Schedule::bcwsPrDay( EffortCostCalculationType type ) const
 
537
{
 
538
    return const_cast<Schedule*>( this )->bcwsPrDay( type );
 
539
}
 
540
 
 
541
EffortCostMap Schedule::bcwsPrDay( EffortCostCalculationType type )
 
542
{
 
543
    //debugPlan<<m_name<<m_appointments;
 
544
    EffortCostCache &ec = m_bcwsPrDay[ (int)type ];
 
545
    if ( ! ec.cached ) {
 
546
        foreach ( Appointment *a, m_appointments ) {
 
547
            ec.effortcostmap += a->plannedPrDay( a->startTime().date(), a->endTime().date(), type );
 
548
        }
 
549
    }
 
550
    return ec.effortcostmap;
 
551
}
 
552
 
 
553
EffortCostMap Schedule::plannedEffortCostPrDay( const QDate &start, const QDate &end, EffortCostCalculationType type ) const
 
554
{
 
555
    //debugPlan<<m_name<<m_appointments;
 
556
    EffortCostMap ec;
 
557
    QListIterator<Appointment*> it( m_appointments );
 
558
    while ( it.hasNext() ) {
 
559
        //debugPlan<<m_name;
 
560
        ec += it.next() ->plannedPrDay( start, end, type );
 
561
    }
 
562
    return ec;
 
563
}
 
564
 
 
565
EffortCostMap Schedule::plannedEffortCostPrDay( const Resource *resource, const QDate &start, const QDate &end, EffortCostCalculationType type ) const
 
566
{
 
567
    //debugPlan<<m_name<<m_appointments;
 
568
    EffortCostMap ec;
 
569
    foreach ( Appointment *a, m_appointments ) {
 
570
        if ( a->resource() && a->resource()->resource() == resource ) {
 
571
            ec += a->plannedPrDay( start, end, type );
 
572
            break;
 
573
        }
 
574
    }
 
575
    return ec;
 
576
}
 
577
 
 
578
Duration Schedule::plannedEffort( const Resource *resource, EffortCostCalculationType type ) const
 
579
{
 
580
    //debugPlan;
 
581
    Duration eff;
 
582
    QListIterator<Appointment*> it( m_appointments );
 
583
    while ( it.hasNext() ) {
 
584
        eff += it.next() ->plannedEffort( resource, type );
 
585
    }
 
586
    return eff;
 
587
}
 
588
 
 
589
Duration Schedule::plannedEffort( EffortCostCalculationType type ) const
 
590
{
 
591
    //debugPlan;
 
592
    Duration eff;
 
593
    QListIterator<Appointment*> it( m_appointments );
 
594
    while ( it.hasNext() ) {
 
595
        eff += it.next() ->plannedEffort( type );
 
596
    }
 
597
    return eff;
 
598
}
 
599
 
 
600
Duration Schedule::plannedEffort( const QDate &date, EffortCostCalculationType type ) const
 
601
{
 
602
    //debugPlan;
 
603
    Duration eff;
 
604
    QListIterator<Appointment*> it( m_appointments );
 
605
    while ( it.hasNext() ) {
 
606
        eff += it.next() ->plannedEffort( date, type );
 
607
    }
 
608
    return eff;
 
609
}
 
610
 
 
611
Duration Schedule::plannedEffort( const Resource *resource, const QDate &date, EffortCostCalculationType type ) const
 
612
{
 
613
    //debugPlan;
 
614
    Duration eff;
 
615
    QListIterator<Appointment*> it( m_appointments );
 
616
    while ( it.hasNext() ) {
 
617
        eff += it.next() ->plannedEffort( resource, date, type );
 
618
    }
 
619
    return eff;
 
620
}
 
621
 
 
622
Duration Schedule::plannedEffortTo( const QDate &date, EffortCostCalculationType type ) const
 
623
{
 
624
    //debugPlan;
 
625
    Duration eff;
 
626
    QListIterator<Appointment*> it( m_appointments );
 
627
    while ( it.hasNext() ) {
 
628
        eff += it.next() ->plannedEffortTo( date, type );
 
629
    }
 
630
    return eff;
 
631
}
 
632
 
 
633
Duration Schedule::plannedEffortTo(  const Resource *resource, const QDate &date, EffortCostCalculationType type ) const
 
634
{
 
635
    //debugPlan;
 
636
    Duration eff;
 
637
    QListIterator<Appointment*> it( m_appointments );
 
638
    while ( it.hasNext() ) {
 
639
        eff += it.next() ->plannedEffortTo( resource, date, type );
 
640
    }
 
641
    return eff;
 
642
}
 
643
 
 
644
EffortCost Schedule::plannedCost( EffortCostCalculationType type ) const
 
645
{
 
646
    //debugPlan;
 
647
    EffortCost c;
 
648
    QListIterator<Appointment*> it( m_appointments );
 
649
    while ( it.hasNext() ) {
 
650
        c += it.next() ->plannedCost( type );
 
651
    }
 
652
    return c;
 
653
}
 
654
 
 
655
double Schedule::plannedCost( const QDate &date, EffortCostCalculationType type ) const
 
656
{
 
657
    //debugPlan;
 
658
    double c = 0;
 
659
    QListIterator<Appointment*> it( m_appointments );
 
660
    while ( it.hasNext() ) {
 
661
        c += it.next() ->plannedCost( date, type );
 
662
    }
 
663
    return c;
 
664
}
 
665
 
 
666
double Schedule::plannedCostTo( const QDate &date, EffortCostCalculationType type ) const
 
667
{
 
668
    //debugPlan;
 
669
    double c = 0;
 
670
    QListIterator<Appointment*> it( m_appointments );
 
671
    while ( it.hasNext() ) {
 
672
        c += it.next() ->plannedCostTo( date, type );
 
673
    }
 
674
    return c;
 
675
}
 
676
 
 
677
void Schedule::addLog( const Schedule::Log &log )
 
678
{
 
679
    if ( m_parent ) {
 
680
        m_parent->addLog( log );
 
681
    }
 
682
}
 
683
 
 
684
QString Schedule::Log::formatMsg() const
 
685
 {
 
686
    QString s;
 
687
    s += node ? QString( "%1 " ).arg( node->name(), -8 ) : "";
 
688
    s += resource ? QString( "%1 ").arg(resource->name(), -8 ) : "";
 
689
    s += message;
 
690
    return s;
 
691
}
 
692
 
 
693
void Schedule::clearPerformanceCache()
 
694
{
 
695
    m_bcwsPrDay.clear();
 
696
    m_bcwpPrDay.clear();
 
697
    m_acwp.clear();
 
698
}
 
699
 
 
700
//-------------------------------------------------
 
701
NodeSchedule::NodeSchedule()
 
702
        : Schedule(),
 
703
        m_node( 0 )
 
704
{
 
705
    //debugPlan<<"("<<this<<")";
 
706
    init();
 
707
}
 
708
 
 
709
NodeSchedule::NodeSchedule( Node *node, const QString& name, Schedule::Type type, long id )
 
710
        : Schedule( name, type, id ),
 
711
        m_node( node )
 
712
{
 
713
    //debugPlan<<"node name:"<<node->name();
 
714
    init();
 
715
}
 
716
 
 
717
NodeSchedule::NodeSchedule( Schedule *parent, Node *node )
 
718
        : Schedule( parent ),
 
719
        m_node( node )
 
720
{
 
721
 
 
722
    //debugPlan<<"node name:"<<node->name();
 
723
    init();
 
724
}
 
725
 
 
726
NodeSchedule::~NodeSchedule()
 
727
{
 
728
    //debugPlan<<this<<""<<m_appointments.count();
 
729
    while ( !m_appointments.isEmpty() ) {
 
730
        Appointment *a = m_appointments.takeFirst();
 
731
        a->setNode( 0 );
 
732
        delete a;
 
733
    }
 
734
    //debugPlan<<"forw"<<m_forward.count();
 
735
    while ( !m_forward.isEmpty() ) {
 
736
        Appointment *a = m_forward.takeFirst();
 
737
        a->setNode( 0 );
 
738
        delete a;
 
739
    }
 
740
    //debugPlan<<"backw"<<m_backward.count();
 
741
    while ( !m_backward.isEmpty() ) {
 
742
        Appointment *a = m_backward.takeFirst();
 
743
        a->setNode( 0 );
 
744
        delete a;
 
745
    }
 
746
}
 
747
 
 
748
void NodeSchedule::init()
 
749
{
 
750
    resourceError = false;
 
751
    resourceOverbooked = false;
 
752
    resourceNotAvailable = false;
 
753
    constraintError = false;
 
754
    schedulingError = false;
 
755
    notScheduled = true;
 
756
    inCriticalPath = false;
 
757
    m_calculationMode = Schedule::Scheduling;
 
758
    positiveFloat = Duration::zeroDuration;
 
759
    negativeFloat = Duration::zeroDuration;
 
760
    freeFloat = Duration::zeroDuration;
 
761
}
 
762
 
 
763
void NodeSchedule::setDeleted( bool on )
 
764
{
 
765
    //debugPlan<<"deleted="<<on;
 
766
    m_deleted = on;
 
767
    // set deleted also for possible resource schedules
 
768
    QListIterator<Appointment*> it = m_appointments;
 
769
    while ( it.hasNext() ) {
 
770
        Appointment * a = it.next();
 
771
        if ( a->resource() ) {
 
772
            a->resource() ->setDeleted( on );
 
773
        }
 
774
    }
 
775
}
 
776
 
 
777
bool NodeSchedule::loadXML( const KoXmlElement &sch, XMLLoaderObject &status )
 
778
{
 
779
    //debugPlan;
 
780
    QString s;
 
781
    Schedule::loadXML( sch, status );
 
782
    s = sch.attribute( "earlystart" );
 
783
    if ( s.isEmpty() ) { // try version < 0.6
 
784
        s = sch.attribute( "earlieststart" );
 
785
    }
 
786
    if ( !s.isEmpty() ) {
 
787
        earlyStart = DateTime::fromString( s, status.projectTimeZone() );
 
788
    }
 
789
    s = sch.attribute( "latefinish" );
 
790
    if ( s.isEmpty() ) { // try version < 0.6
 
791
        s = sch.attribute( "latestfinish" );
 
792
    }
 
793
    if ( !s.isEmpty() ) {
 
794
        lateFinish = DateTime::fromString( s, status.projectTimeZone() );
 
795
    }
 
796
    s = sch.attribute( "latestart" );
 
797
    if ( !s.isEmpty() ) {
 
798
        lateStart = DateTime::fromString( s, status.projectTimeZone() );
 
799
    }
 
800
    s = sch.attribute( "earlyfinish" );
 
801
    if ( !s.isEmpty() ) {
 
802
        earlyFinish = DateTime::fromString( s, status.projectTimeZone() );
 
803
    }
 
804
    s = sch.attribute( "start" );
 
805
    if ( !s.isEmpty() )
 
806
        startTime = DateTime::fromString( s, status.projectTimeZone() );
 
807
    s = sch.attribute( "end" );
 
808
    if ( !s.isEmpty() )
 
809
        endTime = DateTime::fromString( s, status.projectTimeZone() );
 
810
    s = sch.attribute( "start-work" );
 
811
    if ( !s.isEmpty() )
 
812
        workStartTime = DateTime::fromString( s, status.projectTimeZone() );
 
813
    s = sch.attribute( "end-work" );
 
814
    if ( !s.isEmpty() )
 
815
        workEndTime = DateTime::fromString( s, status.projectTimeZone() );
 
816
    duration = Duration::fromString( sch.attribute( "duration" ) );
 
817
 
 
818
    inCriticalPath = sch.attribute( "in-critical-path", "0" ).toInt();
 
819
    resourceError = sch.attribute( "resource-error", "0" ).toInt();
 
820
    resourceOverbooked = sch.attribute( "resource-overbooked", "0" ).toInt();
 
821
    resourceNotAvailable = sch.attribute( "resource-not-available", "0" ).toInt();
 
822
    constraintError = sch.attribute( "scheduling-conflict", "0" ).toInt();
 
823
    schedulingError = sch.attribute( "scheduling-error", "0" ).toInt();
 
824
    notScheduled = sch.attribute( "not-scheduled", "1" ).toInt();
 
825
 
 
826
    positiveFloat = Duration::fromString( sch.attribute( "positive-float" ) );
 
827
    negativeFloat = Duration::fromString( sch.attribute( "negative-float" ) );
 
828
    freeFloat = Duration::fromString( sch.attribute( "free-float" ) );
 
829
 
 
830
    return true;
 
831
}
 
832
 
 
833
void NodeSchedule::saveXML( QDomElement &element ) const
 
834
{
 
835
    //debugPlan;
 
836
    QDomElement sch = element.ownerDocument().createElement( "schedule" );
 
837
    element.appendChild( sch );
 
838
    saveCommonXML( sch );
 
839
 
 
840
    if ( earlyStart.isValid() ) {
 
841
        sch.setAttribute( "earlystart", earlyStart.toString( Qt::ISODate ) );
 
842
    }
 
843
    if ( lateStart.isValid() ) {
 
844
        sch.setAttribute( "latestart", lateStart.toString( Qt::ISODate ) );
 
845
    }
 
846
    if ( earlyFinish.isValid() ) {
 
847
        sch.setAttribute( "earlyfinish", earlyFinish.toString( Qt::ISODate ) );
 
848
    }
 
849
    if ( lateFinish.isValid() ) {
 
850
        sch.setAttribute( "latefinish", lateFinish.toString( Qt::ISODate ) );
 
851
    }
 
852
    if ( startTime.isValid() )
 
853
        sch.setAttribute( "start", startTime.toString( Qt::ISODate ) );
 
854
    if ( endTime.isValid() )
 
855
        sch.setAttribute( "end", endTime.toString( Qt::ISODate ) );
 
856
    if ( workStartTime.isValid() )
 
857
        sch.setAttribute( "start-work", workStartTime.toString( Qt::ISODate ) );
 
858
    if ( workEndTime.isValid() )
 
859
        sch.setAttribute( "end-work", workEndTime.toString( Qt::ISODate ) );
 
860
 
 
861
    sch.setAttribute( "duration", duration.toString() );
 
862
 
 
863
    sch.setAttribute( "in-critical-path", QString::number(inCriticalPath) );
 
864
    sch.setAttribute( "resource-error", QString::number(resourceError) );
 
865
    sch.setAttribute( "resource-overbooked", QString::number(resourceOverbooked) );
 
866
    sch.setAttribute( "resource-not-available", QString::number(resourceNotAvailable) );
 
867
    sch.setAttribute( "scheduling-conflict", QString::number(constraintError) );
 
868
    sch.setAttribute( "scheduling-error", QString::number(schedulingError) );
 
869
    sch.setAttribute( "not-scheduled", QString::number(notScheduled) );
 
870
 
 
871
    sch.setAttribute( "positive-float", positiveFloat.toString() );
 
872
    sch.setAttribute( "negative-float", negativeFloat.toString() );
 
873
    sch.setAttribute( "free-float", freeFloat.toString() );
 
874
}
 
875
 
 
876
void NodeSchedule::addAppointment( Schedule *resource, const DateTime &start, const DateTime &end, double load )
 
877
{
 
878
    //debugPlan;
 
879
    Appointment * a = findAppointment( resource, this, m_calculationMode );
 
880
    if ( a != 0 ) {
 
881
        //debugPlan<<"Add interval to existing"<<a;
 
882
        a->addInterval( start, end, load );
 
883
        return ;
 
884
    }
 
885
    a = new Appointment( resource, this, start, end, load );
 
886
    bool result = add( a );
 
887
    Q_ASSERT ( result );
 
888
    result = resource->add( a );
 
889
    Q_ASSERT ( result );
 
890
    Q_UNUSED ( result ); // cheating the compiler in release mode to not warn about unused-but-set-variable
 
891
    //debugPlan<<"Added interval to new"<<a;
 
892
}
 
893
 
 
894
void NodeSchedule::takeAppointment( Appointment *appointment, int mode )
 
895
{
 
896
    Schedule::takeAppointment( appointment, mode );
 
897
    appointment->setNode( 0 ); // not my appointment anymore
 
898
    //debugPlan<<"Taken:"<<appointment;
 
899
    if ( appointment->resource() )
 
900
        appointment->resource() ->takeAppointment( appointment );
 
901
}
 
902
 
 
903
QList<Resource*> NodeSchedule::resources() const
 
904
{
 
905
    QList<Resource*> rl;
 
906
    foreach( Appointment *a, m_appointments ) {
 
907
        rl += a->resource() ->resource();
 
908
    }
 
909
    return rl;
 
910
}
 
911
 
 
912
QStringList NodeSchedule::resourceNameList() const
 
913
{
 
914
    QStringList rl;
 
915
    foreach( Appointment *a, m_appointments ) {
 
916
        rl += a->resource() ->resource() ->name();
 
917
    }
 
918
    return rl;
 
919
}
 
920
 
 
921
void NodeSchedule::logError( const QString &msg, int phase )
 
922
{
 
923
    Schedule::Log log( m_node, Log::Type_Error, msg, phase );
 
924
    if ( m_parent ) {
 
925
        m_parent->addLog( log );
 
926
    } else {
 
927
        addLog( log );
 
928
    }
 
929
}
 
930
 
 
931
void NodeSchedule::logWarning( const QString &msg, int phase )
 
932
{
 
933
    Schedule::Log log( m_node, Log::Type_Warning, msg, phase );
 
934
    if ( m_parent ) {
 
935
        m_parent->addLog( log );
 
936
    } else {
 
937
        addLog( log );
 
938
    }
 
939
}
 
940
 
 
941
void NodeSchedule::logInfo( const QString &msg, int phase )
 
942
{
 
943
    Schedule::Log log( m_node, Log::Type_Info, msg, phase );
 
944
    if ( m_parent ) {
 
945
        m_parent->addLog( log );
 
946
    } else {
 
947
        addLog( log );
 
948
    }
 
949
}
 
950
 
 
951
void NodeSchedule::logDebug( const QString &msg, int phase )
 
952
{
 
953
    Schedule::Log log( m_node, Log::Type_Debug, msg, phase );
 
954
    if ( m_parent ) {
 
955
        m_parent->addLog( log );
 
956
    } else {
 
957
        addLog( log );
 
958
    }
 
959
}
 
960
 
 
961
//-----------------------------------------------
 
962
ResourceSchedule::ResourceSchedule()
 
963
        : Schedule(),
 
964
        m_resource( 0 )
 
965
{
 
966
    //debugPlan<<"("<<this<<")";
 
967
}
 
968
 
 
969
ResourceSchedule::ResourceSchedule( Resource *resource, const QString& name, Schedule::Type type, long id )
 
970
        : Schedule( name, type, id ),
 
971
        m_resource( resource ),
 
972
        m_parent( 0 ),
 
973
        m_nodeSchedule( 0 )
 
974
{
 
975
    //debugPlan<<"resource:"<<resource->name();
 
976
}
 
977
 
 
978
ResourceSchedule::ResourceSchedule( Schedule *parent, Resource *resource )
 
979
        : Schedule( parent ),
 
980
        m_resource( resource ),
 
981
        m_parent( parent ),
 
982
        m_nodeSchedule( 0 )
 
983
{
 
984
    //debugPlan<<"resource:"<<resource->name();
 
985
}
 
986
 
 
987
ResourceSchedule::~ResourceSchedule()
 
988
{
 
989
    //debugPlan<<this<<""<<m_appointments.count();
 
990
    while ( !m_appointments.isEmpty() ) {
 
991
        Appointment *a = m_appointments.takeFirst();
 
992
        a->setResource( 0 );
 
993
        delete a;
 
994
    }
 
995
    //debugPlan<<"forw"<<m_forward.count();
 
996
    while ( !m_forward.isEmpty() ) {
 
997
        Appointment *a = m_forward.takeFirst();
 
998
        a->setResource( 0 );
 
999
        delete a;
 
1000
    }
 
1001
    //debugPlan<<"backw"<<m_backward.count();
 
1002
    while ( !m_backward.isEmpty() ) {
 
1003
        Appointment *a = m_backward.takeFirst();
 
1004
        a->setResource( 0 );
 
1005
        delete a;
 
1006
    }
 
1007
}
 
1008
 
 
1009
// called from the resource
 
1010
void ResourceSchedule::addAppointment( Schedule *node, const DateTime &start, const DateTime &end, double load )
 
1011
{
 
1012
    Q_ASSERT( start < end );
 
1013
    //debugPlan<<"("<<this<<")"<<node<<","<<m_calculationMode;
 
1014
    Appointment * a = findAppointment( this, node, m_calculationMode );
 
1015
    if ( a != 0 ) {
 
1016
        //debugPlan<<"Add interval to existing"<<a;
 
1017
        a->addInterval( start, end, load );
 
1018
        return ;
 
1019
    }
 
1020
    a = new Appointment( this, node, start, end, load );
 
1021
    bool result = add( a );
 
1022
    Q_ASSERT ( result == true );
 
1023
    result = node->add( a );
 
1024
    Q_ASSERT ( result == true );
 
1025
    Q_UNUSED ( result );  //don't warn about unused-but-set-variable in release mode
 
1026
    //debugPlan<<"Added interval to new"<<a;
 
1027
}
 
1028
 
 
1029
void ResourceSchedule::takeAppointment( Appointment *appointment, int mode )
 
1030
{
 
1031
    Schedule::takeAppointment( appointment, mode );
 
1032
    appointment->setResource( 0 );
 
1033
    //debugPlan<<"Taken:"<<appointment;
 
1034
    if ( appointment->node() )
 
1035
        appointment->node() ->takeAppointment( appointment );
 
1036
}
 
1037
 
 
1038
bool ResourceSchedule::isOverbooked() const
 
1039
{
 
1040
    return false;
 
1041
}
 
1042
 
 
1043
bool ResourceSchedule::isOverbooked( const DateTime &start, const DateTime &end ) const
 
1044
{
 
1045
    if ( m_resource == 0 )
 
1046
        return false;
 
1047
    //debugPlan<<start.toString()<<" -"<<end.toString();
 
1048
    Appointment a = appointmentIntervals();
 
1049
    foreach ( const AppointmentInterval &i, a.intervals().map() ) {
 
1050
        if ( ( !end.isValid() || i.startTime() < end ) &&
 
1051
                ( !start.isValid() || i.endTime() > start ) ) {
 
1052
            if ( i.load() > m_resource->units() ) {
 
1053
                //debugPlan<<m_name<<" overbooked";
 
1054
                return true;
 
1055
            }
 
1056
        }
 
1057
        if ( i.startTime() >= end )
 
1058
            break;
 
1059
    }
 
1060
    //debugPlan<<m_name<<" not overbooked";
 
1061
    return false;
 
1062
}
 
1063
 
 
1064
double ResourceSchedule::normalRatePrHour() const
 
1065
{
 
1066
    return m_resource ? m_resource->normalRate() : 0.0;
 
1067
}
 
1068
 
 
1069
//TODO change to return the booked effort
 
1070
Duration ResourceSchedule::effort( const DateTimeInterval &interval ) const
 
1071
{
 
1072
    Duration eff = interval.second - interval.first;
 
1073
    if ( allowOverbooking() ) {
 
1074
        return eff;
 
1075
    }
 
1076
    Appointment a;
 
1077
    if ( checkExternalAppointments() ) {
 
1078
        a.setIntervals( m_resource->externalAppointments() );
 
1079
    }
 
1080
    a.merge( appointmentIntervals( m_calculationMode ) );
 
1081
    if ( a.isEmpty() || a.startTime() >= interval.second || a.endTime() <= interval.first ) {
 
1082
        return eff;
 
1083
    }
 
1084
    foreach ( const AppointmentInterval &i, a.intervals().map() ) {
 
1085
        if ( interval.second <= i.startTime() ) {
 
1086
            break;
 
1087
        }
 
1088
        if ( interval.first >= i.startTime() ) {
 
1089
            DateTime et = i.endTime() < interval.second ? i.endTime() : interval.second;
 
1090
            eff -= ( et - interval.first ) * ((double)i.load()/100.0 );
 
1091
        } else {
 
1092
            DateTime et = i.endTime() < interval.second ? i.endTime() : interval.second;
 
1093
            eff -= ( et - i.startTime() ) * ((double)i.load()/100.0 );
 
1094
        }
 
1095
    }
 
1096
    return eff;
 
1097
}
 
1098
 
 
1099
DateTimeInterval ResourceSchedule::available( const DateTimeInterval &interval ) const
 
1100
{
 
1101
    //const_cast<ResourceSchedule*>(this)->logDebug( QString( "Schedule available id=%1, Mode=%2: interval=%3 - %4" ).arg(m_id).arg(m_calculationMode).arg(interval.first.toString()).arg(interval.second.toString()) );
 
1102
    if ( allowOverbooking() ) {
 
1103
        return DateTimeInterval( interval.first, interval.second );
 
1104
    }
 
1105
    QTimeZone projectTimeZone = QTimeZone::systemTimeZone();
 
1106
    if (m_resource) {
 
1107
        projectTimeZone = m_resource->project()->timeZone();
 
1108
    }
 
1109
    DateTimeInterval ci(interval.first.toTimeZone(projectTimeZone), interval.second.toTimeZone(projectTimeZone));
 
1110
    Appointment a;
 
1111
    if ( checkExternalAppointments() ) {
 
1112
        a.setIntervals( m_resource->externalAppointments( ci ) );
 
1113
    }
 
1114
    a.merge( appointmentIntervals( m_calculationMode, ci ) );
 
1115
    if ( a.isEmpty() || a.startTime() >= ci.second || a.endTime() <= ci.first ) {
 
1116
        //debugPlan<<this<<"id="<<m_id<<"Mode="<<m_calculationMode<<""<<interval.first<<","<<interval.second<<" FREE";
 
1117
        return DateTimeInterval( interval.first, interval.second ); // just return the interval
 
1118
    }
 
1119
    //debugPlan<<"available:"<<interval<<endl<<a.intervals();
 
1120
    DateTimeInterval res;
 
1121
    int units = m_resource ? m_resource->units() : 100;
 
1122
    foreach ( const AppointmentInterval &i, a.intervals().map() ) {
 
1123
        //const_cast<ResourceSchedule*>(this)->logDebug( QString( "Schedule available check interval=%1 - %2" ).arg(i.startTime().toString()).arg(i.endTime().toString()) );
 
1124
        if ( i.startTime() < ci.second && i.endTime() > ci.first ) {
 
1125
            // interval intersects appointment
 
1126
            if ( ci.first >= i.startTime() && ci.second <= i.endTime() ) {
 
1127
                // interval within appointment
 
1128
                if ( i.load() < units ) {
 
1129
                    if ( ! res.first.isValid() ) {
 
1130
                        res.first = qMax( ci.first, i.startTime() );
 
1131
                    }
 
1132
                    res.second = qMin( ci.second, i.endTime() );
 
1133
                } else {
 
1134
                    // fully booked
 
1135
                    if ( res.first.isValid() ) {
 
1136
                        res.second = i.startTime();
 
1137
                        if ( res.first >= res.second ) {
 
1138
                            res = DateTimeInterval();
 
1139
                        }
 
1140
                    }
 
1141
                }
 
1142
                //debugPlan<<"available within:"<<interval<<i<<":"<<ci<<res;
 
1143
                break;
 
1144
            }
 
1145
            DateTime t = i.startTime();
 
1146
            if ( ci.first < t ) {
 
1147
                // Interval starts before appointment, so free from interval start
 
1148
                //debugPlan<<"available before:"<<interval<<i<<":"<<ci<<res;
 
1149
                //const_cast<ResourceSchedule*>(this)->logDebug( QString( "Schedule available t>first: returns interval=%1 - %2" ).arg(ci.first.toString()).arg(t.toString()) );
 
1150
                if ( ! res.first.isValid() ) {
 
1151
                    res.first = ci.first;
 
1152
                }
 
1153
                res.second = t;
 
1154
                if ( i.load() < units ) {
 
1155
                    res.second = qMin( ci.second, i.endTime() );
 
1156
                    if ( ci.second > i.endTime() ) {
 
1157
                        ci.first = i.endTime();
 
1158
                        //debugPlan<<"available next 1:"<<interval<<i<<":"<<ci<<res;
 
1159
                        continue; // check next appointment
 
1160
                    }
 
1161
                }
 
1162
                //debugPlan<<"available:"<<interval<<i<<":"<<ci<<res;
 
1163
                break;
 
1164
            }
 
1165
            // interval start >= appointment start
 
1166
            t = i.endTime();
 
1167
            if ( t < ci.second ) {
 
1168
                // check if rest of appointment is free
 
1169
                if ( units <= i.load() ) {
 
1170
                    ci.first = t; // fully booked, so move forvard to appointment end
 
1171
                }
 
1172
                res = ci;
 
1173
                //debugPlan<<"available next 2:"<<interval<<i<<":"<<ci<<res;
 
1174
                continue;
 
1175
            }
 
1176
            //debugPlan<<"available:"<<interval<<i<<":"<<ci<<res;
 
1177
            Q_ASSERT( false );
 
1178
        } else if ( i.startTime() >= interval.second ) {
 
1179
            // no more overlaps
 
1180
            break;
 
1181
        }
 
1182
    }
 
1183
    //debugPlan<<"available: result="<<interval<<":"<<res;
 
1184
    return DateTimeInterval(res.first.toTimeZone(interval.first.timeZone()), res.second.toTimeZone(interval.second.timeZone()));
 
1185
}
 
1186
 
 
1187
void ResourceSchedule::logError( const QString &msg, int phase )
 
1188
{
 
1189
    if ( m_parent ) {
 
1190
        Schedule::Log log( m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Error, msg, phase );
 
1191
        m_parent->addLog( log );
 
1192
    }
 
1193
}
 
1194
 
 
1195
void ResourceSchedule::logWarning( const QString &msg, int phase )
 
1196
{
 
1197
    if ( m_parent ) {
 
1198
        Schedule::Log log( m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Warning, msg, phase );
 
1199
        m_parent->addLog( log );
 
1200
    }
 
1201
}
 
1202
 
 
1203
void ResourceSchedule::logInfo( const QString &msg, int phase )
 
1204
{
 
1205
    if ( m_parent ) {
 
1206
        Schedule::Log log( m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Info, msg, phase );
 
1207
        m_parent->addLog( log );
 
1208
    }
 
1209
}
 
1210
 
 
1211
void ResourceSchedule::logDebug( const QString &msg, int phase )
 
1212
{
 
1213
    if ( m_parent ) {
 
1214
        Schedule::Log log( m_nodeSchedule ? m_nodeSchedule->node() : 0, m_resource, Log::Type_Debug, msg, phase );
 
1215
        m_parent->addLog( log );
 
1216
    }
 
1217
}
 
1218
 
 
1219
//--------------------------------------
 
1220
MainSchedule::MainSchedule()
 
1221
    : NodeSchedule(),
 
1222
 
 
1223
    m_manager( 0 )
 
1224
{
 
1225
    //debugPlan<<"("<<this<<")";
 
1226
    init();
 
1227
}
 
1228
 
 
1229
MainSchedule::MainSchedule( Node *node, const QString& name, Schedule::Type type, long id )
 
1230
    : NodeSchedule( node, name, type, id ),
 
1231
      criticalPathListCached( false ),
 
1232
      m_manager( 0 ),
 
1233
      m_currentCriticalPath( 0 )
 
1234
{
 
1235
    //debugPlan<<"node name:"<<node->name();
 
1236
    init();
 
1237
}
 
1238
 
 
1239
MainSchedule::~MainSchedule()
 
1240
{
 
1241
    //debugPlan<<"("<<this<<")";
 
1242
}
 
1243
 
 
1244
void MainSchedule::incProgress()
 
1245
{
 
1246
    if ( m_manager ) m_manager->incProgress();
 
1247
}
 
1248
 
 
1249
bool MainSchedule::isBaselined() const
 
1250
{
 
1251
    return m_manager == 0 ? false : m_manager->isBaselined();
 
1252
}
 
1253
 
 
1254
bool MainSchedule::usePert() const
 
1255
{
 
1256
    return m_manager == 0 ? false : m_manager->usePert();
 
1257
}
 
1258
 
 
1259
bool MainSchedule::allowOverbooking() const
 
1260
{
 
1261
    return m_manager == 0 ? false : m_manager->allowOverbooking();
 
1262
}
 
1263
 
 
1264
bool MainSchedule::checkExternalAppointments() const
 
1265
{
 
1266
    return m_manager == 0 ? false : m_manager->checkExternalAppointments();
 
1267
}
 
1268
 
 
1269
void MainSchedule::changed( Schedule *sch )
 
1270
{
 
1271
    if ( m_manager ) {
 
1272
        m_manager->scheduleChanged( static_cast<MainSchedule*>( sch ) );
 
1273
    }
 
1274
}
 
1275
 
 
1276
bool MainSchedule::loadXML( const KoXmlElement &sch, XMLLoaderObject &status )
 
1277
{
 
1278
    //debugPlan;
 
1279
    QString s;
 
1280
    Schedule::loadXML( sch, status );
 
1281
 
 
1282
    s = sch.attribute( "start" );
 
1283
    if ( !s.isEmpty() )
 
1284
        startTime = DateTime::fromString( s, status.projectTimeZone() );
 
1285
    s = sch.attribute( "end" );
 
1286
    if ( !s.isEmpty() )
 
1287
        endTime = DateTime::fromString( s, status.projectTimeZone() );
 
1288
 
 
1289
    duration = Duration::fromString( sch.attribute( "duration" ) );
 
1290
    constraintError = sch.attribute( "scheduling-conflict", "0" ).toInt();
 
1291
    schedulingError = sch.attribute( "scheduling-error", "0" ).toInt();
 
1292
    //NOTE: we use "scheduled" as default to match old format without "not-scheduled" element
 
1293
    notScheduled = sch.attribute( "not-scheduled", "0" ).toInt();
 
1294
 
 
1295
    KoXmlNode n = sch.firstChild();
 
1296
    for ( ; ! n.isNull(); n = n.nextSibling() ) {
 
1297
        if ( ! n.isElement() ) {
 
1298
            continue;
 
1299
        }
 
1300
        KoXmlElement el = n.toElement();
 
1301
        if ( el.tagName() == "appointment" ) {
 
1302
            // Load the appointments.
 
1303
            // Resources and tasks must already be loaded
 
1304
            Appointment * child = new Appointment();
 
1305
            if ( !child->loadXML( el, status, *this ) ) {
 
1306
                // TODO: Complain about this
 
1307
                errorPlan << "Failed to load appointment" << endl;
 
1308
                delete child;
 
1309
            }
 
1310
        } else if ( el.tagName() == "criticalpath-list" ) {
 
1311
            // Tasks must already be loaded
 
1312
            for ( KoXmlNode n1 = el.firstChild(); ! n1.isNull(); n1 = n1.nextSibling() ) {
 
1313
                if ( ! n1.isElement() ) {
 
1314
                    continue;
 
1315
                }
 
1316
                KoXmlElement e1 = n1.toElement();
 
1317
                if ( e1.tagName() != "criticalpath" ) {
 
1318
                    continue;
 
1319
                }
 
1320
                QList<Node*> lst;
 
1321
                for ( KoXmlNode n2 = e1.firstChild(); ! n2.isNull(); n2 = n2.nextSibling() ) {
 
1322
                    if ( ! n2.isElement() ) {
 
1323
                        continue;
 
1324
                    }
 
1325
                    KoXmlElement e2 = n2.toElement();
 
1326
                    if ( e2.tagName() != "node" ) {
 
1327
                        continue;
 
1328
                    }
 
1329
                    QString s = e2.attribute( "id" );
 
1330
                    Node *node = status.project().findNode( s );
 
1331
                    if ( node ) {
 
1332
                        lst.append( node );
 
1333
                    } else {
 
1334
                        errorPlan<<"Failed to find node id="<<s;
 
1335
                    }
 
1336
                }
 
1337
                m_pathlists.append( lst );
 
1338
            }
 
1339
            criticalPathListCached = true;
 
1340
        }
 
1341
    }
 
1342
    return true;
 
1343
}
 
1344
 
 
1345
void MainSchedule::saveXML( QDomElement &element ) const
 
1346
{
 
1347
    saveCommonXML( element );
 
1348
 
 
1349
    element.setAttribute( "start", startTime.toString( Qt::ISODate ) );
 
1350
    element.setAttribute( "end", endTime.toString( Qt::ISODate ) );
 
1351
    element.setAttribute( "duration", duration.toString() );
 
1352
    element.setAttribute( "scheduling-conflict", QString::number(constraintError) );
 
1353
    element.setAttribute( "scheduling-error", QString::number(schedulingError) );
 
1354
    element.setAttribute( "not-scheduled", QString::number(notScheduled) );
 
1355
 
 
1356
    if ( ! m_pathlists.isEmpty() ) {
 
1357
        QDomElement lists = element.ownerDocument().createElement( "criticalpath-list" );
 
1358
        element.appendChild( lists );
 
1359
        foreach ( const QList<Node*> &l, m_pathlists ) {
 
1360
            if ( l.isEmpty() ) {
 
1361
                continue;
 
1362
            }
 
1363
            QDomElement list = lists.ownerDocument().createElement( "criticalpath" );
 
1364
            lists.appendChild( list );
 
1365
            foreach ( Node *n, l ) {
 
1366
                QDomElement el = list.ownerDocument().createElement( "node" );
 
1367
                list.appendChild( el );
 
1368
                el.setAttribute( "id", n->id() );
 
1369
            }
 
1370
        }
 
1371
    }
 
1372
}
 
1373
 
 
1374
DateTime MainSchedule::calculateForward( int use )
 
1375
{
 
1376
    DateTime late;
 
1377
    foreach( Node *n, m_backwardnodes ) {
 
1378
        DateTime t = n->calculateForward( use );
 
1379
        if ( !late.isValid() || late < t ) {
 
1380
            late = t;
 
1381
        }
 
1382
    }
 
1383
    return late;
 
1384
}
 
1385
 
 
1386
DateTime MainSchedule::calculateBackward( int use )
 
1387
{
 
1388
    DateTime early;
 
1389
    foreach( Node *n, m_forwardnodes ) {
 
1390
        DateTime t = n->calculateBackward( use );
 
1391
        if ( !early.isValid() || early > t ) {
 
1392
            early = t;
 
1393
        }
 
1394
    }
 
1395
    return early;
 
1396
}
 
1397
 
 
1398
DateTime MainSchedule::scheduleForward( const DateTime &earliest, int use )
 
1399
{
 
1400
    DateTime end;
 
1401
    foreach( Node *n, m_forwardnodes ) {
 
1402
        DateTime t = n->scheduleForward( earliest, use );
 
1403
        if ( !end.isValid() || end < t ) {
 
1404
            end = t;
 
1405
        }
 
1406
    }
 
1407
    return end;
 
1408
}
 
1409
 
 
1410
DateTime MainSchedule::scheduleBackward( const DateTime &latest, int use )
 
1411
{
 
1412
    DateTime start;
 
1413
    foreach( Node *n, m_backwardnodes ) {
 
1414
        DateTime t = n->scheduleBackward( latest, use );
 
1415
        if ( !start.isValid() || start > t ) {
 
1416
            start = t;
 
1417
        }
 
1418
    }
 
1419
    return start;
 
1420
}
 
1421
 
 
1422
bool MainSchedule::recalculate() const
 
1423
{
 
1424
    return m_manager == 0 ? false : m_manager->recalculate();
 
1425
}
 
1426
 
 
1427
DateTime MainSchedule::recalculateFrom() const
 
1428
{
 
1429
    return m_manager == 0 ? DateTime() : m_manager->recalculateFrom();
 
1430
}
 
1431
 
 
1432
long MainSchedule::parentScheduleId() const
 
1433
{
 
1434
    return m_manager == 0 ? -2 : m_manager->parentScheduleId();
 
1435
}
 
1436
 
 
1437
void MainSchedule::clearCriticalPathList()
 
1438
{
 
1439
    m_pathlists.clear();
 
1440
    m_currentCriticalPath = 0;
 
1441
    criticalPathListCached = false;
 
1442
}
 
1443
 
 
1444
QList<Node*> *MainSchedule::currentCriticalPath() const
 
1445
{
 
1446
    return m_currentCriticalPath;
 
1447
}
 
1448
 
 
1449
void MainSchedule::addCriticalPath( QList<Node*> *lst )
 
1450
{
 
1451
    QList<Node*> l;
 
1452
    if ( lst ) {
 
1453
        l = *lst;
 
1454
    }
 
1455
    m_pathlists.append( l );
 
1456
    m_currentCriticalPath = &( m_pathlists.last() );
 
1457
}
 
1458
 
 
1459
void MainSchedule::addCriticalPathNode( Node *node )
 
1460
{
 
1461
    if ( m_currentCriticalPath == 0 ) {
 
1462
        errorPlan<<"No currentCriticalPath"<<endl;
 
1463
        return;
 
1464
    }
 
1465
    m_currentCriticalPath->append( node );
 
1466
}
 
1467
 
 
1468
QVector<Schedule::Log> MainSchedule::logs() const
 
1469
{
 
1470
    return m_log;
 
1471
}
 
1472
 
 
1473
void MainSchedule::addLog( const KPlato::Schedule::Log &log )
 
1474
{
 
1475
    Q_ASSERT( log.resource || log.node );
 
1476
#ifndef NDEBUG
 
1477
    if ( log.resource ) {
 
1478
        Q_ASSERT( manager()->project().findResource( log.resource->id() ) == log.resource );
 
1479
    } else if ( log.node ) {
 
1480
        Q_ASSERT( manager()->project().findNode( log.node->id() ) == log.node );
 
1481
    }
 
1482
#endif
 
1483
    const int phaseToSet = ( log.phase == -1 && ! m_log.isEmpty() ) ? m_log.last().phase : -1;
 
1484
    m_log.append( log );
 
1485
 
 
1486
    if ( phaseToSet != -1 ) {
 
1487
        m_log.last().phase = phaseToSet;
 
1488
    }
 
1489
    if ( m_manager ) {
 
1490
        m_manager->logAdded(m_log.last());
 
1491
    }
 
1492
}
 
1493
 
 
1494
//static
 
1495
QString MainSchedule::logSeverity( int severity )
 
1496
{
 
1497
    switch ( severity ) {
 
1498
        case Log::Type_Debug: return "Debug";//FIXME i18n( "Debug" );
 
1499
        case Log::Type_Info: return i18n( "Info" );
 
1500
        case Log::Type_Warning: return i18n( "Warning" );
 
1501
        case Log::Type_Error: return i18n( "Error" );
 
1502
        default: break;
 
1503
    }
 
1504
    return QString( "Severity %1" ).arg( severity );
 
1505
}
 
1506
 
 
1507
QStringList MainSchedule::logMessages() const
 
1508
{
 
1509
    QStringList lst;
 
1510
    foreach ( const Schedule::Log &l, m_log ) {
 
1511
        lst << l.formatMsg();
 
1512
    }
 
1513
    return lst;
 
1514
}
 
1515
 
 
1516
//-----------------------------------------
 
1517
ScheduleManager::ScheduleManager( Project &project, const QString name )
 
1518
    : m_project( project),
 
1519
    m_parent( 0 ),
 
1520
    m_name( name ),
 
1521
    m_baselined( false ),
 
1522
    m_allowOverbooking( false ),
 
1523
    m_checkExternalAppointments( true ),
 
1524
    m_usePert( false ),
 
1525
    m_recalculate( false ),
 
1526
    m_schedulingDirection( false ),
 
1527
    m_scheduling( false ),
 
1528
    m_progress( 0 ),
 
1529
    m_maxprogress( 0 ),
 
1530
    m_expected( 0 )
 
1531
{
 
1532
    //debugPlan<<name;
 
1533
}
 
1534
 
 
1535
ScheduleManager::~ScheduleManager()
 
1536
{
 
1537
    qDeleteAll( m_children );
 
1538
    setParentManager( 0 );
 
1539
}
 
1540
 
 
1541
void ScheduleManager::setParentManager( ScheduleManager *sm, int index )
 
1542
{
 
1543
    if ( m_parent ) {
 
1544
        m_parent->removeChild( this );
 
1545
    }
 
1546
    m_parent = sm;
 
1547
    if ( sm ) {
 
1548
        sm->insertChild( this, index );
 
1549
    }
 
1550
}
 
1551
 
 
1552
int ScheduleManager::removeChild( const ScheduleManager *sm )
 
1553
{
 
1554
    int i = m_children.indexOf( const_cast<ScheduleManager*>( sm ) );
 
1555
    if ( i != -1 ) {
 
1556
        m_children.removeAt( i );
 
1557
    }
 
1558
    return i;
 
1559
}
 
1560
 
 
1561
void ScheduleManager::insertChild( ScheduleManager *sm, int index )
 
1562
{
 
1563
    //debugPlan<<m_name<<", insert"<<sm->name()<<","<<index;
 
1564
    if ( index == -1 ) {
 
1565
        m_children.append( sm );
 
1566
    } else {
 
1567
        m_children.insert( index, sm );
 
1568
    }
 
1569
}
 
1570
 
 
1571
void ScheduleManager::createSchedules()
 
1572
{
 
1573
    setExpected( m_project.createSchedule( m_name, Schedule::Expected ) );
 
1574
}
 
1575
 
 
1576
int ScheduleManager::indexOf( const ScheduleManager *child ) const
 
1577
{
 
1578
    //debugPlan<<this<<","<<child;
 
1579
    return m_children.indexOf( const_cast<ScheduleManager*>( child ) );
 
1580
}
 
1581
 
 
1582
ScheduleManager *ScheduleManager::findManager( const QString& name ) const
 
1583
{
 
1584
    if ( m_name == name ) {
 
1585
        return const_cast<ScheduleManager*>( this );
 
1586
    }
 
1587
    foreach ( ScheduleManager *sm, m_children ) {
 
1588
        ScheduleManager *m = sm->findManager( name );
 
1589
        if ( m ) {
 
1590
            return m;
 
1591
        }
 
1592
    }
 
1593
    return 0;
 
1594
}
 
1595
 
 
1596
QList<ScheduleManager*> ScheduleManager::allChildren() const
 
1597
{
 
1598
    QList<ScheduleManager*> lst;
 
1599
    foreach ( ScheduleManager *sm, m_children ) {
 
1600
        lst << sm;
 
1601
        lst << sm->allChildren();
 
1602
    }
 
1603
    return lst;
 
1604
}
 
1605
 
 
1606
bool ScheduleManager::isParentOf( const ScheduleManager *sm ) const
 
1607
{
 
1608
    if ( indexOf( sm ) >= 0 ) {
 
1609
        return true;
 
1610
    }
 
1611
    foreach ( ScheduleManager *p, m_children ) {
 
1612
        if ( p->isParentOf( sm ) ) {
 
1613
            return true;
 
1614
        }
 
1615
    }
 
1616
    return false;
 
1617
}
 
1618
 
 
1619
void ScheduleManager::setName( const QString& name )
 
1620
{
 
1621
    m_name = name;
 
1622
#ifndef NDEBUG
 
1623
    setObjectName( name );
 
1624
#endif
 
1625
    if ( m_expected ) {
 
1626
        m_expected->setName( name );
 
1627
        m_project.changed( m_expected );
 
1628
    }
 
1629
    m_project.changed( this );
 
1630
}
 
1631
 
 
1632
bool ScheduleManager::isChildBaselined() const
 
1633
{
 
1634
    //debugPlan<<on;
 
1635
    foreach ( ScheduleManager *sm, m_children ) {
 
1636
        if ( sm->isBaselined() || sm->isChildBaselined() ) {
 
1637
            return true;
 
1638
        }
 
1639
    }
 
1640
    return false;
 
1641
}
 
1642
 
 
1643
void ScheduleManager::setBaselined( bool on )
 
1644
{
 
1645
    //debugPlan<<on;
 
1646
    m_baselined = on;
 
1647
    m_project.changed( this );
 
1648
}
 
1649
 
 
1650
void ScheduleManager::setAllowOverbooking( bool on )
 
1651
{
 
1652
    //debugPlan<<on;
 
1653
    m_allowOverbooking = on;
 
1654
    m_project.changed( this );
 
1655
}
 
1656
 
 
1657
bool ScheduleManager::allowOverbooking() const
 
1658
{
 
1659
    //debugPlan<<m_name<<"="<<m_allowOverbooking;
 
1660
    return m_allowOverbooking;
 
1661
}
 
1662
 
 
1663
bool ScheduleManager::checkExternalAppointments() const
 
1664
{
 
1665
    //debugPlan<<m_name<<"="<<m_allowOverbooking;
 
1666
    return m_checkExternalAppointments;
 
1667
}
 
1668
 
 
1669
void ScheduleManager::setCheckExternalAppointments( bool on )
 
1670
{
 
1671
    //debugPlan<<m_name<<"="<<m_checkExternalAppointments;
 
1672
    m_checkExternalAppointments = on;
 
1673
}
 
1674
 
 
1675
void ScheduleManager::scheduleChanged( MainSchedule *sch )
 
1676
{
 
1677
    m_project.changed( sch );
 
1678
    m_project.changed( this ); //hmmm, due to aggregated info
 
1679
}
 
1680
 
 
1681
void ScheduleManager::setUsePert( bool on )
 
1682
{
 
1683
    m_usePert = on;
 
1684
    m_project.changed( this );
 
1685
}
 
1686
 
 
1687
void ScheduleManager::setSchedulingDirection( bool on )
 
1688
{
 
1689
    //debugPlan<<on;
 
1690
    m_schedulingDirection = on;
 
1691
    m_project.changed( this );
 
1692
}
 
1693
 
 
1694
void ScheduleManager::setScheduling( bool on )
 
1695
{
 
1696
    m_scheduling = on;
 
1697
    if ( ! on ) {
 
1698
        m_project.setProgress( 0, this );
 
1699
    }
 
1700
    m_project.changed( this );
 
1701
}
 
1702
 
 
1703
const QList<SchedulerPlugin*> ScheduleManager::schedulerPlugins() const
 
1704
{
 
1705
    return m_project.schedulerPlugins().values();
 
1706
}
 
1707
 
 
1708
QString ScheduleManager::schedulerPluginId() const
 
1709
{
 
1710
    return m_schedulerPluginId;
 
1711
}
 
1712
 
 
1713
void ScheduleManager::setSchedulerPluginId( const QString &id )
 
1714
{
 
1715
    m_schedulerPluginId = id;
 
1716
    m_project.changed( this );
 
1717
}
 
1718
 
 
1719
SchedulerPlugin *ScheduleManager::schedulerPlugin() const
 
1720
{
 
1721
    if ( m_schedulerPluginId.isEmpty() || !m_project.schedulerPlugins().contains( m_schedulerPluginId ) ) {
 
1722
        // try to avoid crash
 
1723
        return m_project.schedulerPlugins().value( m_project.schedulerPlugins().keys().value( 0 ) );
 
1724
    }
 
1725
    return m_project.schedulerPlugins().value( m_schedulerPluginId );
 
1726
}
 
1727
 
 
1728
QStringList ScheduleManager::schedulerPluginNames() const
 
1729
{
 
1730
    QStringList lst;
 
1731
    QMap<QString, SchedulerPlugin*>::const_iterator it = m_project.schedulerPlugins().constBegin();
 
1732
    QMap<QString, SchedulerPlugin*>::const_iterator end = m_project.schedulerPlugins().constEnd();
 
1733
    for ( ; it != end; ++it ) {
 
1734
        lst << it.value()->name();
 
1735
    }
 
1736
    return lst;
 
1737
}
 
1738
 
 
1739
int ScheduleManager::schedulerPluginIndex() const
 
1740
{
 
1741
    if ( m_schedulerPluginId.isEmpty() ) {
 
1742
        return 0;
 
1743
    }
 
1744
    return m_project.schedulerPlugins().keys().indexOf( m_schedulerPluginId );
 
1745
}
 
1746
 
 
1747
void ScheduleManager::setSchedulerPlugin( int index )
 
1748
{
 
1749
    if ( schedulerPlugin() ) {
 
1750
        schedulerPlugin()->stopCalculation( this ); // in case...
 
1751
    }
 
1752
 
 
1753
    m_schedulerPluginId = m_project.schedulerPlugins().keys().value( index );
 
1754
    debugPlan<<index<<m_schedulerPluginId;
 
1755
    m_project.changed( this );
 
1756
}
 
1757
 
 
1758
void ScheduleManager::calculateSchedule()
 
1759
{
 
1760
    m_calculationresult = CalculationRunning;
 
1761
    if ( schedulerPlugin() ) {
 
1762
        schedulerPlugin()->calculate( m_project, this );
 
1763
    }
 
1764
}
 
1765
 
 
1766
void ScheduleManager::stopCalculation()
 
1767
{
 
1768
    if ( schedulerPlugin() ) {
 
1769
        schedulerPlugin()->stopCalculation( this );
 
1770
    }
 
1771
}
 
1772
 
 
1773
void ScheduleManager::haltCalculation()
 
1774
{
 
1775
    if ( schedulerPlugin() ) {
 
1776
        schedulerPlugin()->haltCalculation( this );
 
1777
    }
 
1778
}
 
1779
 
 
1780
void ScheduleManager::setMaxProgress( int value )
 
1781
{
 
1782
    m_maxprogress = value;
 
1783
    emit maxProgressChanged( value );
 
1784
    m_project.changed( this );
 
1785
}
 
1786
 
 
1787
void ScheduleManager::setProgress( int value )
 
1788
{
 
1789
    m_progress = value;
 
1790
    emit progressChanged( value );
 
1791
    m_project.changed( this );
 
1792
}
 
1793
 
 
1794
void ScheduleManager::setDeleted( bool on )
 
1795
{
 
1796
    if ( m_expected ) {
 
1797
        m_expected->setDeleted( on );
 
1798
    }
 
1799
    m_project.changed( this );
 
1800
}
 
1801
 
 
1802
void ScheduleManager::setExpected( MainSchedule *sch )
 
1803
{
 
1804
    //debugPlan<<m_expected<<","<<sch;
 
1805
    if ( m_expected ) {
 
1806
        m_project.sendScheduleToBeRemoved( m_expected );
 
1807
        m_expected->setDeleted( true );
 
1808
        m_project.sendScheduleRemoved( m_expected );
 
1809
    }
 
1810
    m_expected = sch;
 
1811
    if ( sch ) {
 
1812
        m_project.sendScheduleToBeAdded( this, 0 );
 
1813
        sch->setManager( this );
 
1814
        m_expected->setDeleted( false );
 
1815
        m_project.sendScheduleAdded( sch );
 
1816
    }
 
1817
    m_project.changed( this );
 
1818
}
 
1819
 
 
1820
QStringList ScheduleManager::state() const
 
1821
{
 
1822
    QStringList lst;
 
1823
    if ( isBaselined() ) {
 
1824
        return lst << i18n( "Baselined" );
 
1825
    }
 
1826
    if ( m_scheduling ) {
 
1827
        return lst << i18n( "Scheduling" );
 
1828
    }
 
1829
    if ( m_expected == 0 ) {
 
1830
        return lst << i18n( "Not scheduled" );
 
1831
    }
 
1832
    if ( Schedule *s = m_expected ) {
 
1833
        if ( s->resourceError || s->resourceOverbooked || s->resourceNotAvailable || s->constraintError || s->schedulingError ) {
 
1834
            return lst << i18n( "Error" );
 
1835
        }
 
1836
        return s->state();
 
1837
    }
 
1838
    return lst;
 
1839
}
 
1840
 
 
1841
QList<long unsigned int> ScheduleManager::supportedGranularities() const
 
1842
{
 
1843
    QList<long unsigned int> lst;
 
1844
    if ( schedulerPlugin() ) {
 
1845
        lst = schedulerPlugin()->granularities();
 
1846
    }
 
1847
    return lst;
 
1848
}
 
1849
 
 
1850
int ScheduleManager::granularity() const
 
1851
{
 
1852
    if ( schedulerPlugin() ) {
 
1853
        return schedulerPlugin()->granularity();
 
1854
    }
 
1855
    return 0;
 
1856
}
 
1857
 
 
1858
void ScheduleManager::setGranularity( int duration )
 
1859
{
 
1860
    if ( schedulerPlugin() ) {
 
1861
        schedulerPlugin()->setGranularity( duration );
 
1862
    }
 
1863
    m_project.changed( this );
 
1864
}
 
1865
 
 
1866
void ScheduleManager::incProgress()
 
1867
{
 
1868
    m_project.incProgress();
 
1869
}
 
1870
 
 
1871
void ScheduleManager::logAdded( const Schedule::Log &log )
 
1872
{
 
1873
    emit sigLogAdded( log );
 
1874
    int row = expected()->logs().count() - 1;
 
1875
    emit logInserted( expected(), row, row );
 
1876
}
 
1877
 
 
1878
void ScheduleManager::slotAddLog( const QVector<KPlato::Schedule::Log> &log )
 
1879
{
 
1880
    if ( expected() && ! log.isEmpty() ) {
 
1881
        int first = expected()->logs().count();
 
1882
        int last = first + log.count() - 1;
 
1883
        Q_UNUSED(last);
 
1884
        foreach ( const KPlato::Schedule::Log &l, log ) {
 
1885
            expected()->addLog( l );
 
1886
        }
 
1887
    }
 
1888
}
 
1889
 
 
1890
QMap< int, QString > ScheduleManager::phaseNames() const
 
1891
{
 
1892
    if ( expected() ) {
 
1893
        return expected()->phaseNames();
 
1894
    }
 
1895
    return QMap<int, QString>();
 
1896
}
 
1897
 
 
1898
void ScheduleManager::setPhaseNames( const QMap<int, QString> &phasenames )
 
1899
{
 
1900
    if ( expected() ) {
 
1901
        expected()->setPhaseNames( phasenames );
 
1902
    }
 
1903
}
 
1904
 
 
1905
 
 
1906
bool ScheduleManager::loadXML( KoXmlElement &element, XMLLoaderObject &status )
 
1907
{
 
1908
    MainSchedule *sch = 0;
 
1909
    if ( status.version() <= "0.5" ) {
 
1910
        m_usePert = false;
 
1911
        sch = loadMainSchedule( element, status );
 
1912
        if ( sch ) {
 
1913
            sch->setManager( this );
 
1914
            switch ( sch->type() ) {
 
1915
                case Schedule::Expected: setExpected( sch ); break;
 
1916
            }
 
1917
        }
 
1918
        return true;
 
1919
    }
 
1920
    setName( element.attribute( "name" ) );
 
1921
    m_id = element.attribute( "id" );
 
1922
    m_usePert = (element.attribute( "distribution" ).toInt()) == 1;
 
1923
    m_allowOverbooking = (bool)(element.attribute( "overbooking" ).toInt());
 
1924
    m_checkExternalAppointments = (bool)(element.attribute( "check-external-appointments" ).toInt());
 
1925
    m_schedulingDirection = (bool)(element.attribute( "scheduling-direction" ).toInt());
 
1926
    m_baselined = (bool)(element.attribute( "baselined" ).toInt());
 
1927
    m_schedulerPluginId = element.attribute( "scheduler-plugin-id" );
 
1928
    if ( status.project().schedulerPlugins().contains( m_schedulerPluginId ) ) {
 
1929
        // atm we only load for current plugin
 
1930
        int g = element.attribute( "granularity", "0" ).toInt();
 
1931
        status.project().schedulerPlugins().value( m_schedulerPluginId )->setGranularity( g );
 
1932
    }
 
1933
    m_recalculate = (bool)(element.attribute( "recalculate" ).toInt());
 
1934
    m_recalculateFrom = DateTime::fromString( element.attribute( "recalculate-from" ), status.projectTimeZone() );
 
1935
    KoXmlNode n = element.firstChild();
 
1936
    for ( ; ! n.isNull(); n = n.nextSibling() ) {
 
1937
        if ( ! n.isElement() ) {
 
1938
            continue;
 
1939
        }
 
1940
        KoXmlElement e = n.toElement();
 
1941
        //debugPlan<<e.tagName();
 
1942
        if ( e.tagName() == "schedule" ) {
 
1943
            sch = loadMainSchedule( e, status );
 
1944
            if ( sch ) {
 
1945
                sch->setManager( this );
 
1946
                switch ( sch->type() ) {
 
1947
                    case Schedule::Expected: setExpected( sch ); break;
 
1948
                }
 
1949
            }
 
1950
        } else if ( e.tagName() == "plan" ) {
 
1951
            ScheduleManager *sm = new ScheduleManager( status.project() );
 
1952
            if ( sm->loadXML( e, status ) ) {
 
1953
                m_project.addScheduleManager( sm, this );
 
1954
            } else {
 
1955
                errorPlan<<"Failed to load schedule manager"<<endl;
 
1956
                delete sm;
 
1957
            }
 
1958
        }
 
1959
    }
 
1960
    return true;
 
1961
}
 
1962
 
 
1963
MainSchedule *ScheduleManager::loadMainSchedule( KoXmlElement &element, XMLLoaderObject &status ) {
 
1964
    MainSchedule *sch = new MainSchedule();
 
1965
    if ( sch->loadXML( element, status ) ) {
 
1966
        status.project().addSchedule( sch );
 
1967
        sch->setNode( &(status.project()) );
 
1968
        status.project().setParentSchedule( sch );
 
1969
    } else {
 
1970
        errorPlan << "Failed to load schedule" << endl;
 
1971
        delete sch;
 
1972
        sch = 0;
 
1973
    }
 
1974
    return sch;
 
1975
}
 
1976
 
 
1977
bool ScheduleManager::loadMainSchedule( MainSchedule *schedule, KoXmlElement &element, XMLLoaderObject &status ) {
 
1978
    long sid = schedule->id();
 
1979
    if ( schedule->loadXML( element, status ) ) {
 
1980
        if ( sid != schedule->id() && status.project().findSchedule( sid ) ) {
 
1981
            status.project().takeSchedule( schedule );
 
1982
        }
 
1983
        if ( ! status.project().findSchedule( schedule->id() ) ) {
 
1984
            status.project().addSchedule( schedule );
 
1985
        }
 
1986
        schedule->setNode( &(status.project()) );
 
1987
        status.project().setParentSchedule( schedule );
 
1988
        return true;
 
1989
    }
 
1990
    return false;
 
1991
}
 
1992
 
 
1993
void ScheduleManager::saveXML( QDomElement &element ) const
 
1994
{
 
1995
    QDomElement el = element.ownerDocument().createElement( "plan" );
 
1996
    element.appendChild( el );
 
1997
    el.setAttribute( "name", m_name );
 
1998
    el.setAttribute( "id", m_id );
 
1999
    el.setAttribute( "distribution", QString::number(m_usePert ? 1 : 0) );
 
2000
    el.setAttribute( "overbooking", QString::number(m_allowOverbooking) );
 
2001
    el.setAttribute( "check-external-appointments", QString::number(m_checkExternalAppointments) );
 
2002
    el.setAttribute( "scheduling-direction", QString::number(m_schedulingDirection) );
 
2003
    el.setAttribute( "baselined", QString::number(m_baselined) );
 
2004
    el.setAttribute( "scheduler-plugin-id", m_schedulerPluginId );
 
2005
    if ( schedulerPlugin() ) {
 
2006
        // atm we only save for current plugin
 
2007
        el.setAttribute( "granularity", QString::number(schedulerPlugin()->granularity()) );
 
2008
    }
 
2009
    el.setAttribute( "recalculate", QString::number(m_recalculate) );
 
2010
    el.setAttribute( "recalculate-from", m_recalculateFrom.toString( Qt::ISODate ) );
 
2011
    if ( m_expected && ! m_expected->isDeleted() ) {
 
2012
        QDomElement schs = el.ownerDocument().createElement( "schedule" );
 
2013
        el.appendChild( schs );
 
2014
        m_expected->saveXML( schs );
 
2015
        m_project.saveAppointments( schs, m_expected->id() );
 
2016
    }
 
2017
    foreach ( ScheduleManager *sm, m_children ) {
 
2018
        sm->saveXML( el );
 
2019
    }
 
2020
 
 
2021
}
 
2022
 
 
2023
void ScheduleManager::saveWorkPackageXML( QDomElement &element, const Node &node ) const
 
2024
{
 
2025
    QDomElement el = element.ownerDocument().createElement( "plan" );
 
2026
    element.appendChild( el );
 
2027
    el.setAttribute( "name", m_name );
 
2028
    el.setAttribute( "id", m_id );
 
2029
    el.setAttribute( "distribution", QString::number(m_usePert ? 1 : 0) );
 
2030
    el.setAttribute( "overbooking", QString::number(m_allowOverbooking) );
 
2031
    el.setAttribute( "check-external-appointments", QString::number(m_checkExternalAppointments) );
 
2032
    el.setAttribute( "scheduling-direction", QString::number(m_schedulingDirection) );
 
2033
    el.setAttribute( "baselined", QString::number(m_baselined) );
 
2034
    if ( m_expected && ! m_expected->isDeleted() ) { // TODO: should we check isScheduled() ?
 
2035
        QDomElement schs = el.ownerDocument().createElement( "schedule" );
 
2036
        el.appendChild( schs );
 
2037
        m_expected->saveXML( schs );
 
2038
        Schedule *s = node.findSchedule( m_expected->id() );
 
2039
        if ( s && ! s->isDeleted() ) {
 
2040
            s->saveAppointments( schs );
 
2041
        }
 
2042
    }
 
2043
}
 
2044
 
 
2045
 
 
2046
} //namespace KPlato
 
2047
 
 
2048
QDebug operator<<( QDebug dbg, const KPlato::Schedule *s )
 
2049
{
 
2050
    if ( s ) {
 
2051
        return dbg<<(*s);
 
2052
    }
 
2053
    return dbg<<"Schedule(0x0)";
 
2054
}
 
2055
QDebug operator<<( QDebug dbg, const KPlato::Schedule &s )
 
2056
{
 
2057
    dbg.nospace()<<"Schedule["<<s.id();
 
2058
    if (s.isDeleted()) {
 
2059
        dbg.nospace()<<": Deleted";
 
2060
    } else {
 
2061
        dbg.nospace()<<": "<<s.name();
 
2062
    }
 
2063
    dbg.nospace()<<"]";
 
2064
    return dbg.space();
 
2065
}
 
2066
 
 
2067
QDebug operator<<( QDebug dbg, const KPlato::Schedule::Log &log )
 
2068
{
 
2069
    dbg.nospace()<<"Schedule::Log: "<<log.formatMsg();
 
2070
    return dbg.space();
 
2071
}