OSDN Git Service

c08bcd431d52a2923128348b838704d653e59829
[iptd/iPTd.git] / src / ry0 / iPTd / Reservation.cpp
1 /**\r
2  * @file Reservation.cpp\r
3  *\r
4  */\r
5 \r
6 #include <time.h>\r
7 #include <fcntl.h>\r
8 #include <io.h>\r
9 #include <direct.h>\r
10 \r
11 \r
12 #define DBG_LEVEL 3\r
13 #include "Raym/Log.h"\r
14 \r
15 #include "keys.h"\r
16 #include "mpeg2/ts/Analyzer.h"\r
17 #include "ry0/iPTd/Reservation.h"\r
18 #include "ry0/iPTd/Controller.h"\r
19 \r
20 using namespace Raym;\r
21 using namespace ry0::device;\r
22 \r
23 namespace ry0\r
24 {\r
25 namespace iPTd\r
26 {\r
27 \r
28 static const long long CMD_PERIODIC                 = 0x0001;   // 周期処理\r
29 static const long long CMD_COLLECT_EPG_ISDB_S       = 0x0002;   // 番組情報取得(ISDB-S)\r
30 static const long long CMD_COLLECT_EPG_ISDB_T       = 0x0003;   // 番組情報取得(ISDB-T)\r
31 \r
32 static const time_t OFFSET_OF_START_TIME            = -2;       // 録画開始時刻の補正(秒単位)\r
33 static const time_t OFFSET_OF_END_TIME              = -3;       // 録画停止時刻の補正(秒単位)\r
34 static const time_t OFFSET_OF_WAKEUP                = -240;     // 起動スケジュールの補正(秒単位)  注:休止するまでの時間(DEF_SUSPEND_TIME)よりも短くすること\r
35 static const time_t OFFSET_OF_SUPPRESSION_TIME      = -600;     // 録画開始前に休止の抑制を開始する時間(秒単位)\r
36 \r
37 static const TimeInterval DEF_COLLECT_EPG_DELAY     = 1.0;\r
38 static const TimeInterval DEF_COLLECT_EPG_RETRY     = 60.0;\r
39 static const TimeInterval DEF_COLLECT_EPG_LIMIT_S   = 10.5;\r
40 static const TimeInterval DEF_COLLECT_EPG_LIMIT_T   = 20.5;\r
41 \r
42 Reservation::Reservation()\r
43 {\r
44     _controller         = NULL;\r
45     _tuners             = NULL;\r
46     _reservations_path  = NULL;\r
47     _reservations       = NULL;\r
48     _reservation_seq_id = -1;\r
49     _epgs_path          = NULL;\r
50     _epgs               = NULL;\r
51     _timer_periodic     = NULL;\r
52     _timer_epg_s        = NULL;\r
53     _timer_epg_t        = NULL;\r
54     _cancel_epg_collect = false;\r
55 }\r
56 \r
57 Reservation::~Reservation()\r
58 {\r
59     RELEASE(_reservations_path);\r
60     RELEASE(_reservations);\r
61     RELEASE(_epgs_path);\r
62     RELEASE(_epgs);\r
63     RELEASE(_timer_periodic);\r
64     RELEASE(_timer_epg_s);\r
65     RELEASE(_timer_epg_t);\r
66 }\r
67 \r
68 Reservation *Reservation::alloc()\r
69 {\r
70     return new Reservation();\r
71 }\r
72 \r
73 Reservation *Reservation::initWithController(Controller *controller)\r
74 {\r
75     _controller  = controller;\r
76     _tuners      = controller->_tuners;\r
77 \r
78     _reservations_path = String::alloc()->initWithFormat("%s%s.iptd.reservations.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
79     if (_reservations_path == NULL)\r
80     {\r
81         release();\r
82         return NULL;\r
83     }\r
84 \r
85     _reservations = Dictionary::alloc()->initWithContentsOfFile(_reservations_path);\r
86     if (_reservations == NULL)\r
87     {\r
88         DebugLog1("reservations file: %s (created)", _reservations_path->cString());\r
89         _reservations = Dictionary::alloc()->initWithCapacity(0);\r
90         if (_reservations == NULL)\r
91         {\r
92             release();\r
93             return NULL;\r
94         }\r
95         _reservation_seq_id = 1;\r
96     }\r
97     else\r
98     {\r
99         DebugLog1("reservations file: %s", _reservations_path->cString());\r
100 \r
101         _reservation_seq_id = _reservations->integerForKey(KEY_EPG_LAST_RESV_ID);\r
102     }\r
103 \r
104     _epgs_path = String::alloc()->initWithFormat("%s%s.iptd.epgs.plist", _controller->_system_path->cString(), Controller::_plist_prefix);\r
105     if (_epgs_path == NULL)\r
106     {\r
107         release();\r
108         return NULL;\r
109     }\r
110 \r
111     _epgs = Dictionary::alloc()->initWithContentsOfFile(_epgs_path);\r
112     if (_epgs == NULL)\r
113     {\r
114         DebugLog1("epgs file: %s (created)", _epgs_path->cString());\r
115         _epgs = Dictionary::alloc()->initWithCapacity(0);\r
116         if (_epgs == NULL)\r
117         {\r
118             release();\r
119             return NULL;\r
120         }\r
121     }\r
122     else\r
123     {\r
124         DebugLog1("epgs file: %s", _epgs_path->cString());\r
125 \r
126         // 過去の番組データは削除\r
127         removePastEPGs();\r
128     }\r
129 \r
130     return this;\r
131 }\r
132 \r
133 bool Reservation::canSuspend()\r
134 {\r
135     bool result = true;\r
136 \r
137     RaymLock(_controller);\r
138     if (((_timer_epg_s != NULL) && _timer_epg_s->valid()) ||\r
139         ((_timer_epg_t != NULL) && _timer_epg_t->valid()))\r
140     {\r
141         result = false;\r
142     }\r
143     RaymUnlock(_controller);\r
144 \r
145     if (result)\r
146     {\r
147         result = canTerminate();\r
148     }\r
149 \r
150     return result;\r
151 }\r
152 \r
153 bool Reservation::canTerminate()\r
154 {\r
155     bool result = true;\r
156 \r
157     RaymLock(_controller);\r
158 \r
159     time_t now = time(NULL);\r
160 \r
161     for (int tuner = 0; tuner < _controller->_tuner_count; ++tuner)\r
162     {\r
163         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
164         if ((array == NULL) || (array->count() == 0))\r
165         {\r
166             continue;\r
167         }\r
168 \r
169         Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
170         time_t start;\r
171         time_t end;\r
172         Controller::getTimeWithEPG(epg, &start, &end);\r
173 \r
174         if (start + OFFSET_OF_SUPPRESSION_TIME <= now)\r
175         {\r
176             result = false;\r
177             break;\r
178         }\r
179     }\r
180 \r
181     RaymUnlock(_controller);\r
182 \r
183     return result;\r
184 }\r
185 \r
186 void Reservation::systemWillSuspend()\r
187 {\r
188     RaymLock(_controller);\r
189     _cancel_epg_collect = true;\r
190     RaymUnlock(_controller);\r
191 \r
192     if ((_timer_epg_s != NULL) && _timer_epg_s->valid())\r
193     {\r
194         _timer_epg_s->invalidate();\r
195     }\r
196     if ((_timer_epg_t != NULL) && _timer_epg_t->valid())\r
197     {\r
198         _timer_epg_t->invalidate();\r
199     }\r
200     if ((_timer_periodic != NULL) && _timer_periodic->valid())\r
201     {\r
202         _timer_periodic->invalidate();\r
203     }\r
204 \r
205     RaymLock(_controller);\r
206     _cancel_epg_collect = false;\r
207     RaymUnlock(_controller);\r
208 }\r
209 \r
210 void Reservation::systemResumed()\r
211 {\r
212     DebugLog2("%s", __FUNCTION__);\r
213 \r
214     RaymLock(_controller);\r
215 \r
216     _cancel_epg_collect = false;\r
217 \r
218     if (_timer_periodic == NULL)\r
219     {\r
220         _timer_periodic = Timer::alloc()->initWithTimeInterval(1.0, this, (void *)CMD_PERIODIC, true);\r
221         if (_timer_periodic != NULL)\r
222         {\r
223             _timer_periodic->fire();\r
224         }\r
225     }\r
226 \r
227     if (_timer_epg_s == NULL)\r
228     {\r
229         _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
230         if (_timer_epg_s != NULL)\r
231         {\r
232             _timer_epg_s->fire();\r
233         }\r
234     }\r
235 \r
236     if (_timer_epg_t == NULL)\r
237     {\r
238         _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
239         if (_timer_epg_t != NULL)\r
240         {\r
241             _timer_epg_t->fire();\r
242         }\r
243     }\r
244 \r
245     RaymUnlock(_controller);\r
246 }\r
247 \r
248 //\r
249 // 録画予約:チューナ/EPG指定\r
250 //\r
251 bool Reservation::reserve(int tuner, Dictionary *in_epg)\r
252 {\r
253     DebugLog2("Reservation::reserve(tuner, epg)");\r
254 \r
255     bool result = false;\r
256 \r
257     // lock\r
258     RaymLock(_controller);\r
259 \r
260     while ((0 <= tuner) && (tuner < _controller->_tuner_count) && (in_epg != NULL))\r
261     {\r
262         Dictionary *epg = Dictionary::dictionaryWithDictionary(in_epg);\r
263         if (epg == NULL)\r
264         {\r
265             DebugLog3("Dictionary::dictionaryWithDictionary() ng.");\r
266             break;\r
267         }\r
268 \r
269         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
270         if (array == NULL)\r
271         {\r
272             array = Array::arrayWithCapacity(0);\r
273             _reservations->setObject(array, _tuners[tuner]->name());\r
274         }\r
275 \r
276         time_t epg_start;\r
277         time_t epg_end;\r
278         Controller::getTimeWithEPG(epg, &epg_start, &epg_end);\r
279         DebugLog2("epg start: %ld, end: %ld\n", epg_start, epg_end);\r
280 \r
281         time_t pre_start = 0;\r
282         time_t pre_end = 0;\r
283         pre_end = time(NULL);\r
284         DebugLog2("pre_end: %ld", pre_end);\r
285 \r
286         for (uint i = 0; i < array->count(); ++i)\r
287         {\r
288             Dictionary *cur = (Dictionary *)array->objectAtIndex(i);\r
289             time_t cur_start;\r
290             time_t cur_end;\r
291             Controller::getTimeWithEPG(cur, &cur_start, &cur_end);\r
292             DebugLog2("cur start: %ld, end: %ld\n", cur_start, cur_end);\r
293             if ((pre_end <= epg_start) && (epg_end <= cur_start))\r
294             {\r
295                 DebugLog2("insert: %d\n", i);\r
296                 array->insertObject(epg, i);\r
297                 result = true;\r
298                 break;\r
299             }\r
300             pre_start = cur_start;\r
301             pre_end = cur_end;\r
302         }\r
303 \r
304         if (!result)\r
305         {\r
306             if (pre_end <= epg_start)\r
307             {\r
308                 DebugLog2("add\n");\r
309                 array->addObject(epg);\r
310                 result = true;\r
311             }\r
312             else\r
313             {\r
314                 DebugLog2("no add\n");\r
315             }\r
316         }\r
317         if (result)\r
318         {\r
319             epg->setInteger(_reservation_seq_id, KEY_EPG_RESV_ID);\r
320             _reservation_seq_id = (_reservation_seq_id + 1) % 1000000;\r
321             _reservations->setInteger(_reservation_seq_id, KEY_EPG_LAST_RESV_ID);\r
322 \r
323             //\r
324             _reservations->writeToFile(_reservations_path, true);\r
325         }\r
326 \r
327         break;\r
328     }\r
329 \r
330     // unlock\r
331     RaymUnlock(_controller);\r
332 \r
333     return result;\r
334 }\r
335 \r
336 //\r
337 // tuner: 0 - (_tunerCount - 1)  cancel current\r
338 // tuner: -1  cancel reserve_id\r
339 //\r
340 bool Reservation::cancel(int tuner, int reserve_id)\r
341 {\r
342     bool result = false;\r
343 \r
344     // lock\r
345     RaymLock(_controller);\r
346 \r
347     //\r
348     if ((0 <= tuner) && (tuner < _controller->_tuner_count))\r
349     {\r
350         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
351         if (array != NULL)\r
352         {\r
353             if (array->count() > 0)\r
354             {\r
355                 Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
356                 String *status = epg->stringForKey(KEY_EPG_STATUS);\r
357                 if (status != NULL)\r
358                 {\r
359                     if (status->isEqualToString("running"))\r
360                     {\r
361                         epg->setString("stop", KEY_EPG_STATUS);\r
362                         result = true;\r
363                     }\r
364                 }\r
365             }\r
366         }\r
367     }\r
368 \r
369     //\r
370     else if ((tuner < 0) && (0 <= reserve_id) && (reserve_id < 1000000))\r
371     {\r
372         for (int i = 0; i < _controller->_tuner_count; ++i)\r
373         {\r
374             Array *array = _reservations->arrayForKey(_tuners[i]->name());\r
375             if (array != NULL)\r
376             {\r
377                 for (uint j = 0; j < array->count(); ++j)\r
378                 {\r
379                     Dictionary *epg = (Dictionary *)array->objectAtIndex(j);\r
380                     if (reserve_id == epg->integerForKey(KEY_EPG_RESV_ID))\r
381                     {\r
382                         String *status = epg->stringForKey(KEY_EPG_STATUS);\r
383                         if ((status != NULL) && status->isEqualToString("running"))\r
384                         {\r
385                             epg->setString("stop", KEY_EPG_STATUS);\r
386                         }\r
387                         else\r
388                         {\r
389                             array->removeObjectAtIndex(j);\r
390                         }\r
391                         result = true;\r
392                         break;\r
393                     }\r
394                 }\r
395             }\r
396             if (result)\r
397             {\r
398                 break;\r
399             }\r
400         }\r
401     }\r
402 \r
403     if (result)\r
404     {\r
405         _reservations->writeToFile(_reservations_path, true);\r
406     }\r
407 \r
408     // unlock\r
409     RaymUnlock(_controller);\r
410 \r
411     return result;\r
412 }\r
413 \r
414 void Reservation::updateSchedule()\r
415 {\r
416     DebugLog2("Reservation::updateSchedule()");\r
417 \r
418     RaymLock(_controller);\r
419 \r
420     // レジューム時刻\r
421     time_t resume_time = 0;\r
422 \r
423     // EPG収集時刻を取得\r
424     String *collect_epg_time = _controller->_props->stringForKey(KEY_COLLECT_EPG_TIME);\r
425     if (collect_epg_time != NULL)\r
426     {\r
427         Controller::getTimeWithString(collect_epg_time, &resume_time);\r
428     }\r
429 \r
430     // 録画予約をチェック\r
431     for (int tuner = 0; tuner < _controller->_tuner_count; ++tuner)\r
432     {\r
433         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
434         if ((array == NULL) || (array->count() == 0))\r
435         {\r
436             // next tuner\r
437             continue;\r
438         }\r
439 \r
440         Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
441         time_t start = 0;\r
442         time_t end = 0;\r
443         Controller::getTimeWithEPG(epg, &start, &end);\r
444         if ((start != 0) && (end != 0))\r
445         {\r
446             if (start < resume_time)\r
447             {\r
448                 resume_time = start;\r
449             }\r
450         }\r
451     }\r
452 \r
453     //\r
454     TM resume_tm;\r
455     resume_time += OFFSET_OF_WAKEUP;    // 起動時刻を 5分前に設定(OFFSET_OF_WAKEUPで調整)\r
456     if (localtime_s(&resume_tm, &resume_time) == 0)\r
457     {\r
458         _controller->resetWakeSchedule();\r
459         if (_controller->setWakeSchedule(resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min))\r
460         {\r
461             DebugLog0("set wake schedule to \"%04d/%02d/%02d %02d:%02d:00\".",\r
462                       resume_tm.tm_year + 1900, resume_tm.tm_mon + 1, resume_tm.tm_mday, resume_tm.tm_hour, resume_tm.tm_min);\r
463         }\r
464     }\r
465 \r
466     // unlock\r
467     RaymUnlock(_controller);\r
468 }\r
469 \r
470 std::string Reservation::createVideoPath(int tuner)\r
471 {\r
472     DebugLog2("Reservation::createVideoPath()");\r
473 \r
474     std::string result = "";\r
475 \r
476     while (true)\r
477     {\r
478         time_t now;\r
479         time(&now);\r
480         TM tm;\r
481         if (localtime_s(&tm, &now) != 0)\r
482         {\r
483             break;\r
484         }\r
485 \r
486         result = _controller->_props->stringForKey(KEY_STORE_PATH)->cString();\r
487         DebugLog2("result: %s\n", result.c_str());\r
488 \r
489         char tmp[128];\r
490         if (sprintf_s(tmp, sizeof(tmp), "\\%04d", tm.tm_year + 1900) < 0)\r
491         {\r
492             DebugLog0("sprintf_s() error: year\n");\r
493             result = "";\r
494             break;\r
495         }\r
496         result += tmp;\r
497         DebugLog2("result: %s\n", result.c_str());\r
498 \r
499         STAT stat;\r
500         if (_stat(result.c_str(), &stat) != 0)\r
501         {\r
502             if (_mkdir(result.c_str()) != 0)\r
503             {\r
504                 DebugLog0("_mkdir() error: year\n");\r
505                 result = "";\r
506                 break;\r
507             }\r
508             _stat(result.c_str(), &stat);\r
509         }\r
510         if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
511         {\r
512             DebugLog0("%s is not directory.\n", result.c_str());\r
513             result = "";\r
514             break;\r
515         }\r
516 \r
517         if (sprintf_s(tmp, sizeof(tmp), "\\%02d", tm.tm_mon + 1) < 0)\r
518         {\r
519             DebugLog0("sprintf_s() error: month\n");\r
520             result = "";\r
521             break;\r
522         }\r
523         result += tmp;\r
524         DebugLog2("result: %s\n", result.c_str());\r
525 \r
526         if (_stat(result.c_str(), &stat) != 0)\r
527         {\r
528             if (_mkdir(result.c_str()) != 0)\r
529             {\r
530                 DebugLog0("_mkdir() error: month\n");\r
531                 result = "";\r
532                 break;\r
533             }\r
534             _stat(result.c_str(), &stat);\r
535         }\r
536         if ((stat.st_mode & _S_IFDIR) != _S_IFDIR)\r
537         {\r
538             DebugLog0("%s is not directory.", result.c_str());\r
539             result = "";\r
540             break;\r
541         }\r
542 \r
543         if (sprintf_s(tmp, sizeof(tmp),\r
544                       "\\%04d%02d%02d_%02d%02d%02d_%03d_%s.ts",\r
545                       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,\r
546                       _tuners[tuner]->channel(), _tuners[tuner]->name()) < 0)\r
547         {\r
548             DebugLog0("sprintf_s() error: filename");\r
549             result = "";\r
550             break;\r
551         }\r
552         result += tmp;\r
553         DebugLog2("result: %s\n", result.c_str());\r
554 \r
555         break;\r
556     }\r
557 \r
558     return result;\r
559 }\r
560 \r
561 void Reservation::removePastEPGs()\r
562 {\r
563     DebugLog2("Reservation::removePastEPGs()");\r
564 \r
565     time_t now = time(NULL);\r
566 \r
567     RaymLock(_controller);\r
568 \r
569     Array *keys1 = _epgs->allKeys();\r
570     for (uint i = 0; i < keys1->count(); ++i)\r
571     {\r
572         Dictionary *events = _epgs->dictionaryForKey((String *)keys1->objectAtIndex(i));\r
573         if (events != NULL)\r
574         {\r
575             Array *keys2 = events->allKeys();\r
576             for (uint j = 0; j < keys2->count(); ++j)\r
577             {\r
578                 String *key = (String *)keys2->objectAtIndex(j);\r
579                 Dictionary *epg = events->dictionaryForKey(key);\r
580                 if (epg != NULL)\r
581                 {\r
582                     time_t start = 0;\r
583                     time_t end = 0;\r
584                     Controller::getTimeWithEPG(epg, &start, &end);\r
585 \r
586                     if (now > end)\r
587                     {\r
588                         events->removeObjectForKey(key);\r
589                     }\r
590                 }\r
591             }\r
592         }\r
593     }\r
594 \r
595     RaymUnlock(_controller);\r
596 }\r
597 \r
598 void Reservation::collectEPGsForTuner(int tuner, TimeInterval limit)\r
599 {\r
600     DebugLog2("Reservation::collectEPGsForTuner(%d) start.", tuner);\r
601 \r
602     // 既にロックされた状態でコールされる前提\r
603     bool locked = false;\r
604     RaymLock(_controller);\r
605     if ((0 <= tuner) && (tuner < _controller->_tuner_count))\r
606     {\r
607         locked = _tuners[tuner]->isLocked();\r
608     }\r
609     RaymUnlock(_controller);\r
610     if (!locked)\r
611     {\r
612         DebugLog2("Reservation::collectEPGsForTuner(%d) end(no locked).", tuner);\r
613         return;\r
614     }\r
615 \r
616     // \r
617     Array *collected = Array::arrayWithCapacity(0);\r
618 \r
619     // 開始時刻取得\r
620     Date *start = Date::date();\r
621 \r
622     // 取得開始\r
623     bool done = false;\r
624     while (!done)\r
625     {\r
626         AutoreleasePool *pool = AutoreleasePool::alloc()->init();\r
627 \r
628         // EPG取得\r
629         MPEG2::TS::Analyzer an;\r
630         an.setFlag(MPEG2::TS::Analyzer::FLAG_EIT);\r
631         _tuners[tuner]->setListener(&an);\r
632         Array *epgs = an.epgInfos();\r
633         _tuners[tuner]->setListener(NULL);\r
634 \r
635         if (epgs != NULL)\r
636         {\r
637             // 取得成功\r
638             collected->addObjectsFromArray(epgs);\r
639         }\r
640 \r
641         // リミットチェック\r
642         if (Date::date()->timeIntervalSinceDate(start) >= limit)\r
643         {\r
644             done = true;\r
645         }\r
646 \r
647         // キャンセル確認\r
648         RaymLock(_controller);\r
649         if (!done)\r
650         {\r
651             done = _cancel_epg_collect;\r
652         }\r
653         RaymUnlock(_controller);\r
654 \r
655         pool->release();\r
656     }\r
657 \r
658     // lock\r
659     RaymLock(_controller);\r
660 \r
661     for (uint j = 0; j < collected->count(); ++j)\r
662     {\r
663         Dictionary *epg1 = (Dictionary *)collected->objectAtIndex(j);\r
664 \r
665         if (epg1->stringForKey(KEY_EPG_TITLE) == NULL)\r
666         {\r
667             // タイトルが無い場合は不要\r
668             continue;\r
669         }\r
670 \r
671         // Service ID を Primary Key\r
672         String *key = epg1->stringForKey(KEY_EPG_SERVICE_ID);\r
673         if (key != NULL)\r
674         {\r
675             Dictionary *epgs_of_service = _epgs->dictionaryForKey(key);\r
676             if (epgs_of_service == NULL)\r
677             {\r
678                 // 無い場合は作成\r
679                 epgs_of_service = Dictionary::dictionaryWithCapacity(0);\r
680                 _epgs->setObject(epgs_of_service, key);\r
681             }\r
682             // Event ID を Secondary Key\r
683             key = epg1->stringForKey(KEY_EPG_EVENT_ID);\r
684             if (key != NULL)\r
685             {\r
686                 epgs_of_service->setObject(epg1, key);\r
687             }\r
688         }\r
689     }\r
690 \r
691     // ファイルへ書き出し\r
692     _epgs->writeToFile(_epgs_path, true);\r
693 \r
694     // unlock\r
695     RaymUnlock(_controller);\r
696 \r
697     DebugLog2("Reservation::collectEPGsForTuner(%d) end.", tuner);\r
698 }\r
699 \r
700 bool Reservation::collectEPGs(Tuner::Type type)\r
701 {\r
702     DebugLog0("Reservation::collectEPGs(%s) start.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
703 \r
704     // 使用するチューナを決定\r
705     int tuner = 0;\r
706     bool locked = false;\r
707     RaymLock(_controller);\r
708     for (int i = _controller->_tuner_count - 1; i >= 0; --i)\r
709     {\r
710         if (_controller->isTunerEnabled(i))\r
711         {\r
712             if ((!_tuners[i]->isLocked()) && (_tuners[i]->type() == type))\r
713             {\r
714                 if (_tuners[i]->lock())\r
715                 {\r
716                     tuner = i;\r
717                     locked = true;\r
718                     break;\r
719                 }\r
720             }\r
721         }\r
722     }\r
723     RaymUnlock(_controller);\r
724 \r
725     // ロック確認\r
726     if (!locked)\r
727     {\r
728         DebugLog0("Controller::collectEPGs(%s) end(Can't locked).", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
729         // ロックできない場合は収集しない\r
730         return false;\r
731     }\r
732 \r
733     bool canceled = false;\r
734     DebugLog0("start collect EPG of \"%s(#%d)\".", _tuners[tuner]->name(), tuner);\r
735 \r
736     // 現在のチャンネルを保存\r
737     int channel = _tuners[tuner]->channel();\r
738 \r
739     // チャンネルを変更しつつEPGを取得\r
740     int max_channel = (type == Tuner::ISDB_S ? Tuner::MAX_CHANNELS_ISDB_S : Tuner::MAX_CHANNELS_ISDB_T);\r
741     for (int ch = 0; ch <= max_channel; ++ch)\r
742     {\r
743         // チャンネルが有効かチェック\r
744         if (_controller->isChannelEnabled(tuner, ch))\r
745         {\r
746             // チャンネル設定\r
747             if (_tuners[tuner]->setChannel(ch))\r
748             {\r
749                 // EPG取集\r
750                 collectEPGsForTuner(tuner, ((type == Tuner::ISDB_S) ? DEF_COLLECT_EPG_LIMIT_S : DEF_COLLECT_EPG_LIMIT_T));\r
751 \r
752                 // キャンセル確認\r
753                 EnterCriticalSection(&_cs);\r
754                 if (_cancel_epg_collect)\r
755                 {\r
756                     ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
757                     canceled = true;\r
758                 }\r
759                 LeaveCriticalSection(&_cs);\r
760 \r
761                 // キャンセル確認\r
762                 //   終了不可 -> 録画待機中/録画中  なので、収集は諦める\r
763                 if (!canTerminate())\r
764                 {\r
765                     ch = Tuner::MAX_CHANNELS_ISDB_T + 1;\r
766                     canceled = true;\r
767                 }\r
768             }\r
769         }\r
770     }\r
771 \r
772     // 元のチャンネルに戻す\r
773     _tuners[tuner]->setChannel(channel);\r
774 \r
775     // lock\r
776     EnterCriticalSection(&_cs);\r
777 \r
778     // チューナをアンロック\r
779     _tuners[tuner]->unlock();\r
780 \r
781     // unlock\r
782     LeaveCriticalSection(&_cs);\r
783 \r
784     if (canceled)\r
785     {\r
786         DebugLog0("collect EPG of \"%s\" was canceled.", _tuners[tuner]->name());\r
787     }\r
788     else\r
789     {\r
790         DebugLog0("collect EPG of \"%s\" was finished.", _tuners[tuner]->name());\r
791 \r
792         // 過去のEPGを削除\r
793         removePastEPGs();\r
794     }\r
795 \r
796     DebugLog2("Reservation::collectEPGs(%s) end.", type == Tuner::ISDB_S ? "ISDB-S" : "ISDB-T");\r
797 \r
798     return true;\r
799 }\r
800 \r
801 void Reservation::periodic()\r
802 {\r
803     bool need_update = false;\r
804 \r
805 #ifdef RAYM_MEMORY_CHECK\r
806 //    DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
807 #endif\r
808 \r
809     // lock\r
810     RaymLock(_controller);\r
811 \r
812     // 現在時刻取得\r
813     time_t now = time(NULL);\r
814 \r
815 //    DebugLog2("periodic: %d", now);\r
816 \r
817     //\r
818     for (int tuner = 0; tuner < _controller->_tuner_count; ++tuner)\r
819     {\r
820         Array *array = _reservations->arrayForKey(_tuners[tuner]->name());\r
821         if ((array == NULL) || (array->count() == 0))\r
822         {\r
823             // next tuner\r
824             continue;\r
825         }\r
826 \r
827         //\r
828         Dictionary *epg = (Dictionary *)array->objectAtIndex(0);\r
829         time_t start;\r
830         time_t end;\r
831         Controller::getTimeWithEPG(epg, &start, &end);\r
832         \r
833         //\r
834         // 録画停止要否チェック\r
835         //\r
836         bool stop_need = false;\r
837         while (true)\r
838         {\r
839             String *status = epg->stringForKey(KEY_EPG_STATUS);\r
840             if (status != NULL)\r
841             {\r
842                 if (status->isEqualToString("stop"))\r
843                 {\r
844                     stop_need = true;\r
845                     break;\r
846                 }\r
847                 if (!status->isEqualToString("running"))\r
848                 {\r
849                     break;\r
850                 }\r
851             }\r
852             if (end + OFFSET_OF_END_TIME <= now)\r
853             {\r
854                 stop_need = true;\r
855             }\r
856             break;\r
857         }\r
858         if (stop_need)\r
859         {\r
860             DebugLog2("I try stop\n");\r
861             int fd = _tuners[tuner]->stopRecording();\r
862             if (fd < 0)\r
863             {\r
864                 DebugLog1("stopRecording() error.\n");\r
865             }\r
866             else\r
867             {\r
868                 DebugLog2("stopRecording() ok\n");\r
869                 DebugLog0("stop recording of \"%s\"", _tuners[tuner]->name());\r
870                 _close(fd);\r
871             }\r
872             array->removeObject(epg);\r
873             \r
874             if (array->count() > 0)\r
875             {\r
876                 epg = (Dictionary *)array->objectAtIndex(0);\r
877             }\r
878             else\r
879             {\r
880                 epg = NULL;\r
881             }\r
882             need_update = true;\r
883         }\r
884 \r
885         if (epg == NULL)\r
886         {\r
887             // next tuner\r
888             continue;\r
889         }\r
890 \r
891         //\r
892         // 録画開始要否チェック\r
893         //\r
894         bool start_need = false;\r
895         start = end = 0;\r
896         Controller::getTimeWithEPG(epg, &start, &end);\r
897         if ((start != 0) && (end != 0))\r
898         {\r
899             String *status = epg->stringForKey(KEY_EPG_STATUS);\r
900             if ((status == NULL) || !(status->isEqualToString("running")))\r
901             {\r
902                 if (end + OFFSET_OF_END_TIME <= now)\r
903                 {\r
904                     // 既に終了時間が経過しているので削除する\r
905                     array->removeObject(epg);\r
906                 }\r
907                 else if (start + OFFSET_OF_START_TIME <= now)\r
908                 {\r
909                     start_need = true;\r
910                 }\r
911             }\r
912         }\r
913 \r
914         if (start_need)\r
915         {\r
916             DebugLog2("I need start.\n");\r
917             String *ch = epg->stringForKey(KEY_EPG_CHANNEL);\r
918             if (ch != NULL)\r
919             {\r
920                 int channel = atoi(ch->cString());\r
921                 DebugLog2("channel: %d\n", channel);\r
922                 std::string videopath = createVideoPath(tuner);\r
923                 if (videopath != "")\r
924                 {\r
925                     DebugLog2("videopath: %s\n", videopath.c_str());\r
926                     int fd = -1;\r
927                     if (_sopen_s(&fd, videopath.c_str(),\r
928                                  (_O_CREAT | _O_EXCL | _O_WRONLY | _O_BINARY | _O_TRUNC), _SH_DENYRW, (_S_IREAD | _S_IWRITE)) == 0)\r
929                     {\r
930                         DebugLog2("open ok.\n");\r
931                         bool startResult = true;\r
932                         if (_tuners[tuner]->channel() != channel)\r
933                         {\r
934                             if (!_controller->setChannel(tuner, channel))\r
935                             {\r
936                                 DebugLog3("setChannel() ng.");\r
937                                 startResult = false;\r
938                             }\r
939                         }\r
940 \r
941                         if (startResult)\r
942                         {\r
943                             if (_tuners[tuner]->startRecording(fd))\r
944                             {\r
945                                 DebugLog2("startRecording() ok.");\r
946                                 DebugLog0("start recording of \"%s\" to %s.", _tuners[tuner]->name(), videopath.c_str());\r
947                             }\r
948                             else\r
949                             {\r
950                                 DebugLog3("Tuner::startRecording() failed.");\r
951                                 startResult = false;\r
952                             }\r
953                         }\r
954 \r
955                         if (startResult)\r
956                         {\r
957                             epg->setString("running", KEY_EPG_STATUS);\r
958                         }\r
959                         else\r
960                         {\r
961                             _close(fd);\r
962                         }\r
963                     }\r
964                     else\r
965                     {\r
966                         DebugLog0("open ng. 0x%08x\n", errno);\r
967                     }\r
968                 }\r
969                 else\r
970                 {\r
971                     DebugLog0("Can't create videopath.\n");\r
972                 }\r
973             }\r
974             else\r
975             {\r
976                 DebugLog0("error.\n");\r
977             }\r
978         }\r
979     }\r
980 \r
981     if (need_update)\r
982     {\r
983         //\r
984         _reservations->writeToFile(_reservations_path, true);\r
985     }\r
986 \r
987     // EPG収集時刻を取得\r
988     String *collect_str = _controller->_props->stringForKey(KEY_COLLECT_EPG_TIME);\r
989     if (collect_str != NULL)\r
990     {\r
991         // 秒に変換\r
992         time_t collect_time = 0;\r
993         Controller::getTimeWithString(collect_str, &collect_time);\r
994 \r
995         // 現在時刻と比較\r
996         if ((collect_time <= now) && (now < collect_time + 1))\r
997         {\r
998             // タイマが起動中か確認\r
999             if ((_timer_epg_s == NULL) || !_timer_epg_s->valid())\r
1000             {\r
1001                 // EPG収集用タイマ起動(ISDB-S)\r
1002                 RELEASE(_timer_epg_s);\r
1003                 _timer_epg_s = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_S, true);\r
1004                 if (_timer_epg_s != NULL)\r
1005                 {\r
1006                     _timer_epg_s->fire();\r
1007                 }\r
1008             }\r
1009 \r
1010             // タイマが起動中か確認\r
1011             if ((_timer_epg_t == NULL) || !_timer_epg_t->valid())\r
1012             {\r
1013                 // EPG収集用タイマ起動(ISDB-T)\r
1014                 RELEASE(_timer_epg_t);\r
1015                 _timer_epg_t = Timer::alloc()->initWithTimeInterval(DEF_COLLECT_EPG_DELAY, this, (void *)CMD_COLLECT_EPG_ISDB_T, true);\r
1016                 if (_timer_epg_t != NULL)\r
1017                 {\r
1018                     _timer_epg_t->fire();\r
1019                 }\r
1020             }\r
1021         }\r
1022     }\r
1023 \r
1024     // unlock\r
1025     RaymUnlock(_controller);\r
1026 \r
1027     //\r
1028     // 1/100秒単位が 0 に近くなるように次回T.O.を微調整\r
1029     // ただし、windowsは精度が低いので期待しないことw\r
1030     //\r
1031 \r
1032 #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)\r
1033     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000Ui64;\r
1034 #else\r
1035     static const __time64_t DELTA_EPOCH_IN_MICROSECS = 11644473600000000ULL;\r
1036 #endif\r
1037     // 現在時刻を取得\r
1038     FILETIME ft;\r
1039     GetSystemTimeAsFileTime(&ft);\r
1040 \r
1041     // EPOCH秒への変換\r
1042     __time64_t now_sec;\r
1043     __time64_t now_usec;\r
1044     now_sec = ft.dwHighDateTime;\r
1045     now_sec <<= 32;\r
1046     now_sec |= ft.dwLowDateTime;\r
1047     now_sec /= 10;  /*convert into microseconds*/\r
1048     now_sec -= DELTA_EPOCH_IN_MICROSECS;\r
1049     now_usec = (now_sec % 1000000UL);\r
1050     now_sec = now_sec / 1000000UL;\r
1051 \r
1052     TimeInterval interval = (TimeInterval)now_usec;\r
1053     interval = interval / 1000000;\r
1054     _timer_periodic->setTimeInterval(1.005 - interval);\r
1055 \r
1056 #ifdef RAYM_MEMORY_CHECK\r
1057 //    DebugLog0("global_objc_count_ = %d", Raym::global_raym_count_);\r
1058 #endif\r
1059 }\r
1060 \r
1061 void Reservation::timerExpired(Timer *timer, void *userInfo)\r
1062 {\r
1063     switch ((long long)userInfo)\r
1064     {\r
1065     case CMD_PERIODIC:\r
1066         periodic();\r
1067         break;\r
1068 \r
1069     case CMD_COLLECT_EPG_ISDB_S:\r
1070         if (collectEPGs(Tuner::ISDB_S))\r
1071         {\r
1072             _timer_epg_s->setRepeats(false);\r
1073         }\r
1074         else\r
1075         {\r
1076             _timer_epg_s->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
1077         }\r
1078         break;\r
1079 \r
1080     case CMD_COLLECT_EPG_ISDB_T:\r
1081         if (collectEPGs(Tuner::ISDB_T))\r
1082         {\r
1083             _timer_epg_t->setRepeats(false);\r
1084         }\r
1085         else\r
1086         {\r
1087             _timer_epg_t->setTimeInterval(DEF_COLLECT_EPG_RETRY);\r
1088         }\r
1089         break;\r
1090     }\r
1091 }\r
1092 \r
1093 } // iPTd\r
1094 } // ry0\r