00001
00008 #include <stdlib.h>
00009 #include <stdio.h>
00010 #include <string.h>
00011 #include <math.h>
00012 #include <float.h>
00013 #include <ctype.h>
00014
00015 #include "platform.h"
00016 #include "object.h"
00017 #include "output.h"
00018 #include "schedule.h"
00019 #include "exception.h"
00020
00021 #ifndef QNAN
00022 #define QNAN sqrt(-1)
00023 #endif
00024
00025 static SCHEDULE *schedule_list = NULL;
00026 static SCHEDULEXFORM *schedule_xformlist=NULL;
00027
00028 SCHEDULEXFORM *scheduletransform_getnext(SCHEDULEXFORM *xform)
00029 {
00030 return xform?xform->next:schedule_xformlist;
00031 }
00032
00033 int schedule_add_xform(XFORMSOURCE stype,
00034 double *source,
00035 double *target,
00036 double scale,
00037 double bias,
00038 OBJECT *obj,
00039 PROPERTY *prop)
00040 {
00041 SCHEDULEXFORM *xform = (SCHEDULEXFORM*)malloc(sizeof(SCHEDULEXFORM));
00042 if (xform==NULL)
00043 return 0;
00044 xform->source_type = stype;
00045 xform->source = source;
00046 xform->source_addr = source;
00047 xform->target_obj = obj;
00048 xform->target_prop = prop;
00049 xform->target = target;
00050 xform->scale = scale;
00051 xform->bias = bias;
00052 xform->next = schedule_xformlist;
00053 schedule_xformlist = xform;
00054 return 1;
00055 }
00056
00060 SCHEDULE *schedule_getnext(SCHEDULE *sch)
00061 {
00062 return sch==NULL ? schedule_list : sch->next;
00063 }
00064
00068 SCHEDULE *schedule_find_byname(char *name)
00069 {
00070 SCHEDULE *sch;
00071 for (sch=schedule_list; sch!=NULL; sch=sch->next)
00072 {
00073 if (strcmp(sch->name,name)==0)
00074 return sch;
00075 }
00076 return NULL;
00077 }
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 int schedule_matcher(char *pattern, unsigned char *table, int max, int base)
00088 {
00089 int go=0;
00090 int start=0;
00091 int stop=-1;
00092 int range=0;
00093 char *p;
00094 memset(table,0,max);
00095 for (p=pattern; ; p++)
00096 {
00097 switch (*p) {
00098 case '\0':
00099 go = 1;
00100 break;
00101 case '*':
00102
00103 start=base; stop=max; go=1;
00104 break;
00105 case ',':
00106
00107 go = 1;
00108 break;
00109 case '-':
00110
00111 range = 1;
00112 stop = 0;
00113 break;
00114 case '0':
00115 case '1':
00116 case '2':
00117 case '3':
00118 case '4':
00119 case '5':
00120 case '6':
00121 case '7':
00122 case '8':
00123 case '9':
00124 if (range)
00125 stop = stop*10 + (*p-'0');
00126 else
00127 stop = start = start*10 + (*p-'0');
00128 break;
00129 default:
00130 return 0;
00131 break;
00132 }
00133 if (go && stop>=0)
00134 { int i;
00135
00136
00137 if (start<base)
00138 {
00139 output_warning("schedule_matcher(char *pattern='%s',...) start before min of %d", pattern,base);
00140 start = base;
00141 }
00142
00143
00144 if (stop>max)
00145 {
00146 output_warning("schedule_matcher(char *pattern='%s',...) end exceed max of %d", pattern,max);
00147 stop = max;
00148 }
00149
00150
00151 if (start>stop)
00152 {
00153 for (i=start; i<=max; i++)
00154 table[i-base] = 1;
00155 for (i=base; i<=stop; i++)
00156 table[i-base] = 1;
00157 }
00158 else
00159 {
00160 for (i=start; i<=stop; i++)
00161 table[i-base] = 1;
00162 }
00163
00164
00165 start = range = go = 0;
00166 stop = -1;
00167 }
00168 if (*p=='\0')
00169 break;
00170 }
00171
00172 return 1;
00173 }
00174
00177 int find_value_index (SCHEDULE *sch,
00178 unsigned char block,
00179 double value)
00180 {
00181 int ndx;
00182 for (ndx=0; ndx<(int)(sch->count[block]); ndx++)
00183 {
00184 if ((float)(sch->data[block*MAXVALUES+ndx]) == (float)value)
00185 return ndx;
00186 }
00187 return -1;
00188 }
00189
00190
00191
00192
00193 int schedule_compile_block(SCHEDULE *sch, char *blockname, char *blockdef)
00194 {
00195 char *token = NULL;
00196 unsigned int minute=0;
00197
00198
00199 if (sch->block>=MAXBLOCKS)
00200 {
00201 output_error("schedule_compile(SCHEDULE *sch='{name=%s, ...}') maximum number of blocks reached", sch->name);
00202
00203
00204
00205 return 0;
00206 }
00207
00208
00209 sch->count[sch->block]=1;
00210 while ( (token=strtok(token==NULL?blockdef:NULL,";\r\n"))!=NULL )
00211 {
00212 struct {
00213 char *name;
00214 int base;
00215 int max;
00216 char pattern[256];
00217 char table[60];
00218 } matcher[] = {
00219 {"minute",0,59},
00220 {"hour",0,23},
00221 {"day",1,31},
00222 {"month",1,12},
00223 {"weekday",0,8},
00224 }, *match;
00225 unsigned int calendar;
00226 double value=1.0;
00227 int ndx;
00228
00229
00230 while (isspace(*token)) token++;
00231 if (strcmp(token,"")==0)
00232 continue;
00233 if (sscanf(token,"%s%*[ \t]%s%*[ \t]%s%*[ \t]%s%*[ \t]%s%*[ \t]%lf",matcher[0].pattern,matcher[1].pattern,matcher[2].pattern,matcher[3].pattern,matcher[4].pattern,&value)<5)
00234 {
00235 output_error("schedule_compile(SCHEDULE *sch='{name=%s, ...}') ignored an invalid definition '%s'", sch->name, token);
00236
00237
00238
00239 continue;
00240 }
00241 else
00242 {
00243 if ((ndx=find_value_index(sch,sch->block,value))==-1)
00244 {
00245 ndx = sch->count[sch->block]++;
00246
00247 if(ndx > MAXVALUES-1)
00248 {
00249 output_error("schedule_compile(SCHEDULE *sch='{name=%s, ...}') maximum number of values reached in block %i", sch->name, sch->block);
00250 return 0;
00251 }
00252 sch->data[sch->block*MAXVALUES+ndx] = value;
00253 }
00254 sch->sum[sch->block] += value;
00255 sch->abs[sch->block] += (value<0?-value:value);
00256 }
00257
00258
00259 for (match=matcher; match<matcher+sizeof(matcher)/sizeof(matcher[0]); match++)
00260 {
00261
00262 if (!schedule_matcher(match->pattern,match->table,match->max,match->base))
00263 {
00264 output_error("schedule_compile(SCHEDULE *sch={name='%s', ...}) %s pattern syntax error in item '%s'", sch->name, match->name, token);
00265
00266
00267
00268 return 0;
00269 }
00270 }
00271
00272
00273 for (calendar=0; calendar<14; calendar++)
00274 {
00275 unsigned int weekday = calendar%7;
00276 unsigned int is_leapyear = (calendar>=7?1:0);
00277 unsigned int calendar = weekday*2+is_leapyear;
00278 unsigned int month;
00279 unsigned int days[] = {31,(is_leapyear?29:28),31,30,31,30,31,31,30,31,30,31};
00280 unsigned int n = sch->block*MAXVALUES + ndx;
00281 minute = 0;
00282 for (month=0; month<12; month++)
00283 {
00284 unsigned int day;
00285 if (!matcher[3].table[month])
00286 {
00287 minute+=60*24*days[month];
00288 continue;
00289 }
00290 for (day=0; day<days[month]; weekday++,day++)
00291 {
00292 unsigned int hour;
00293 weekday%=7;
00294 if (!matcher[4].table[weekday] || !matcher[2].table[day])
00295 {
00296 minute+=60*24;
00297 continue;
00298 }
00299 for (hour=0; hour<24; hour++)
00300 {
00301 unsigned int stop = minute+60;
00302 if (!matcher[1].table[hour])
00303 {
00304 minute = stop;
00305 continue;
00306 }
00307 while (minute<stop)
00308 {
00309 if (matcher[0].table[minute%60])
00310 {
00311 if (sch->index[calendar][minute]>0)
00312 {
00313 char *dayofweek[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun","Hol"};
00314 output_error("schedule_compile(SCHEDULE *sch={name='%s', ...}) '%s' in block '%s' has a conflict with value %g on %s %d/%d %02d:%02d", sch->name, token, blockname, sch->data[sch->index[calendar][minute]], dayofweek[weekday], month+1, day+1, hour, minute%60);
00315
00316
00317
00318 return 0;
00319 }
00320 else
00321 {
00322
00323 sch->index[calendar][minute] = n;
00324 sch->weight[n]++;
00325 sch->minutes[sch->block]++;
00326
00327 }
00328 }
00329 minute++;
00330 }
00331 }
00332 }
00333 }
00334 }
00335 }
00336 strcpy(sch->blockname[sch->block],blockname);
00337 return 1;
00338 }
00339
00340
00341
00342
00343 int schedule_compile(SCHEDULE *sch)
00344 {
00345 char *p = sch->definition, *q = NULL;
00346 char blockdef[65536];
00347 char blockname[64];
00348 enum {INIT, NAME, OPEN, BLOCK, CLOSE} state = INIT;
00349 int comment=0;
00350
00351
00352 if (strchr(p,'{')==NULL && strchr(p,'}')==NULL)
00353 {
00354
00355
00356 while (isspace(*p)) p++;
00357 strcpy(blockdef,p);
00358 if (schedule_compile_block(sch,"*",blockdef))
00359 {
00360 sch->block++;
00361 return 1;
00362 }
00363 else
00364 return 0;
00365 }
00366
00367
00368 while (*p!='\0')
00369 {
00370
00371 if (*p=='#')
00372 {
00373 comment=1;
00374 p++;
00375 continue;
00376 }
00377 else if (comment)
00378 {
00379 if (*p=='\n')
00380 comment=0;
00381 p++;
00382 continue;
00383 }
00384
00385 switch (state) {
00386 case INIT:
00387 case CLOSE:
00388 if (!isspace(*p) && !iscntrl(*p))
00389 {
00390 if (sch->block>=MAXBLOCKS)
00391 {
00392 output_error("maximum number of allowed schedule blocks exceeded");
00393
00394
00395
00396 return 0;
00397 }
00398 state = NAME;
00399 q = blockname;
00400
00401 }
00402 else
00403 p++;
00404 break;
00405 case NAME:
00406 if (isspace(*p) || iscntrl(*p))
00407 {
00408 state = OPEN;
00409 p++;
00410 }
00411 else if (*p=='{' || *p==';')
00412 {
00413 state = OPEN;
00414 }
00415 else
00416 {
00417 if (q<blockname+sizeof(blockname)-1)
00418 {
00419 *q++ = *p++;
00420 *q = '\0';
00421 }
00422 else
00423 {
00424 output_error("schedule name is too long");
00425
00426
00427
00428 return 0;
00429 }
00430 }
00431 break;
00432 case OPEN:
00433 if (*p==';')
00434 {
00435 if (strcmp(blockname,"weighted")==0)
00436 sch->flags |= SN_WEIGHTED;
00437 else if (strcmp(blockname,"absolute")==0)
00438 sch->flags |= SN_ABSOLUTE;
00439 else if (strcmp(blockname,"normal")==0)
00440 sch->flags |= SN_NORMAL;
00441 else if (strcmp(blockname,"positive")==0)
00442 sch->flags |= SN_POSITIVE;
00443 else if (strcmp(blockname,"nonzero")==0)
00444 sch->flags |= SN_NONZERO;
00445 else if (strcmp(blockname,"boolean")==0)
00446 sch->flags |= SN_BOOLEAN;
00447 else
00448 output_error("schedule %s: block option '%s' is not recognized", sch->name, blockname);
00449 state = CLOSE;
00450 p++;
00451 }
00452 else if (*p=='{')
00453 {
00454 state = BLOCK;
00455 q = blockdef;
00456 p++;
00457 }
00458 else if (!isspace(*p) && !iscntrl(*p))
00459 {
00460 output_error("schedule %s: unexpected text before block start", sch->name);
00461
00462
00463
00464 return 0;
00465 }
00466 else
00467 p++;
00468 break;
00469 case BLOCK:
00470 if (*p=='}')
00471 {
00472 state = CLOSE;
00473 q = NULL;
00474 p++;
00475 if (schedule_compile_block(sch,blockname,blockdef))
00476 sch->block++;
00477 else
00478 return 0;
00479 }
00480 else
00481 {
00482 if (q<blockdef+sizeof(blockdef)-1)
00483 {
00484 *q++ = *p++;
00485 *q = '\0';
00486 }
00487 else
00488 {
00489 output_error("schedule name is too long");
00490
00491
00492
00493 return 0;
00494 }
00495 }
00496 break;
00497 default:
00498 break;
00499 }
00500 }
00501 return 1;
00502 }
00503
00513 SCHEDULE *schedule_create(char *name,
00514 char *definition)
00515 {
00516
00517 SCHEDULE *sch = schedule_find_byname(name);
00518 if (sch!=NULL)
00519 {
00520 if (definition!=NULL && strcmp(sch->definition,definition)!=0)
00521 {
00522 output_error("schedule_create(char *name='%s', char *definition='%s') definition does not match previous definition of schedule '%s')", name, definition, name);
00523
00524
00525
00526
00527 }
00528 return sch;
00529 }
00530
00531
00532 else if (definition==NULL)
00533 {
00534 return NULL;
00535 }
00536
00537
00538 sch = schedule_new();
00539 if (sch==NULL)
00540 {
00541 output_error("schedule_create(char *name='%s', char *definition='%s') memory allocation failed)", name, definition);
00542
00543
00544
00545 return NULL;
00546 }
00547 output_debug("schedule '%s' uses %.1f MB of memory", name, sizeof(SCHEDULE)/1000000.0);
00548 if (strlen(name)>=sizeof(sch->name))
00549 {
00550 output_error("schedule_create(char *name='%s', char *definition='%s') name too long)", name, definition);
00551
00552
00553
00554 free(sch);
00555 return NULL;
00556 }
00557 strcpy(sch->name,name);
00558 if (strlen(definition)>=sizeof(sch->definition))
00559 {
00560 output_error("schedule_create(char *name='%s', char *definition='%s') definition too long)", name, definition);
00561
00562
00563
00564 free(sch);
00565 return NULL;
00566 }
00567 strcpy(sch->definition,definition);
00568
00569
00570 if (schedule_compile(sch))
00571 {
00572
00573 unsigned char calendar;
00574 int invariant = 1;
00575 for (calendar=0; calendar<14; calendar++)
00576 {
00577
00578 int t = sizeof(sch->dtnext[calendar])/sizeof(sch->dtnext[calendar][0])-1;
00579
00580
00581 sch->dtnext[calendar][t] = 1;
00582
00583
00584 for (t--; t>=0; t--)
00585 {
00586
00587 int index0 = sch->index[calendar][t];
00588 int index1 = sch->index[calendar][t+1];
00589
00590
00591 if (sch->data[index0]==sch->data[index1])
00592 {
00593
00594 if (sch->dtnext[calendar][t]<255)
00595
00596
00597 sch->dtnext[calendar][t] = sch->dtnext[calendar][t+1] + 1;
00598 else
00599
00600
00601 sch->dtnext[calendar][t] = 1;
00602 }
00603 else
00604 {
00605
00606 sch->dtnext[calendar][t] = 1;
00607 invariant = 0;
00608 }
00609 }
00610 }
00611
00612
00613 if (invariant)
00614 memset(sch->dtnext,0,sizeof(sch->dtnext));
00615
00616
00617 if (sch->flags!=0)
00618 schedule_normalize(sch,sch->flags);
00619
00620
00621 if ((sch->flags&(SN_POSITIVE|SN_NONZERO|SN_BOOLEAN)) != 0 && ! schedule_validate(sch,sch->flags))
00622 return NULL;
00623
00624
00625 schedule_add(sch);
00626 return sch;
00627 }
00628 else
00629 {
00630
00631 free(sch);
00632 return NULL;
00633 }
00634 }
00635
00636 SCHEDULE *schedule_new(void)
00637 {
00638
00639 SCHEDULE *sch = (SCHEDULE*)malloc(sizeof(SCHEDULE));
00640 if (sch==NULL) return NULL;
00641
00642
00643 memset(sch,0,sizeof(SCHEDULE));
00644 sch->next_t = TS_NEVER;
00645 return sch;
00646 }
00647 void schedule_add(SCHEDULE *sch)
00648 {
00649 sch->next = schedule_list;
00650 schedule_list = sch;
00651 }
00652
00656 int schedule_validate(SCHEDULE *sch, int flags)
00657 {
00658 unsigned int b,i;
00659 int failed=0;
00660 for (b=0; b<MAXBLOCKS; b++) {
00661 for (i=1; i<=sch->count[b]; i++)
00662 {
00663 double value = sch->data[b*MAXVALUES+i];
00664 int weight = sch->weight[b*MAXVALUES+i];
00665 int unit = (weight>0 && value==1.0);
00666 int zero = (weight>0 && value==0.0);
00667 int positive = (weight>0 && value>0.0);
00668 int negative = (weight>0 && value<0.0);
00669 if ((flags&SN_BOOLEAN) && !(unit || zero))
00670 {
00671 output_error("schedule %s fails 'boolean' validation in block %s at schedule index %d", sch->name, sch->blockname[b], i);
00672 failed = 1;
00673 }
00674 else if ((flags&SN_POSITIVE) && negative)
00675 {
00676 output_error("schedule %s fails 'positive' validation in block %s at schedule index %d", sch->name, sch->blockname[b], i);
00677 failed = 1;
00678 }
00679 else if ((flags&SN_NONZERO) && zero)
00680 {
00681 output_error("schedule %s fails 'nonzero' validation in block %s at schedule index %d", sch->name, sch->blockname[b], i);
00682 failed = 1;
00683 }
00684 }
00685 }
00686 return !failed;
00687 }
00688
00693 int schedule_normalize(SCHEDULE *sch,
00694 int flags)
00695 {
00696 unsigned int b,i;
00697 int count=0;
00698
00699
00700 if (sch->flags&flags)
00701 return -1;
00702
00703
00704 for (b=0; b<MAXBLOCKS; b++)
00705 {
00706
00707 if (sch->count[b]==0)
00708 continue;
00709
00710
00711 if (flags&SN_WEIGHTED)
00712 {
00713 double scale[MAXVALUES];
00714 unsigned int i;
00715 int nonzero = 0;
00716 memset(scale,0,sizeof(scale));
00717 for (i=1; i<=sch->count[b]; i++)
00718 {
00719 if (sch->weight[i]!=0)
00720 {
00721 nonzero = 1;
00722 scale[i] += sch->data[b*MAXVALUES+i] * sch->weight[i] / sch->minutes[b];
00723 }
00724 }
00725 if (nonzero)
00726 {
00727 for (i=1; i<=sch->count[b]; i++)
00728 sch->data[b*MAXVALUES+i]*=scale[i];
00729 }
00730 }
00731
00732
00733 else
00734 {
00735 double scale = (flags&SN_ABSOLUTE?sch->abs[b]:sch->sum[b]);
00736
00737
00738 if (scale!=0)
00739 {
00740
00741 count++;
00742 for (i=1; i<=sch->count[b]; i++)
00743 sch->data[b*MAXVALUES+i]/=scale;
00744 }
00745 }
00746 }
00747
00748
00749 sch->flags |= flags;
00750
00751 return count;
00752 }
00753
00757 SCHEDULEINDEX schedule_index(SCHEDULE *sch, TIMESTAMP ts)
00758 {
00759 SCHEDULEINDEX ref = 0;
00760 DATETIME dt;
00761
00762
00763 if (!local_datetime(ts,&dt))
00764 {
00765 throw_exception("schedule_read(SCHEDULE *schedule={name='%s',...}, TIMESTAMP ts=%"FMT_INT64"d) unable to determine local time", sch->name, ts);
00766
00767
00768
00769
00770 }
00771
00772
00773 SET_CALENDAR(ref, ((dt.weekday-dt.yearday+53*7)%7)*2 + ISLEAPYEAR(dt.year));
00774
00775
00776 SET_MINUTE(ref, (dt.yearday*24 + dt.hour)*60 + dt.minute);
00777
00778
00779 return ref;
00780 }
00781
00785 double schedule_value(SCHEDULE *sch,
00786 SCHEDULEINDEX index)
00787 {
00788 long int cal = GET_CALENDAR(index);
00789 long int min = GET_MINUTE(index);
00790 return sch->data[sch->index[GET_CALENDAR(index)][GET_MINUTE(index)]];
00791 }
00792
00796 long schedule_dtnext(SCHEDULE *sch,
00797 SCHEDULEINDEX index)
00798 {
00799 return sch->dtnext[GET_CALENDAR(index)][GET_MINUTE(index)];
00800 }
00801
00802 long schedule_duration(SCHEDULE *sch,
00803 SCHEDULEINDEX index)
00804 {
00805 int block = (sch->index[GET_CALENDAR(index)][GET_MINUTE(index)]>>6)&MAXBLOCKS;
00806 return sch->minutes[block];
00807 }
00808
00809 double schedule_weight(SCHEDULE *sch,
00810 SCHEDULEINDEX index)
00811 {
00812 return sch->weight[sch->index[GET_CALENDAR(index)][GET_MINUTE(index)]];
00813 }
00814
00818 TIMESTAMP schedule_sync(SCHEDULE *sch,
00819 TIMESTAMP t)
00820 {
00821 static TIMESTAMP last_t = 0;
00822 static SCHEDULEINDEX index = {0};
00823 double value;
00824 long dtnext;
00825 double duration;
00826
00827
00828 if (t!=last_t) index = schedule_index(sch,t);
00829 value = schedule_value(sch,index);
00830 dtnext = schedule_dtnext(sch,index)*60;
00831 duration = (double)schedule_duration(sch,index);
00832
00833
00834 if (value!=sch->value || sch->duration!=duration)
00835 {
00836
00837 sch->value = value;
00838 sch->duration = duration / 60;
00839 sch->fraction = schedule_weight(sch,index) / duration;
00840 }
00841
00842
00843 sch->next_t = (dtnext==0 ? TS_NEVER : t + dtnext - t % 60);
00844 return sch->next_t;
00845 }
00846
00850 TIMESTAMP schedule_syncall(TIMESTAMP t1)
00851 {
00852 SCHEDULE *sch;
00853 TIMESTAMP t2 = TS_NEVER;
00854 for (sch=schedule_list; sch!=NULL; sch=sch->next)
00855 {
00856 TIMESTAMP t3 = schedule_sync(sch,t1);
00857 if (t3<t2) t2 = t3;
00858 }
00859
00860 return t2;
00861 }
00862
00863 TIMESTAMP scheduletransform_syncall(TIMESTAMP t1, XFORMSOURCE restrict)
00864 {
00865 SCHEDULEXFORM *xform;
00866
00867 for (xform=schedule_xformlist; xform!=NULL; xform=xform->next)
00868 {
00869 if (xform->source_type&restrict)
00870 *(xform->target) = *(xform->source) * xform->scale + xform->bias;
00871 }
00872 return TS_NEVER;
00873 }
00874
00875 int schedule_test(void)
00876 {
00877 int failed = 0;
00878 int ok = 0;
00879 int errorcount = 0;
00880 char ts[64];
00881
00882
00883 struct s_test {
00884 char *name, *def;
00885 char *t1, *t2;
00886 int normalize;
00887 double value;
00888 } *p, test[] = {
00889
00890 {"empty", "", "2000/01/01 00:00:00", "NEVER", 0, 0.0},
00891 {"halfday-binary", "* 12-23 * * *", "2001/02/03 01:30:00", "2001/02/03 12:00:00", 0, 0.0},
00892 {"halfday-binary", NULL, "2002/03/05 13:45:00", "2002/03/06 00:00:00", 0, 1.0},
00893 {"halfday-bimodal", "* 0-11 * * * 0.25; * 12-23 * * * 0.75;", "2003/04/07 01:15:00", "2003/04/07 12:00:00", 0, 0.25},
00894 {"halfday-bimodal", "* 0-11 * * * 0.25; * 12-23 * * * 0.75;", "2004/05/09 00:00:00", "2004/05/09 12:00:00", 0, 0.25},
00895 {"halfday-bimodal", NULL, "2005/06/11 13:20:00", "2005/06/12 00:00:00", 0, 0.75},
00896 {"halfday-bimodal", NULL, "2006/07/13 12:00:00", "2006/07/14 00:00:00", 0, 0.75},
00897 {"halfday-bimodal", NULL, "2007/08/15 00:00:00", "2007/08/15 12:00:00", 0, 0.25},
00898 {"quarterday-normal", "* 0-5 * * *; * 12-17 * * *;", "2008/09/17 00:00:00", "2008/09/17 06:00:00", SN_WEIGHTED, 0.5},
00899 {"quarterday-normal", NULL, "2009/10/19 06:00:00", "2009/10/19 12:00:00", SN_WEIGHTED, 0.0},
00900 {"quarterday-normal", NULL, "2010/11/21 12:00:00", "2010/11/21 18:00:00", SN_WEIGHTED, 0.5},
00901 {"quarterday-normal", NULL, "2011/12/23 18:00:00", "2011/12/24 00:00:00", SN_WEIGHTED, 0.0},
00902 };
00903
00904 output_test("\nBEGIN: schedule tests");
00905 for (p=test;p<test+sizeof(test)/sizeof(test[0]);p++)
00906 {
00907 TIMESTAMP t1 = convert_to_timestamp(p->t1);
00908 int errors=0;
00909 SCHEDULE *s = schedule_create(p->name, p->def);
00910 output_test("Schedule %s { %s } sync to %s...", p->name, p->def?p->def:(s?s->definition:"???"), convert_from_timestamp(t1,ts,sizeof(ts))?ts:"???");
00911 if (s==NULL)
00912 {
00913 output_test(" ! schedule %s { %s } create failed", p->name, p->def);
00914 errors++;
00915 }
00916 else
00917 {
00918 TIMESTAMP t2;
00919 if (p->normalize)
00920 schedule_normalize(s,p->normalize);
00921 t2 = s?schedule_sync(s,t1):TS_NEVER;
00922 if (s->value!=p->value)
00923 {
00924 output_test(" ! expected value %lg but found %lg", p->value, s->value);
00925 errors++;
00926 }
00927 if (t2!=convert_to_timestamp(p->t2))
00928 {
00929 output_test(" ! expected next time %s but found %s", p->t2, convert_from_timestamp(t2,ts,sizeof(ts))?ts:"???");
00930 errors++;
00931 }
00932 }
00933 if (errors==0)
00934 {
00935 output_test(" test passed");
00936 ok++;
00937 }
00938 else
00939 failed++;
00940 errorcount+=errors;
00941 }
00942
00943
00944 if (failed)
00945 {
00946 output_error("scheduletest: %d schedule tests failed--see test.txt for more information",failed);
00947 output_test("!!! %d schedule tests failed, %d errors found",failed,errorcount);
00948 }
00949 else
00950 {
00951 output_verbose("%d schedule tests completed with no errors--see test.txt for details",ok);
00952 output_test("scheduletest: %d schedule tests completed, %d errors found",ok,errorcount);
00953 }
00954 output_test("END: schedule tests");
00955 return failed;
00956 }
00957
00958 void schedule_dump(SCHEDULE *sch, char *file)
00959 {
00960 FILE *fp = fopen(file,"w");
00961 int calendar;
00962
00963
00964 fprintf(fp,"schedule %s { %s }\n", sch->name, sch->definition);
00965 fprintf(fp,"sizeof(SCHEDULE) = %.3f MB\n", (double)sizeof(SCHEDULE)/1024/1024);
00966 for (calendar=0; calendar<14; calendar++)
00967 {
00968 int year=0, month, y;
00969 int daysinmonth[] = {31,((calendar&1)?29:28),31,30,31,30,31,31,30,31,30,31};
00970 char *monthname[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
00971 fprintf(fp,"\nYears:");
00972 for (y=1970; y<2039; y++)
00973 {
00974 DATETIME dt = {y,0,1,0,0,0};
00975 TIMESTAMP ts = mkdatetime(&dt);
00976 SCHEDULEINDEX ndx = schedule_index(sch,ts);
00977 if (GET_CALENDAR(ndx)==calendar)
00978 {
00979 fprintf(fp," %d",y);
00980 if (year==0) year=y;
00981 }
00982 }
00983 fprintf(fp," (calendar %d)\n",calendar);
00984
00985 for (month=0; month<12; month++)
00986 {
00987 int day, hour;
00988 fprintf(fp," %s ", monthname[month]);
00989 for (hour=0; hour<24; hour++)
00990 {
00991 fprintf(fp," %2d:00", hour);
00992 }
00993 fprintf(fp,"\n");
00994 for (day=0; day<daysinmonth[month]; day++)
00995 {
00996 int hour;
00997 char wd[] = "SMTWTFSH";
00998 DATETIME dt = {year,month,day,0,0,0,0,0,""};
00999 TIMESTAMP ts = mkdatetime(&dt);
01000 local_datetime(ts,&dt);
01001 fprintf(fp," %c %2d",wd[dt.weekday],day+1);
01002 for (hour=0; hour<24; hour++)
01003 {
01004 int minute=0;
01005 DATETIME dt = {year,month,day,hour,0,0};
01006 TIMESTAMP ts = mkdatetime(&dt);
01007 SCHEDULEINDEX ndx = schedule_index(sch,ts);
01008 fprintf(fp,"%5g%c",schedule_value(sch,ndx),schedule_dtnext(sch,ndx)<60?'*':' ');
01009 }
01010 fprintf(fp,"\n");
01011 }
01012 }
01013 }
01014
01015 fclose(fp);
01016 }
01017