text: /**
   * Copyright © 2021 IBM Corporation
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include "power_interface.hpp"
  
  #include "types.hpp"
  
  #include <fmt/format.h>
  
  #include <phosphor-logging/log.hpp>
  #include <sdbusplus/exception.hpp>
  #include <sdbusplus/sdbus.hpp>
  #include <sdbusplus/server.hpp>
  
  #include <string>
  #include <tuple>
  
  using namespace phosphor::logging;
  
  namespace phosphor::power::sequencer
  {
  
  PowerInterface::PowerInterface(sdbusplus::bus::bus& bus, const char* path) :
      _serverInterface(bus, path, POWER_IFACE, _vtable, this)
  {}
  
  int PowerInterface::callbackGetPgood(sd_bus* /*bus*/, const char* /*path*/,
                                       const char* /*interface*/,
                                       const char* /*property*/,
                                       sd_bus_message* msg, void* context,
                                       sd_bus_error* error)
  {
      if (msg != nullptr && context != nullptr)
      {
          try
          {
              auto pwrObj = static_cast<PowerInterface*>(context);
              int pgood = pwrObj->getPgood();
              log<level::INFO>(
                  fmt::format("callbackGetPgood: {}", pgood).c_str());
  
              sdbusplus::message::message(msg).append(pgood);
          }
          catch (const sdbusplus::exception_t& e)
          {
              return sd_bus_error_set(error, e.name(), e.description());
          }
      }
      else
      {
          // The message or context were null
          log<level::ERR>("Unable to service get pgood property callback");
          return -1;
      }
  
      return 1;
  }
  
  int PowerInterface::callbackGetPgoodTimeout(sd_bus* /*bus*/,
                                              const char* /*path*/,
                                              const char* /*interface*/,
                                              const char* /*property*/,
                                              sd_bus_message* msg, void* context,
                                              sd_bus_error* error)
  {
      if (msg != nullptr && context != nullptr)
      {
          try
          {
              auto pwrObj = static_cast<PowerInterface*>(context);
              int timeout = pwrObj->getPgoodTimeout();
              log<level::INFO>(
                  fmt::format("callbackGetPgoodTimeout: {}", timeout).c_str());
  
              sdbusplus::message::message(msg).append(timeout);
          }
          catch (const sdbusplus::exception_t& e)
          {
              return sd_bus_error_set(error, e.name(), e.description());
          }
      }
      else
      {
          // The message or context were null
          log<level::ERR>(
              "Unable to service get pgood timeout property callback");
          return -1;
      }
  
      return 1;
  }
  
  int PowerInterface::callbackGetPowerState(sd_bus_message* msg, void* context,
                                            sd_bus_error* error)
  {
      if (msg != nullptr && context != nullptr)
      {
          try
          {
              auto pwrObj = static_cast<PowerInterface*>(context);
              // Return the current power state of the GPIO, rather than the last
              // requested power state change
              int pgood = pwrObj->getPgood();
              log<level::INFO>(
                  fmt::format("callbackGetPowerState: {}", pgood).c_str());
  
              auto reply = sdbusplus::message::message(msg).new_method_return();
              reply.append(pgood);
              reply.method_return();
          }
          catch (const sdbusplus::exception_t& e)
          {
              return sd_bus_error_set(error, e.name(), e.description());
          }
      }
      else
      {
          // The message or context were null
          log<level::ERR>("Unable to service getPowerState method callback");
          return -1;
      }
  
      return 1;
  }
  
  int PowerInterface::callbackSetPgoodTimeout(sd_bus* /*bus*/,
                                              const char* /*path*/,
                                              const char* /*interface*/,
                                              const char* /*property*/,
                                              sd_bus_message* msg, void* context,
                                              sd_bus_error* error)
  {
      if (msg != nullptr && context != nullptr)
      {
          try
          {
              auto m = sdbusplus::message::message(msg);
  
              int timeout{};
              m.read(timeout);
              log<level::INFO>(
                  fmt::format("callbackSetPgoodTimeout: {}", timeout).c_str());
  
              auto pwrObj = static_cast<PowerInterface*>(context);
              pwrObj->setPgoodTimeout(timeout);
          }
          catch (const sdbusplus::exception_t& e)
          {
              return sd_bus_error_set(error, e.name(), e.description());
          }
      }
      else
      {
          // The message or context were null
          log<level::ERR>(
              "Unable to service set pgood timeout property callback");
          return -1;
      }
  
      return 1;
  }
  
  int PowerInterface::callbackGetState(sd_bus* /*bus*/, const char* /*path*/,
                                       const char* /*interface*/,
                                       const char* /*property*/,
                                       sd_bus_message* msg, void* context,
                                       sd_bus_error* error)
  {
      if (msg != nullptr && context != nullptr)
      {
          try
          {
              auto pwrObj = static_cast<PowerInterface*>(context);
              int state = pwrObj->getState();
              log<level::INFO>(
                  fmt::format("callbackGetState: {}", state).c_str());
  
              sdbusplus::message::message(msg).append(state);
          }
          catch (const sdbusplus::exception_t& e)
          {
              return sd_bus_error_set(error, e.name(), e.description());
          }
      }
      else
      {
          // The message or context were null
          log<level::ERR>("Unable to service get state property callback");
          return -1;
      }
  
      return 1;
  }
  
  int PowerInterface::callbackSetPowerState(sd_bus_message* msg, void* context,
                                            sd_bus_error* error)
  {
      if (msg != nullptr && context != nullptr)
      {
          try
          {
              auto m = sdbusplus::message::message(msg);
  
              int state{};
              m.read(state);
  
              if (state != 1 && state != 0)
              {
                  return sd_bus_error_set(error,
                                          "org.openbmc.ControlPower.Error.Failed",
                                          "Invalid power state");
              }
              log<level::INFO>(
                  fmt::format("callbackSetPowerState: {}", state).c_str());
  
              auto pwrObj = static_cast<PowerInterface*>(context);
              pwrObj->setState(state);
  
              m.new_method_return().method_return();
          }
          catch (const sdbusplus::exception_t& e)
          {
              return sd_bus_error_set(error, e.name(), e.description());
          }
      }
      else
      {
          // The message or context were null
          log<level::ERR>("Unable to service setPowerState method callback");
          return -1;
      }
  
      return 1;
  }
  
  int PowerInterface::callbackSetPowerSupplyError(sd_bus_message* msg,
                                                  void* context,
                                                  sd_bus_error* error)
  {
      if (msg != nullptr && context != nullptr)
      {
          try
          {
              auto m = sdbusplus::message::message(msg);
  
              std::string psError{};
              m.read(psError);
              log<level::INFO>(
                  fmt::format("callbackSetPowerSupplyError: {}", psError)
                      .c_str());
  
              auto pwrObj = static_cast<PowerInterface*>(context);
              pwrObj->setPowerSupplyError(psError);
  
              m.new_method_return().method_return();
          }
          catch (const sdbusplus::exception_t& e)
          {
              return sd_bus_error_set(error, e.name(), e.description());
          }
      }
      else
      {
          // The message or context were null
          log<level::ERR>(
              "Unable to service setPowerSupplyError method callback");
          return -1;
      }
  
      return 1;
  }
  
  void PowerInterface::emitPowerGoodSignal()
  {
      log<level::INFO>("emitPowerGoodSignal");
      _serverInterface.new_signal("PowerGood").signal_send();
  }
  
  void PowerInterface::emitPowerLostSignal()
  {
      log<level::INFO>("emitPowerLostSignal");
      _serverInterface.new_signal("PowerLost").signal_send();
  }
  
  void PowerInterface::emitPropertyChangedSignal(const char* property)
  {
      log<level::INFO>(
          fmt::format("emitPropertyChangedSignal: {}", property).c_str());
      _serverInterface.property_changed(property);
  }
  
  const sdbusplus::vtable::vtable_t PowerInterface::_vtable[] = {
      sdbusplus::vtable::start(),
      // Method setPowerState takes an int parameter and returns void
      sdbusplus::vtable::method("setPowerState", "i", "", callbackSetPowerState),
      // Method getPowerState takes no parameters and returns int
      sdbusplus::vtable::method("getPowerState", "", "i", callbackGetPowerState),
      // Signal PowerGood
      sdbusplus::vtable::signal("PowerGood", ""),
      // Signal PowerLost
      sdbusplus::vtable::signal("PowerLost", ""),
      // Property pgood is type int, read only, and uses the emits_change flag
      sdbusplus::vtable::property("pgood", "i", callbackGetPgood,
                                  sdbusplus::vtable::property_::emits_change),
      // Property state is type int, read only, and uses the emits_change flag
      sdbusplus::vtable::property("state", "i", callbackGetState,
                                  sdbusplus::vtable::property_::emits_change),
      // Property pgood_timeout is type int, read write, and uses the emits_change
      // flag
      sdbusplus::vtable::property("pgood_timeout", "i", callbackGetPgoodTimeout,
                                  callbackSetPgoodTimeout,
                                  sdbusplus::vtable::property_::emits_change),
      // Method setPowerSupplyError takes a string parameter and returns void
      sdbusplus::vtable::method("setPowerSupplyError", "s", "",
                                callbackSetPowerSupplyError),
      sdbusplus::vtable::end()};
  
  } // namespace phosphor::power::sequencer
  