Redfish: Set the power cap

Set the PowerCap with redfish patch.

Tested:
Case 1: PowerCapEnable is false
$ curl -k -H "X-Auth-Token: $token" -X PUT -d '{"data":false}' https://$bmc/xyz/openbmc_project/control/host0/power_cap/attr/PowerCapEnable
$ curl -k -H "X-Auth-Token: $token"https://${bmc}/redfish/v1/Chassis/chassis/Power
{
  "@odata.context": "/redfish/v1/$metadata#Power.Power",
  "@odata.id": "/redfish/v1/Chassis/chassis/Power",
  "@odata.type": "#Power.v1_5_2.Power",
  "Id": "Power",
  "Name": "Power",
  "PowerControl": [
    {
      "@odata.id": "/redfish/v1/Chassis/chassis/Power#/PowerControl/0",
      "@odata.type": "#Power.v1_0_0.PowerControl",
      "MemberId": "0",
      "Name": "Chassis Power Control",
      "PowerLimit": {
        "LimitInWatts": null
      }
    }
  ],
  ...
}
$curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassis/Power -X PATCH -d '{"PowerControl":[{"PowerLimit":{"LimitInWatts":2004}}]}'
{
  "error": {
    "@Message.ExtendedInfo": [
      {
        "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
        "Message": "PowerCapEnable is false, can't set the PowerCap.",
        "MessageArgs": [],
        "MessageId": "Base.1.4.0.UnableToSetPowerCap",
        "Resolution": "Set PowerCapEnable to be true before setting PowerCap.",
        "Severity": "Warning"
      }
    ],
    "code": "Base.1.4.0.UnableToSetPowerCap",
    "message": "PowerCapEnable is false, can't set the PowerCap."
  }
}

Case 2: PowerCapEnable is true, PowerControl json only
$ curl -k -H "X-Auth-Token: $token" -X PUT -d '{"data":true}' https://$bmc/xyz/openbmc_project/control/host0/power_cap/attr/PowerCapEnable
$ curl -k -H "X-Auth-Token: $token"https://${bmc}/redfish/v1/Chassis/chassis/Power
{
  "@odata.context": "/redfish/v1/$metadata#Power.Power",
  "@odata.id": "/redfish/v1/Chassis/chassis/Power",
  "@odata.type": "#Power.v1_5_2.Power",
  "Id": "Power",
  "Name": "Power",
  "PowerControl": [
    {
      "@odata.id": "/redfish/v1/Chassis/chassis/Power#/PowerControl/0",
      "@odata.type": "#Power.v1_0_0.PowerControl",
      "MemberId": "0",
      "Name": "Chassis Power Control",
      "PowerLimit": {
        "LimitInWatts": 2001.0
      }
    }
  ],
  ...
}
$ curl -k -H "X-Auth-Token: $token"https://${bmc}/redfish/v1/Chassis/chassis/Power -X PATCH -d '{"PowerControl":[{"PowerLimit":{"LimitInWatts":2004}}]}' -v
...
< HTTP/1.1 204 No Content
...
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassis/Power
{
  "@odata.context": "/redfish/v1/$metadata#Power.Power",
  "@odata.id": "/redfish/v1/Chassis/chassis/Power",
  "@odata.type": "#Power.v1_5_2.Power",
  "Id": "Power",
  "Name": "Power",
  "PowerControl": [
    {
      "@odata.id": "/redfish/v1/Chassis/chassis/Power#/PowerControl/0",
      "@odata.type": "#Power.v1_0_0.PowerControl",
      "MemberId": "0",
      "Name": "Chassis Power Control",
      "PowerLimit": {
        "LimitInWatts": 2004.0
      }
    }
  ],
  ...
}

Case 3: PowerCapEnable is true, PowerControl and Voltages json
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassis/Power -X PATCH -d '{"PowerControl":[{"PowerLimit"{"LimitInWatts":2001}}], "Voltages": [{"MemberId" : "p0_vcs_voltage", "ReadingVolts":8}]}' -v
...
< HTTP/1.1 204 No Content
...

Case 4: Wrong chassis path
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassi/Power -X PATCH -d '{"PowerControl":[{"PowerLimit":{"LimitInWatts":2001}}]}'
{
  "error": {
    "@Message.ExtendedInfo": [
      {
        "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
        "Message": "The requested resource of type Chassis named chassi was not found.",
        "MessageArgs": [
          "Chassis",
          "chassi"
        ],
        "MessageId": "Base.1.4.0.ResourceNotFound",
        "Resolution": "Provide a valid resource identifier and resubmit the request.",
        "Severity": "Critical"
      }
    ],
    "code": "Base.1.4.0.ResourceNotFound",
    "message": "The requested resource of type Chassis named chassi was not found."
  }
}

Signed-off-by: Carol Wang <wangkair@cn.ibm.com>
Change-Id: Ifabdf053005b31cf3e3539009a1ec20ce4d46d5b
diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp
index e39aa3e..3ab9e44 100644
--- a/redfish-core/lib/thermal.hpp
+++ b/redfish-core/lib/thermal.hpp
@@ -60,7 +60,45 @@
     void doPatch(crow::Response& res, const crow::Request& req,
                  const std::vector<std::string>& params) override
     {
-        setSensorOverride(res, req, params, typeList, "Thermal");
+        if (params.size() != 1)
+        {
+            res.end();
+            messages::internalError(res);
+            return;
+        }
+
+        const std::string& chassisName = params[0];
+        std::optional<std::vector<nlohmann::json>> temperatureCollections;
+        std::optional<std::vector<nlohmann::json>> fanCollections;
+        std::unordered_map<std::string, std::vector<nlohmann::json>>
+            allCollections;
+
+        auto asyncResp = std::make_shared<SensorsAsyncResp>(
+            res, chassisName, typeList, "Thermal");
+
+        if (!json_util::readJson(req, asyncResp->res, "Temperatures",
+                                 temperatureCollections, "Fans",
+                                 fanCollections))
+        {
+            return;
+        }
+        if (!temperatureCollections && !fanCollections)
+        {
+            messages::resourceNotFound(asyncResp->res, "Thermal",
+                                       "Temperatures / Voltages");
+            return;
+        }
+        if (temperatureCollections)
+        {
+            allCollections.emplace("Temperatures",
+                                   *std::move(temperatureCollections));
+        }
+        if (fanCollections)
+        {
+            allCollections.emplace("Fans", *std::move(fanCollections));
+        }
+
+        setSensorOverride(asyncResp, allCollections, chassisName, typeList);
     }
 };