monitor: Support target_path option in configuration file

The target path containing the interface
xyz.openbmc_project.control.FanPwm or
xyz.openbmc_project.control.FanSpeed can be different between
phosphor-hwmon and dbus-sensor.

In phosphor-hwmon, it is "/xyz/openbmc_project/sensors/fan_tach/".
In dbus-sensor, it is "/xyz/openbmc_project/control/fanpwm/".

This commit supports making this path configurable via "target_path"
as a full object path.
By default, it is the same as the fan tach object path.

Signed-off-by: Chau Ly <chaul@amperecomputing.com>
Change-Id: I2658a462dd49a98ad77d684f2927e6ccae21cd15
diff --git a/docs/monitor/sensors.md b/docs/monitor/sensors.md
index 99c6a75..b6555ee 100644
--- a/docs/monitor/sensors.md
+++ b/docs/monitor/sensors.md
@@ -17,6 +17,8 @@
   * Whether this sensor D-Bus object contains the `Target` property or not.
 * `target_interface` - string (Optional)
   * The D-Bus interface containing the `Target` property. This defaults to `xyz.openbmc_project.Control.FanSpeed` for RPM controlled fans or can be set to `xyz.openbmc_project.Control.FanPwm` for PWM controlled fans.
+* `target_path` - string (Optional)
+  * The D-Bus full object path containing the `Target` property. This defaults to `/xyz/openbmc_project/sensors/fan_tach`+`name`.
 * `factor` - double (Optional)
   * A value to multiply the current target by to adjust the monitoring of this sensor due to how the hardware works. This sensor attribute is optional and defaults to 1.0.
 * `offset` - integer (Optional)
@@ -39,7 +41,8 @@
         {
           "name": "fan0_0",
           "has_target": true,
-          "target_interface": "xyz.openbmc_project.Control.FanPwm"
+          "target_interface": "xyz.openbmc_project.Control.FanPwm",
+          "target_path": "/xyz/openbmc_project/control/fanpwm/PWM0"
         },
         {
           "name": "fan0_1",
diff --git a/monitor/example/monitor.yaml b/monitor/example/monitor.yaml
index 8d70c4f..14fce2c 100644
--- a/monitor/example/monitor.yaml
+++ b/monitor/example/monitor.yaml
@@ -28,6 +28,10 @@
 #                  setting a fan speed (otherwise just for reads)]
 #      target_interface [The fan target interface used by the sensor.
 #                        Default is "xyz.openbmc_project.Control.FanSpeed"]
+#      target_path [The full target path, which implements
+#                   xyz.openbmc_project.Control.FanSpeed or
+#                   xyz.openbmc_project.Control.FanPwm interface.
+#                   Default is /xyz/openbmc_project/sensors/fan_tach/*"]
 #      factor [The factor to multiply with target to calculate the expected
 #              fan speed. Default is 1 for fan speed target;
 #              Customized value for pwm target]
diff --git a/monitor/fan.cpp b/monitor/fan.cpp
index 910d975..94bfcf8 100644
--- a/monitor/fan.cpp
+++ b/monitor/fan.cpp
@@ -69,10 +69,10 @@
         _sensors.emplace_back(std::make_shared<TachSensor>(
             mode, bus, *this, std::get<sensorNameField>(s),
             std::get<hasTargetField>(s), std::get<funcDelay>(def),
-            std::get<targetInterfaceField>(s), std::get<factorField>(s),
-            std::get<offsetField>(s), std::get<methodField>(def),
-            std::get<thresholdField>(s), std::get<ignoreAboveMaxField>(s),
-            std::get<timeoutField>(def),
+            std::get<targetInterfaceField>(s), std::get<targetPathField>(s),
+            std::get<factorField>(s), std::get<offsetField>(s),
+            std::get<methodField>(def), std::get<thresholdField>(s),
+            std::get<ignoreAboveMaxField>(s), std::get<timeoutField>(def),
             std::get<nonfuncRotorErrDelayField>(def),
             std::get<countIntervalField>(def), event));
 
diff --git a/monitor/gen-fan-monitor-defs.py b/monitor/gen-fan-monitor-defs.py
index c771109..1118e83 100755
--- a/monitor/gen-fan-monitor-defs.py
+++ b/monitor/gen-fan-monitor-defs.py
@@ -67,6 +67,9 @@
                       target_interface = sensor.get(
                           'target_interface',
                           'xyz.openbmc_project.Control.FanSpeed')
+                      target_path = sensor.get(
+                          'target_path',
+                          '')
                       factor = sensor.get('factor', 1)
                       offset = sensor.get('offset', 0)
                       threshold = sensor.get('threshold', 1)
@@ -76,6 +79,7 @@
                       SensorDefinition{"${sensor['name']}",
                                        ${has_target},
                                        "${target_interface}",
+                                       "${target_path}",
                                        ${factor},
                                        ${offset},
                                        ${threshold},
diff --git a/monitor/json_parser.cpp b/monitor/json_parser.cpp
index 1144998..e3d23d9 100644
--- a/monitor/json_parser.cpp
+++ b/monitor/json_parser.cpp
@@ -146,6 +146,12 @@
         {
             targetIntf = sensor["target_interface"].get<std::string>();
         }
+        // Target path is optional
+        std::string targetPath;
+        if (sensor.contains("target_path"))
+        {
+            targetPath = sensor["target_path"].get<std::string>();
+        }
         // Factor is optional and defaults to 1
         auto factor = 1.0;
         if (sensor.contains("factor"))
@@ -173,7 +179,7 @@
 
         sensorDefs.emplace_back(std::tuple(
             sensor["name"].get<std::string>(), sensor["has_target"].get<bool>(),
-            targetIntf, factor, offset, threshold, ignoreAboveMax));
+            targetIntf, targetPath, factor, offset, threshold, ignoreAboveMax));
     }
 
     return sensorDefs;
diff --git a/monitor/tach_sensor.cpp b/monitor/tach_sensor.cpp
index 825faac..f5a7238 100644
--- a/monitor/tach_sensor.cpp
+++ b/monitor/tach_sensor.cpp
@@ -73,14 +73,14 @@
 TachSensor::TachSensor([[maybe_unused]] Mode mode, sdbusplus::bus_t& bus,
                        Fan& fan, const std::string& id, bool hasTarget,
                        size_t funcDelay, const std::string& interface,
-                       double factor, int64_t offset, size_t method,
-                       size_t threshold, bool ignoreAboveMax, size_t timeout,
-                       const std::optional<size_t>& errorDelay,
+                       const std::string& path, double factor, int64_t offset,
+                       size_t method, size_t threshold, bool ignoreAboveMax,
+                       size_t timeout, const std::optional<size_t>& errorDelay,
                        size_t countInterval, const sdeventplus::Event& event) :
     _bus(bus),
     _fan(fan), _name(FAN_SENSOR_PATH + id),
     _invName(fs::path(fan.getName()) / id), _hasTarget(hasTarget),
-    _funcDelay(funcDelay), _interface(interface), _factor(factor),
+    _funcDelay(funcDelay), _interface(interface), _path(path), _factor(factor),
     _offset(offset), _method(method), _threshold(threshold),
     _ignoreAboveMax(ignoreAboveMax), _timeout(timeout),
     _timerMode(TimerMode::func),
@@ -111,7 +111,7 @@
             // object can be functional with a missing D-bus sensor.
         }
 
-        auto match = getMatchString(util::FAN_SENSOR_VALUE_INTF);
+        auto match = getMatchString(std::nullopt, util::FAN_SENSOR_VALUE_INTF);
 
         tachSignal = std::make_unique<sdbusplus::bus::match_t>(
             _bus, match.c_str(),
@@ -119,8 +119,14 @@
 
         if (_hasTarget)
         {
-            match = getMatchString(_interface);
-
+            if (_path.empty())
+            {
+                match = getMatchString(std::nullopt, _interface);
+            }
+            else
+            {
+                match = getMatchString(_path, _interface);
+            }
             targetSignal = std::make_unique<sdbusplus::bus::match_t>(
                 _bus, match.c_str(),
                 [this](auto& msg) { this->handleTargetChange(msg); });
@@ -153,7 +159,17 @@
 
     if (_hasTarget)
     {
-        readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, _tachTarget);
+        if (_path.empty())
+        {
+            // Target path is optional
+            readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus,
+                         _tachTarget);
+        }
+        else
+        {
+            readProperty(_interface, FAN_TARGET_PROPERTY, _path, _bus,
+                         _tachTarget);
+        }
 
         // record previous target value
         if (_prevTargets.front() != _tachTarget)
@@ -170,8 +186,14 @@
     _prevTachs.pop_back();
 }
 
-std::string TachSensor::getMatchString(const std::string& interface)
+std::string TachSensor::getMatchString(const std::optional<std::string> path,
+                                       const std::string& interface)
 {
+    if (path)
+    {
+        return sdbusplus::bus::match::rules::propertiesChanged(path.value(),
+                                                               interface);
+    }
     return sdbusplus::bus::match::rules::propertiesChanged(_name, interface);
 }
 
diff --git a/monitor/tach_sensor.hpp b/monitor/tach_sensor.hpp
index 30e9129..875c578 100644
--- a/monitor/tach_sensor.hpp
+++ b/monitor/tach_sensor.hpp
@@ -93,6 +93,7 @@
      *                        setting the speed
      * @param[in] funcDelay - Delay to mark functional
      * @param[in] interface - the interface of the target
+     * @param[in] path - the object path of the sensor target
      * @param[in] factor - the factor of the sensor target
      * @param[in] offset - the offset of the sensor target
      * @param[in] method - the method of out of range
@@ -107,10 +108,11 @@
      */
     TachSensor(Mode mode, sdbusplus::bus_t& bus, Fan& fan,
                const std::string& id, bool hasTarget, size_t funcDelay,
-               const std::string& interface, double factor, int64_t offset,
-               size_t method, size_t threshold, bool ignoreAboveMax,
-               size_t timeout, const std::optional<size_t>& errorDelay,
-               size_t countInterval, const sdeventplus::Event& event);
+               const std::string& interface, const std::string& path,
+               double factor, int64_t offset, size_t method, size_t threshold,
+               bool ignoreAboveMax, size_t timeout,
+               const std::optional<size_t>& errorDelay, size_t countInterval,
+               const sdeventplus::Event& event);
 
     /**
      * @brief Reads a property from the input message and stores it in value.
@@ -386,7 +388,8 @@
      * @brief Returns the match string to use for matching
      *        on a properties changed signal.
      */
-    std::string getMatchString(const std::string& interface);
+    std::string getMatchString(const std::optional<std::string> path,
+                               const std::string& interface);
 
     /**
      * @brief Reads the Target property and stores in _tachTarget.
@@ -462,6 +465,11 @@
     const std::string _interface;
 
     /**
+     * @brief The object path to set sensor's target
+     */
+    const std::string _path;
+
+    /**
      * @brief The factor of target to get fan rpm
      */
     const double _factor;
diff --git a/monitor/types.hpp b/monitor/types.hpp
index 9c0d57f..740f069 100644
--- a/monitor/types.hpp
+++ b/monitor/types.hpp
@@ -105,13 +105,14 @@
 constexpr auto sensorNameField = 0;
 constexpr auto hasTargetField = 1;
 constexpr auto targetInterfaceField = 2;
-constexpr auto factorField = 3;
-constexpr auto offsetField = 4;
-constexpr auto thresholdField = 5;
-constexpr auto ignoreAboveMaxField = 6;
+constexpr auto targetPathField = 3;
+constexpr auto factorField = 4;
+constexpr auto offsetField = 5;
+constexpr auto thresholdField = 6;
+constexpr auto ignoreAboveMaxField = 7;
 
-using SensorDefinition =
-    std::tuple<std::string, bool, std::string, double, int64_t, size_t, bool>;
+using SensorDefinition = std::tuple<std::string, bool, std::string, std::string,
+                                    double, int64_t, size_t, bool>;
 
 constexpr auto fanNameField = 0;
 constexpr auto methodField = 1;