19eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// Copyright 2015 The Weave Authors. All rights reserved.
29eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// Use of this source code is governed by a BSD-style license that can be
39eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// found in the LICENSE file.
49eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
53fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine#include "examples/daemon/common/daemon.h"
63fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine
79eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine#include <weave/device.h>
89eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine#include <weave/provider/task_runner.h>
99eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
109eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine#include <base/bind.h>
119eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine#include <base/memory/weak_ptr.h>
129eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
13d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenkonamespace {
14d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko
15d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenkoconst char kTraits[] = R"({
16d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko  "_sample": {
17d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko    "commands": {
188d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko      "hello": {
19d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko        "minimalRole": "user",
20d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko        "parameters": {
218d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko          "name": { "type": "string" }
22e6fee32eeccd56bc0e07e513aa53e31042baff7bVitaly Buka        },
23e6fee32eeccd56bc0e07e513aa53e31042baff7bVitaly Buka        "results": {
24e6fee32eeccd56bc0e07e513aa53e31042baff7bVitaly Buka          "reply": { "type": "string" }
25d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko        }
26d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko      },
278d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko      "ping": {
288d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko        "minimalRole": "user",
298d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko        "parameters": {}
30d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko      },
318d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko      "countdown": {
32d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko        "minimalRole": "user",
33d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko        "parameters": {
348d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko          "seconds": {
35d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko            "type": "integer",
36d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko            "minimum": 1,
37d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko            "maximum": 25
38d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko          }
39d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko        }
40d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko      }
41d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko    },
42d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko    "state": {
438d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko      "pingCount": { "type": "integer" }
44d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko    }
45d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko  }
46d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko})";
47d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko
48d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenkoconst char kComponent[] = "sample";
49d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko
50d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko}  // anonymous namespace
51d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko
529eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// SampleHandler is a command handler example.
539eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// It implements the following commands:
549eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// - _hello: handle a command with an argument and set its results.
559eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// - _ping: update device state.
569eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine// - _countdown: handle long running command and report progress.
579eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosineclass SampleHandler {
589eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine public:
593fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  SampleHandler(weave::provider::TaskRunner* task_runner)
609eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      : task_runner_{task_runner} {}
613fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  void Register(weave::Device* device) {
629eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    device_ = device;
639eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
64d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko    device->AddTraitDefinitionsFromJson(kTraits);
65d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko    CHECK(device->AddComponent(kComponent, {"_sample"}, nullptr));
66d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko    CHECK(device->SetStatePropertiesFromJson(
678d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko        kComponent, R"({"_sample": {"pingCount": 0}})", nullptr));
689eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
698d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko    device->AddCommandHandler(kComponent, "_sample.hello",
709eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine                              base::Bind(&SampleHandler::OnHelloCommand,
719eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine                                         weak_ptr_factory_.GetWeakPtr()));
728d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko    device->AddCommandHandler(kComponent, "_sample.ping",
739eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine                              base::Bind(&SampleHandler::OnPingCommand,
749eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine                                         weak_ptr_factory_.GetWeakPtr()));
758d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko    device->AddCommandHandler(kComponent, "_sample.countdown",
769eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine                              base::Bind(&SampleHandler::OnCountdownCommand,
779eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine                                         weak_ptr_factory_.GetWeakPtr()));
789eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine  }
799eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
809eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine private:
813fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  void OnHelloCommand(const std::weak_ptr<weave::Command>& command) {
829eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    auto cmd = command.lock();
839eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    if (!cmd)
849eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      return;
859eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    LOG(INFO) << "received command: " << cmd->GetName();
869eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
87c4305600835b91630f9ca4b10ad9070ea55a726cVitaly Buka    const auto& params = cmd->GetParameters();
889eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    std::string name;
898d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko    if (!params.GetString("name", &name)) {
903fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine      weave::ErrorPtr error;
9148a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka      weave::Error::AddTo(&error, FROM_HERE, "invalid_parameter_value",
9248a8669ddc2e8d785aad9ad18a5abbf8f1224fdeVitaly Buka                          "Name is missing");
939eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      cmd->Abort(error.get(), nullptr);
949eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      return;
959eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    }
969eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
979eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    base::DictionaryValue result;
988d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko    result.SetString("reply", "Hello " + name);
999eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    cmd->Complete(result, nullptr);
1009eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    LOG(INFO) << cmd->GetName() << " command finished: " << result;
1019eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine  }
1029eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1033fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  void OnPingCommand(const std::weak_ptr<weave::Command>& command) {
1049eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    auto cmd = command.lock();
1059eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    if (!cmd)
1069eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      return;
1079eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    LOG(INFO) << "received command: " << cmd->GetName();
1089eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1098d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko    device_->SetStateProperty(kComponent, "_sample.pingCount",
110d6db0498402a32ef8ff5b681cef555e4c4fc2079Alex Vakulenko                              base::FundamentalValue{++ping_count_}, nullptr);
111ec9d8481ae5deaaf7901f944b06682e77b978881Alex Vakulenko    LOG(INFO) << "New component state: " << device_->GetComponents();
1129eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
113e6fee32eeccd56bc0e07e513aa53e31042baff7bVitaly Buka    cmd->Complete({}, nullptr);
1149eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
115e6fee32eeccd56bc0e07e513aa53e31042baff7bVitaly Buka    LOG(INFO) << cmd->GetName() << " command finished";
1169eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine  }
1179eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1183fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  void OnCountdownCommand(const std::weak_ptr<weave::Command>& command) {
1199eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    auto cmd = command.lock();
1209eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    if (!cmd)
1219eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      return;
1229eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    LOG(INFO) << "received command: " << cmd->GetName();
1239eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
124c4305600835b91630f9ca4b10ad9070ea55a726cVitaly Buka    const auto& params = cmd->GetParameters();
1259eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    int seconds;
1268d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko    if (!params.GetInteger("seconds", &seconds))
1279eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      seconds = 10;
1289eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1299eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    LOG(INFO) << "starting countdown";
1309eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    DoTick(cmd, seconds);
1319eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine  }
1329eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1333fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  void DoTick(const std::weak_ptr<weave::Command>& command, int seconds) {
1349eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    auto cmd = command.lock();
1359eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    if (!cmd)
1369eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      return;
1379eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1389eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    if (seconds > 0) {
1399eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      LOG(INFO) << "countdown tick: " << seconds << " seconds left";
1409eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      base::DictionaryValue progress;
1418d0cfefae24985025a934ea5461a51472c59cfc0Alex Vakulenko      progress.SetInteger("seconds_left", seconds);
1429eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      cmd->SetProgress(progress, nullptr);
1439eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      task_runner_->PostDelayedTask(
1449eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine          FROM_HERE,
1459eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine          base::Bind(&SampleHandler::DoTick, weak_ptr_factory_.GetWeakPtr(),
1469eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine                     command, --seconds),
1479eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine          base::TimeDelta::FromSeconds(1));
1489eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine      return;
1499eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    }
1509eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
151e6fee32eeccd56bc0e07e513aa53e31042baff7bVitaly Buka    cmd->Complete({}, nullptr);
1529eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine    LOG(INFO) << "countdown finished";
153e6fee32eeccd56bc0e07e513aa53e31042baff7bVitaly Buka    LOG(INFO) << cmd->GetName() << " command finished";
1549eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine  }
1559eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1563fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  weave::Device* device_{nullptr};
1573fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  weave::provider::TaskRunner* task_runner_{nullptr};
1589eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1599eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine  int ping_count_{0};
1609eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine  base::WeakPtrFactory<SampleHandler> weak_ptr_factory_{this};
1619eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine};
1629eaad2c113edde4f674d8d12d0308caa408670cfJohan Euphrosine
1633fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosineint main(int argc, char** argv) {
1643fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  Daemon::Options opts;
1653fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  if (!opts.Parse(argc, argv)) {
1663fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine    Daemon::Options::ShowUsage(argv[0]);
1673fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine    return 1;
1683fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  }
1693fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  Daemon daemon{opts};
1703fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  SampleHandler handler{daemon.GetTaskRunner()};
1713fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  handler.Register(daemon.GetDevice());
1723fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  daemon.Run();
1733fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine  return 0;
1743fb474e64c9ed199919313321e46da3a531ecc7dJohan Euphrosine}
175