Skip to content

Commit 40ce6b8

Browse files
authored
Merge pull request #1 from WebPlatformForEmbedded/timer
Launcher with scheduling support is added
2 parents ac6b868 + 8303bfc commit 40ce6b8

4 files changed

Lines changed: 373 additions & 30 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CMakeCache.txt
2+
Launcher.json
3+
Makefile
4+
cmake_install.cmake
5+
install_manifest.txt
6+
libWPEFrameworkLauncher.so

Launcher.cpp

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ SERVICE_REGISTRATION(Launcher, 1, 0);
6868

6969
/* virtual */ const string Launcher::Initialize(PluginHost::IShell* service)
7070
{
71+
Time relativeTime;
72+
Time interval;
7173
string message;
7274
Config config;
7375

@@ -79,34 +81,42 @@ SERVICE_REGISTRATION(Launcher, 1, 0);
7981

8082
config.FromString(_service->ConfigLine());
8183

82-
_closeTime = (config.CloseTime.Value());
83-
Core::Process::Options options(config.Command.Value().c_str());
84-
auto iter = config.Parameters.Elements();
84+
if (config.ScheduleTime.IsSet() == true) {
8585

86-
while (iter.Next() == true) {
87-
const Config::Parameter& element(iter.Current());
86+
relativeTime = Time(config.ScheduleTime.RelativeTime.Value());
87+
if (relativeTime.IsValid() != true) {
88+
SYSLOG(Trace::Warning, (_T("Time format is wrong")));
89+
}
8890

89-
if ((element.Option.IsSet() == true) && (element.Option.Value().empty() == false)) {
90-
if ((element.Value.IsSet() == true) && (element.Value.Value().empty() == false)) {
91-
options.Set(element.Option.Value(), element.Value.Value());
92-
}
93-
else {
94-
options.Set(element.Option.Value());
95-
}
91+
interval = Time(config.ScheduleTime.Interval.Value());
92+
if (interval.IsValid() != true) {
93+
SYSLOG(Trace::Warning, (_T("Interval format is wrong")));
9694
}
9795
}
9896

99-
_observer.Register(&_notification);
97+
_activity = Core::ProxyType<Job>::Create(&config, interval);
98+
if (_activity.IsValid() == true) {
99+
if (_activity->IsOperational() == false) {
100+
// Well if we where able to parse the parameters (if needed) we are ready to start it..
101+
_observer.Register(&_notification);
100102

101-
// Well if we where able to parse the parameters (if needed) we are ready to start it..
102-
_process.Launch(options, &_pid);
103+
if (relativeTime.IsValid() == true) {
104+
Core::Time scheduledTime(Core::Time::Now());
105+
uint64_t timeValueToTrigger = ((relativeTime.Hour() * 60 + relativeTime.Minute()) * 60 + relativeTime.Second()) * 1000;
106+
scheduledTime.Add(timeValueToTrigger);
103107

104-
if (_pid == 0) {
105-
_observer.Unregister(&_notification);
106-
message = _T("Could not spawn the requested app/script [") + config.Command.Value() + ']';
108+
PluginHost::WorkerPool::Instance().Schedule(scheduledTime, _activity);
109+
}
110+
else {
111+
PluginHost::WorkerPool::Instance().Submit(_activity);
112+
}
113+
}
114+
else {
115+
message = _T("Could not parse the configuration for the job.");
116+
}
107117
}
108118
else {
109-
_memory = Core::Service<MemoryObserverImpl>::Create<Exchange::IMemory>(_pid);
119+
message = _T("Could not create the job.");
110120
}
111121

112122
return (message);
@@ -124,13 +134,13 @@ SERVICE_REGISTRATION(Launcher, 1, 0);
124134
_memory = nullptr;
125135
}
126136

127-
if (_process.IsActive() == true) {
137+
if (_activity->Process().IsActive() == true) {
128138
// First try a gentle touch....
129-
_process.Kill(false);
139+
_activity->Process().Kill(false);
130140

131141
// Wait for a maximum of 3 Seconds before we shoot the process!!
132-
if (_process.WaitProcessCompleted(_closeTime * 1000) != Core::ERROR_NONE) {
133-
_process.Kill(true);
142+
if (_activity->Process().WaitProcessCompleted(_closeTime * 1000) != Core::ERROR_NONE) {
143+
_activity->Process().Kill(true);
134144
}
135145
}
136146

@@ -147,14 +157,20 @@ void Launcher::Update(const ProcessObserver::Info& info)
147157
{
148158
// This can potentially be called on a socket thread, so the deactivation (wich in turn kills this object) must be done
149159
// on a seperate thread. Also make sure this call-stack can be unwound before we are totally destructed.
150-
if (_pid == info.Id()) {
160+
if ((_activity.IsValid() == true) && (_activity->Pid() == info.Id())) {
151161

152162
ASSERT(_service != nullptr);
153163

154164
if (info.Event() == ProcessObserver::Info::EVENT_EXIT) {
155165

156166
if ((info.ExitCode() & 0xFFFF) == 0) {
157-
PluginHost::WorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::AUTOMATIC));
167+
// Only do this if we do not need a retrigger on an intervall.
168+
if (_activity->IsOperational() == false) {
169+
PluginHost::WorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::AUTOMATIC));
170+
}
171+
else {
172+
TRACE(Trace::Information, (_T("The process has run, and completed succefully.")));
173+
}
158174
}
159175
else {
160176
PluginHost::WorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(_service, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::FAILURE));
@@ -163,7 +179,6 @@ void Launcher::Update(const ProcessObserver::Info& info)
163179
}
164180
}
165181

166-
167182
} //namespace Plugin
168183

169184
} // namespace WPEFramework

Launcher.h

Lines changed: 219 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,45 @@ class Launcher : public PluginHost::IPlugin {
292292
Core::JSON::String Value;
293293
};
294294

295+
public:
296+
class Schedule : public Core::JSON::Container {
297+
private:
298+
Schedule& operator=(const Schedule&) = delete;
299+
300+
public:
301+
Schedule()
302+
: Core::JSON::Container()
303+
, RelativeTime()
304+
, Interval() {
305+
Add(_T("relativetime"), &RelativeTime);
306+
Add(_T("interval"), &Interval);
307+
}
308+
Schedule(const Schedule& copy)
309+
: Core::JSON::Container()
310+
, RelativeTime(copy.RelativeTime)
311+
, Interval(copy.Interval) {
312+
Add(_T("relativetime"), &RelativeTime);
313+
Add(_T("interval"), &Interval);
314+
}
315+
~Schedule() {
316+
}
317+
public:
318+
Core::JSON::String RelativeTime;
319+
Core::JSON::String Interval;
320+
};
321+
295322
public:
296323
Config()
297324
: Core::JSON::Container()
298325
, Command()
299326
, Parameters()
300327
, CloseTime(3)
328+
, ScheduleTime()
301329
{
302330
Add(_T("command"), &Command);
303331
Add(_T("parameters"), &Parameters);
304332
Add(_T("closetime"), &CloseTime);
333+
Add(_T("schedule"), &ScheduleTime);
305334
}
306335
~Config()
307336
{
@@ -311,6 +340,193 @@ class Launcher : public PluginHost::IPlugin {
311340
Core::JSON::String Command;
312341
Core::JSON::ArrayType<Parameter> Parameters;
313342
Core::JSON::DecUInt8 CloseTime;
343+
Schedule ScheduleTime;
344+
};
345+
346+
class Time {
347+
public:
348+
Time()
349+
: _hour(~0)
350+
, _minute(~0)
351+
, _second(~0)
352+
{
353+
}
354+
Time(const string& time)
355+
: _hour(~0)
356+
, _minute(~0)
357+
, _second(~0)
358+
{
359+
Parse(time);
360+
}
361+
Time(const Time& copy)
362+
: _hour(copy._hour)
363+
, _minute(copy._minute)
364+
, _second(copy._second)
365+
{
366+
}
367+
~Time ()
368+
{
369+
}
370+
371+
public:
372+
bool IsValid () const { return (HasSeconds() || HasMinutes() || HasHours()); }
373+
bool HasHours() const { return (_hour < 24); }
374+
bool HasMinutes() const { return (_minute < 60); }
375+
bool HasSeconds() const { return (_second < 60); }
376+
uint8_t Hour() const { return _hour; }
377+
uint8_t Minute() const { return _minute; }
378+
uint8_t Second() const { return _second; }
379+
380+
private:
381+
bool Parse(const string& time) {
382+
bool status = true;
383+
string t = time;
384+
385+
//Get hours
386+
uint8_t hour;
387+
string hValue = Split(t, ":");
388+
status = IsValidTime(hValue, hour, 24);
389+
if (status == true) {
390+
391+
//Get minutes
392+
uint8_t minute;
393+
string mValue = Split(t, ".");
394+
status = IsValidTime(mValue, minute, 60);
395+
if (status == true) {
396+
397+
//Store seconds
398+
uint8_t second;
399+
string sValue = t;
400+
status = IsValidTime(sValue, second, 60);
401+
if (status == true) {
402+
403+
//Check all the time components are still valid
404+
if ((hour > 0 && second > 0) && (minute == 0)) {
405+
status = false;
406+
TRACE(Trace::Information, (_T("Invalid time format")));
407+
}
408+
else { //Update time components
409+
_hour = hour;
410+
_minute = minute;
411+
_second = second;
412+
}
413+
}
414+
}
415+
}
416+
return status;
417+
}
418+
419+
private:
420+
inline bool IsDigit(const string& str) {
421+
return (str.find_first_not_of( "0123456789" ) == std::string::npos);
422+
}
423+
424+
inline bool IsValidTime(const string& str, uint8_t& time, const uint8_t limit) {
425+
bool status = true;
426+
if (IsDigit(str)) {
427+
int t = atoi(str.c_str());
428+
if (t >= limit || t < 0) {
429+
status = false;
430+
TRACE(Trace::Information, (_T("Invalid time %s"), str.c_str()));
431+
}
432+
else {
433+
time = t;
434+
}
435+
}
436+
else {
437+
status = false;
438+
TRACE(Trace::Information, (_T("Invalid time %s"), str.c_str()));
439+
}
440+
return status;
441+
}
442+
443+
inline string Split(string& str, const string delimiter) {
444+
string word;
445+
size_t position = str.find(delimiter, 0);
446+
if (position != string::npos) {
447+
word = str.substr(0, position);
448+
str = str.substr(word.size() + 1, str.size());
449+
}
450+
return word;
451+
}
452+
453+
private:
454+
uint8_t _hour;
455+
uint8_t _minute;
456+
uint8_t _second;
457+
};
458+
459+
public:
460+
class Job: public Core::IDispatchType<void> {
461+
private:
462+
Job() = delete;
463+
Job(const Job&) = delete;
464+
Job& operator=(const Job&) = delete;
465+
466+
public:
467+
Job(Config* config, const Time& interval)
468+
: _hasRun(false)
469+
, _pid(0)
470+
, _options(config->Command.Value().c_str())
471+
, _process(false)
472+
, _interval(interval)
473+
{
474+
auto iter = config->Parameters.Elements();
475+
476+
while (iter.Next() == true) {
477+
const Config::Parameter& element(iter.Current());
478+
479+
if ((element.Option.IsSet() == true) && (element.Option.Value().empty() == false)) {
480+
if ((element.Value.IsSet() == true) && (element.Value.Value().empty() == false)) {
481+
_options.Set(element.Option.Value(), element.Value.Value());
482+
}
483+
else {
484+
_options.Set(element.Option.Value());
485+
}
486+
}
487+
}
488+
}
489+
~Job()
490+
{
491+
}
492+
493+
public:
494+
bool IsOperational() const {
495+
496+
return ((_hasRun == true) && (_interval.IsValid() == true));
497+
}
498+
Core::Process& Process() {
499+
return (_process);
500+
}
501+
uint32_t Pid() {
502+
return _pid;
503+
}
504+
virtual void Dispatch() override
505+
{
506+
_hasRun = true;
507+
// Check if the process is not active, no need to reschedule the same job again.
508+
if (_process.IsActive() == false) {
509+
_process.Launch(_options, &_pid);
510+
}
511+
512+
if (_interval.IsValid() == true && ((_interval.Hour() != 0) || (_interval.Minute() != 0) || (_interval.Second() != 0))) {
513+
// Reschedule our next launch point...
514+
Core::Time scheduledTime(Core::Time::Now());
515+
uint64_t intervalTime = ((_interval.Hour() * 60 + _interval.Minute()) * 60 + _interval.Second()) * 1000;
516+
scheduledTime.Add(intervalTime);
517+
PluginHost::WorkerPool::Instance().Schedule(scheduledTime,Core::ProxyType<Core::IDispatch>(*this));
518+
}
519+
else {
520+
_hasRun = false;
521+
}
522+
}
523+
524+
private:
525+
bool _hasRun;
526+
uint32_t _pid;
527+
Core::Process::Options _options;
528+
Core::Process _process;
529+
Time _interval;
314530
};
315531

316532
public:
@@ -319,11 +535,10 @@ class Launcher : public PluginHost::IPlugin {
319535
#endif
320536
Launcher()
321537
: _service(nullptr)
322-
, _process(false)
323-
, _pid(0)
324538
, _closeTime(0)
325539
, _notification(this)
326540
, _memory(nullptr)
541+
, _activity()
327542
{
328543
}
329544
#ifdef __WIN32__
@@ -362,16 +577,16 @@ class Launcher : public PluginHost::IPlugin {
362577

363578
private:
364579
void Update(const ProcessObserver::Info& info);
580+
bool Execute();
365581

366582
private:
367583
PluginHost::IShell* _service;
368-
Core::Process _process;
369-
uint32_t _pid;
370584
uint8_t _closeTime;
371585
Core::Sink<Notification> _notification;
372586
Exchange::IMemory* _memory;
373587

374588
static ProcessObserver _observer;
589+
Core::ProxyType<Job> _activity;
375590
};
376591

377592
} //namespace Plugin

0 commit comments

Comments
 (0)