Break out post codes

Similar to other patches, break out the post codes log services into
their own file.  log_services.hpp is far too large.

Change-Id: I3cb644d52a9d3b5f9a15a2f90c1b69c87491e5c8
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/meson.build b/meson.build
index 4984d1a..f50212c 100644
--- a/meson.build
+++ b/meson.build
@@ -445,12 +445,13 @@
     'test/redfish-core/include/utils/time_utils_test.cpp',
     'test/redfish-core/lib/chassis_test.cpp',
     'test/redfish-core/lib/log_services_dump_test.cpp',
-    'test/redfish-core/lib/log_services_test.cpp',
     'test/redfish-core/lib/manager_diagnostic_data_test.cpp',
+    'test/redfish-core/lib/manager_logservices_journal_test.cpp',
     'test/redfish-core/lib/metadata_test.cpp',
     'test/redfish-core/lib/power_subsystem_test.cpp',
     'test/redfish-core/lib/service_root_test.cpp',
     'test/redfish-core/lib/system_test.cpp',
+    'test/redfish-core/lib/systems_logservices_postcode.cpp',
     'test/redfish-core/lib/thermal_subsystem_test.cpp',
     'test/redfish-core/lib/update_service_test.cpp',
 )
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 1fb88ea..46ceae1 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -3468,607 +3468,4 @@
             });
 }
 
-/****************************************************
- * Redfish PostCode interfaces
- * using DBUS interface: getPostCodesTS
- ******************************************************/
-inline void requestRoutesPostCodesLogService(App& app)
-{
-    BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/")
-        .privileges(redfish::privileges::getLogService)
-        .methods(
-            boost::beast::http::verb::
-                get)([&app](const crow::Request& req,
-                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                            const std::string& systemName) {
-            if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-            {
-                return;
-            }
-            if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-            {
-                // Option currently returns no systems.  TBD
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-            if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-            {
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-            asyncResp->res.jsonValue["@odata.id"] =
-                std::format("/redfish/v1/Systems/{}/LogServices/PostCodes",
-                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
-            asyncResp->res.jsonValue["@odata.type"] =
-                "#LogService.v1_2_0.LogService";
-            asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
-            asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
-            asyncResp->res.jsonValue["Id"] = "PostCodes";
-            asyncResp->res.jsonValue["OverWritePolicy"] =
-                log_service::OverWritePolicy::WrapsWhenFull;
-            asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
-                "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries",
-                BMCWEB_REDFISH_SYSTEM_URI_NAME);
-
-            std::pair<std::string, std::string> redfishDateTimeOffset =
-                redfish::time_utils::getDateTimeOffsetNow();
-            asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
-            asyncResp->res.jsonValue["DateTimeLocalOffset"] =
-                redfishDateTimeOffset.second;
-
-            asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
-                                    ["target"] = std::format(
-                "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog",
-                BMCWEB_REDFISH_SYSTEM_URI_NAME);
-        });
-}
-
-inline void requestRoutesPostCodesClear(App& app)
-{
-    BMCWEB_ROUTE(
-        app,
-        "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/")
-        // The following privilege is incorrect;  It should be ConfigureManager
-        //.privileges(redfish::privileges::postLogService)
-        .privileges({{"ConfigureComponents"}})
-        .methods(boost::beast::http::verb::post)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& systemName) {
-                if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-                {
-                    return;
-                }
-                if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-                {
-                    // Option currently returns no systems.  TBD
-                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                               systemName);
-                    return;
-                }
-                if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-                {
-                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                               systemName);
-                    return;
-                }
-                BMCWEB_LOG_DEBUG("Do delete all postcodes entries.");
-
-                // Make call to post-code service to request clear all
-                crow::connections::systemBus->async_method_call(
-                    [asyncResp](const boost::system::error_code& ec) {
-                        if (ec)
-                        {
-                            // TODO Handle for specific error code
-                            BMCWEB_LOG_ERROR(
-                                "doClearPostCodes resp_handler got error {}",
-                                ec);
-                            asyncResp->res.result(boost::beast::http::status::
-                                                      internal_server_error);
-                            messages::internalError(asyncResp->res);
-                            return;
-                        }
-                        messages::success(asyncResp->res);
-                    },
-                    "xyz.openbmc_project.State.Boot.PostCode0",
-                    "/xyz/openbmc_project/State/Boot/PostCode0",
-                    "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
-            });
-}
-
-/**
- * @brief Parse post code ID and get the current value and index value
- *        eg: postCodeID=B1-2, currentValue=1, index=2
- *
- * @param[in]  postCodeID     Post Code ID
- * @param[out] currentValue   Current value
- * @param[out] index          Index value
- *
- * @return bool true if the parsing is successful, false the parsing fails
- */
-inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue,
-                          uint16_t& index)
-{
-    std::vector<std::string> split;
-    bmcweb::split(split, postCodeID, '-');
-    if (split.size() != 2)
-    {
-        return false;
-    }
-    std::string_view postCodeNumber = split[0];
-    if (postCodeNumber.size() < 2)
-    {
-        return false;
-    }
-    if (postCodeNumber[0] != 'B')
-    {
-        return false;
-    }
-    postCodeNumber.remove_prefix(1);
-    auto [ptrIndex, ecIndex] =
-        std::from_chars(postCodeNumber.begin(), postCodeNumber.end(), index);
-    if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc())
-    {
-        return false;
-    }
-
-    std::string_view postCodeIndex = split[1];
-
-    auto [ptrValue, ecValue] = std::from_chars(
-        postCodeIndex.begin(), postCodeIndex.end(), currentValue);
-
-    return ptrValue == postCodeIndex.end() && ecValue == std::errc();
-}
-
-static bool fillPostCodeEntry(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const boost::container::flat_map<
-        uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
-    const uint16_t bootIndex, const uint64_t codeIndex = 0,
-    const uint64_t skip = 0, const uint64_t top = 0)
-{
-    // Get the Message from the MessageRegistry
-    const registries::Message* message =
-        registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
-    if (message == nullptr)
-    {
-        BMCWEB_LOG_ERROR("Couldn't find known message?");
-        return false;
-    }
-    uint64_t currentCodeIndex = 0;
-    uint64_t firstCodeTimeUs = 0;
-    for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
-             code : postcode)
-    {
-        currentCodeIndex++;
-        std::string postcodeEntryID =
-            "B" + std::to_string(bootIndex) + "-" +
-            std::to_string(currentCodeIndex); // 1 based index in EntryID string
-
-        uint64_t usecSinceEpoch = code.first;
-        uint64_t usTimeOffset = 0;
-
-        if (1 == currentCodeIndex)
-        { // already incremented
-            firstCodeTimeUs = code.first;
-        }
-        else
-        {
-            usTimeOffset = code.first - firstCodeTimeUs;
-        }
-
-        // skip if no specific codeIndex is specified and currentCodeIndex does
-        // not fall between top and skip
-        if ((codeIndex == 0) &&
-            (currentCodeIndex <= skip || currentCodeIndex > top))
-        {
-            continue;
-        }
-
-        // skip if a specific codeIndex is specified and does not match the
-        // currentIndex
-        if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
-        {
-            // This is done for simplicity. 1st entry is needed to calculate
-            // time offset. To improve efficiency, one can get to the entry
-            // directly (possibly with flatmap's nth method)
-            continue;
-        }
-
-        // currentCodeIndex is within top and skip or equal to specified code
-        // index
-
-        // Get the Created time from the timestamp
-        std::string entryTimeStr;
-        entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch);
-
-        // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
-        std::ostringstream hexCode;
-        hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
-                << std::get<0>(code.second);
-        std::ostringstream timeOffsetStr;
-        // Set Fixed -Point Notation
-        timeOffsetStr << std::fixed;
-        // Set precision to 4 digits
-        timeOffsetStr << std::setprecision(4);
-        // Add double to stream
-        timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
-
-        std::string bootIndexStr = std::to_string(bootIndex);
-        std::string timeOffsetString = timeOffsetStr.str();
-        std::string hexCodeStr = hexCode.str();
-
-        std::array<std::string_view, 3> messageArgs = {
-            bootIndexStr, timeOffsetString, hexCodeStr};
-
-        std::string msg =
-            redfish::registries::fillMessageArgs(messageArgs, message->message);
-        if (msg.empty())
-        {
-            messages::internalError(asyncResp->res);
-            return false;
-        }
-
-        // Get Severity template from message registry
-        std::string severity;
-        if (message != nullptr)
-        {
-            severity = message->messageSeverity;
-        }
-
-        // Format entry
-        nlohmann::json::object_t bmcLogEntry;
-        bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
-        bmcLogEntry["@odata.id"] = boost::urls::format(
-            "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}",
-            BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID);
-        bmcLogEntry["Name"] = "POST Code Log Entry";
-        bmcLogEntry["Id"] = postcodeEntryID;
-        bmcLogEntry["Message"] = std::move(msg);
-        bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
-        bmcLogEntry["MessageArgs"] = messageArgs;
-        bmcLogEntry["EntryType"] = "Event";
-        bmcLogEntry["Severity"] = std::move(severity);
-        bmcLogEntry["Created"] = entryTimeStr;
-        if (!std::get<std::vector<uint8_t>>(code.second).empty())
-        {
-            bmcLogEntry["AdditionalDataURI"] =
-                std::format(
-                    "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/",
-                    BMCWEB_REDFISH_SYSTEM_URI_NAME) +
-                postcodeEntryID + "/attachment";
-        }
-
-        // codeIndex is only specified when querying single entry, return only
-        // that entry in this case
-        if (codeIndex != 0)
-        {
-            asyncResp->res.jsonValue.update(bmcLogEntry);
-            return true;
-        }
-
-        nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
-        logEntryArray.emplace_back(std::move(bmcLogEntry));
-    }
-
-    // Return value is always false when querying multiple entries
-    return false;
-}
-
-static void
-    getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                        const std::string& entryId)
-{
-    uint16_t bootIndex = 0;
-    uint64_t codeIndex = 0;
-    if (!parsePostCode(entryId, codeIndex, bootIndex))
-    {
-        // Requested ID was not found
-        messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
-        return;
-    }
-
-    if (bootIndex == 0 || codeIndex == 0)
-    {
-        // 0 is an invalid index
-        messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
-        return;
-    }
-
-    crow::connections::systemBus->async_method_call(
-        [asyncResp, entryId, bootIndex,
-         codeIndex](const boost::system::error_code& ec,
-                    const boost::container::flat_map<
-                        uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
-                        postcode) {
-            if (ec)
-            {
-                BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            if (postcode.empty())
-            {
-                messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
-                return;
-            }
-
-            if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex))
-            {
-                messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
-                return;
-            }
-        },
-        "xyz.openbmc_project.State.Boot.PostCode0",
-        "/xyz/openbmc_project/State/Boot/PostCode0",
-        "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
-        bootIndex);
-}
-
-static void
-    getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                       const uint16_t bootIndex, const uint16_t bootCount,
-                       const uint64_t entryCount, size_t skip, size_t top)
-{
-    crow::connections::systemBus->async_method_call(
-        [asyncResp, bootIndex, bootCount, entryCount, skip,
-         top](const boost::system::error_code& ec,
-              const boost::container::flat_map<
-                  uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
-                  postcode) {
-            if (ec)
-            {
-                BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            uint64_t endCount = entryCount;
-            if (!postcode.empty())
-            {
-                endCount = entryCount + postcode.size();
-                if (skip < endCount && (top + skip) > entryCount)
-                {
-                    uint64_t thisBootSkip =
-                        std::max(static_cast<uint64_t>(skip), entryCount) -
-                        entryCount;
-                    uint64_t thisBootTop =
-                        std::min(static_cast<uint64_t>(top + skip), endCount) -
-                        entryCount;
-
-                    fillPostCodeEntry(asyncResp, postcode, bootIndex, 0,
-                                      thisBootSkip, thisBootTop);
-                }
-                asyncResp->res.jsonValue["Members@odata.count"] = endCount;
-            }
-
-            // continue to previous bootIndex
-            if (bootIndex < bootCount)
-            {
-                getPostCodeForBoot(asyncResp,
-                                   static_cast<uint16_t>(bootIndex + 1),
-                                   bootCount, endCount, skip, top);
-            }
-            else if (skip + top < endCount)
-            {
-                asyncResp->res.jsonValue["Members@odata.nextLink"] =
-                    std::format(
-                        "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=",
-                        BMCWEB_REDFISH_SYSTEM_URI_NAME) +
-                    std::to_string(skip + top);
-            }
-        },
-        "xyz.openbmc_project.State.Boot.PostCode0",
-        "/xyz/openbmc_project/State/Boot/PostCode0",
-        "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
-        bootIndex);
-}
-
-static void
-    getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                         size_t skip, size_t top)
-{
-    uint64_t entryCount = 0;
-    sdbusplus::asio::getProperty<uint16_t>(
-        *crow::connections::systemBus,
-        "xyz.openbmc_project.State.Boot.PostCode0",
-        "/xyz/openbmc_project/State/Boot/PostCode0",
-        "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
-        [asyncResp, entryCount, skip,
-         top](const boost::system::error_code& ec, const uint16_t bootCount) {
-            if (ec)
-            {
-                BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
-                messages::internalError(asyncResp->res);
-                return;
-            }
-            getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top);
-        });
-}
-
-inline void requestRoutesPostCodesEntryCollection(App& app)
-{
-    BMCWEB_ROUTE(app,
-                 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/")
-        .privileges(redfish::privileges::getLogEntryCollection)
-        .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& systemName) {
-                query_param::QueryCapabilities capabilities = {
-                    .canDelegateTop = true,
-                    .canDelegateSkip = true,
-                };
-                query_param::Query delegatedQuery;
-                if (!redfish::setUpRedfishRouteWithDelegation(
-                        app, req, asyncResp, delegatedQuery, capabilities))
-                {
-                    return;
-                }
-                if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-                {
-                    // Option currently returns no systems.  TBD
-                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                               systemName);
-                    return;
-                }
-
-                if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-                {
-                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                               systemName);
-                    return;
-                }
-                asyncResp->res.jsonValue["@odata.type"] =
-                    "#LogEntryCollection.LogEntryCollection";
-                asyncResp->res.jsonValue["@odata.id"] = std::format(
-                    "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries",
-                    BMCWEB_REDFISH_SYSTEM_URI_NAME);
-                asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
-                asyncResp->res.jsonValue["Description"] =
-                    "Collection of POST Code Log Entries";
-                asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
-                asyncResp->res.jsonValue["Members@odata.count"] = 0;
-                size_t skip = delegatedQuery.skip.value_or(0);
-                size_t top =
-                    delegatedQuery.top.value_or(query_param::Query::maxTop);
-                getCurrentBootNumber(asyncResp, skip, top);
-            });
-}
-
-inline void requestRoutesPostCodesEntryAdditionalData(App& app)
-{
-    BMCWEB_ROUTE(
-        app,
-        "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/")
-        .privileges(redfish::privileges::getLogEntry)
-        .methods(
-            boost::beast::http::verb::
-                get)([&app](const crow::Request& req,
-                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                            const std::string& systemName,
-                            const std::string& postCodeID) {
-            if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-            {
-                return;
-            }
-            if (!http_helpers::isContentTypeAllowed(
-                    req.getHeaderValue("Accept"),
-                    http_helpers::ContentType::OctetStream, true))
-            {
-                asyncResp->res.result(boost::beast::http::status::bad_request);
-                return;
-            }
-            if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-            {
-                // Option currently returns no systems.  TBD
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-            if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-            {
-                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                           systemName);
-                return;
-            }
-
-            uint64_t currentValue = 0;
-            uint16_t index = 0;
-            if (!parsePostCode(postCodeID, currentValue, index))
-            {
-                messages::resourceNotFound(asyncResp->res, "LogEntry",
-                                           postCodeID);
-                return;
-            }
-
-            crow::connections::systemBus->async_method_call(
-                [asyncResp, postCodeID, currentValue](
-                    const boost::system::error_code& ec,
-                    const std::vector<std::tuple<
-                        uint64_t, std::vector<uint8_t>>>& postcodes) {
-                    if (ec.value() == EBADR)
-                    {
-                        messages::resourceNotFound(asyncResp->res, "LogEntry",
-                                                   postCodeID);
-                        return;
-                    }
-                    if (ec)
-                    {
-                        BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-
-                    size_t value = static_cast<size_t>(currentValue) - 1;
-                    if (value == std::string::npos ||
-                        postcodes.size() < currentValue)
-                    {
-                        BMCWEB_LOG_WARNING("Wrong currentValue value");
-                        messages::resourceNotFound(asyncResp->res, "LogEntry",
-                                                   postCodeID);
-                        return;
-                    }
-
-                    const auto& [tID, c] = postcodes[value];
-                    if (c.empty())
-                    {
-                        BMCWEB_LOG_WARNING("No found post code data");
-                        messages::resourceNotFound(asyncResp->res, "LogEntry",
-                                                   postCodeID);
-                        return;
-                    }
-                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
-                    const char* d = reinterpret_cast<const char*>(c.data());
-                    std::string_view strData(d, c.size());
-
-                    asyncResp->res.addHeader(
-                        boost::beast::http::field::content_type,
-                        "application/octet-stream");
-                    asyncResp->res.addHeader(
-                        boost::beast::http::field::content_transfer_encoding,
-                        "Base64");
-                    asyncResp->res.write(crow::utility::base64encode(strData));
-                },
-                "xyz.openbmc_project.State.Boot.PostCode0",
-                "/xyz/openbmc_project/State/Boot/PostCode0",
-                "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
-                index);
-        });
-}
-
-inline void requestRoutesPostCodesEntry(App& app)
-{
-    BMCWEB_ROUTE(
-        app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/")
-        .privileges(redfish::privileges::getLogEntry)
-        .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                   const std::string& systemName, const std::string& targetID) {
-                if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-                {
-                    return;
-                }
-                if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-                {
-                    // Option currently returns no systems.  TBD
-                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                               systemName);
-                    return;
-                }
-                if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-                {
-                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                               systemName);
-                    return;
-                }
-
-                getPostCodeForEntry(asyncResp, targetID);
-            });
-}
-
 } // namespace redfish
diff --git a/redfish-core/lib/systems_logservices_postcodes.hpp b/redfish-core/lib/systems_logservices_postcodes.hpp
new file mode 100644
index 0000000..7c592ad
--- /dev/null
+++ b/redfish-core/lib/systems_logservices_postcodes.hpp
@@ -0,0 +1,629 @@
+#pragma once
+
+#include "app.hpp"
+#include "generated/enums/log_service.hpp"
+#include "query.hpp"
+#include "registries/openbmc_message_registry.hpp"
+#include "registries/privilege_registry.hpp"
+#include "utils/time_utils.hpp"
+
+#include <cstdint>
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace redfish
+{
+/****************************************************
+ * Redfish PostCode interfaces
+ * using DBUS interface: getPostCodesTS
+ ******************************************************/
+inline void requestRoutesPostCodesLogService(App& app)
+{
+    BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/")
+        .privileges(redfish::privileges::getLogService)
+        .methods(
+            boost::beast::http::verb::
+                get)([&app](const crow::Request& req,
+                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                            const std::string& systemName) {
+            if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+            {
+                return;
+            }
+            if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+            {
+                // Option currently returns no systems.  TBD
+                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                           systemName);
+                return;
+            }
+            if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+            {
+                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                           systemName);
+                return;
+            }
+            asyncResp->res.jsonValue["@odata.id"] =
+                std::format("/redfish/v1/Systems/{}/LogServices/PostCodes",
+                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
+            asyncResp->res.jsonValue["@odata.type"] =
+                "#LogService.v1_2_0.LogService";
+            asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
+            asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
+            asyncResp->res.jsonValue["Id"] = "PostCodes";
+            asyncResp->res.jsonValue["OverWritePolicy"] =
+                log_service::OverWritePolicy::WrapsWhenFull;
+            asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
+                "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries",
+                BMCWEB_REDFISH_SYSTEM_URI_NAME);
+
+            std::pair<std::string, std::string> redfishDateTimeOffset =
+                redfish::time_utils::getDateTimeOffsetNow();
+            asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
+            asyncResp->res.jsonValue["DateTimeLocalOffset"] =
+                redfishDateTimeOffset.second;
+
+            asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
+                                    ["target"] = std::format(
+                "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog",
+                BMCWEB_REDFISH_SYSTEM_URI_NAME);
+        });
+}
+
+inline void requestRoutesPostCodesClear(App& app)
+{
+    BMCWEB_ROUTE(
+        app,
+        "/redfish/v1/Systems/<str>/LogServices/PostCodes/Actions/LogService.ClearLog/")
+        // The following privilege is incorrect;  It should be ConfigureManager
+        //.privileges(redfish::privileges::postLogService)
+        .privileges({{"ConfigureComponents"}})
+        .methods(boost::beast::http::verb::post)(
+            [&app](const crow::Request& req,
+                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                   const std::string& systemName) {
+                if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+                {
+                    return;
+                }
+                if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+                {
+                    // Option currently returns no systems.  TBD
+                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                               systemName);
+                    return;
+                }
+                if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+                {
+                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                               systemName);
+                    return;
+                }
+                BMCWEB_LOG_DEBUG("Do delete all postcodes entries.");
+
+                // Make call to post-code service to request clear all
+                crow::connections::systemBus->async_method_call(
+                    [asyncResp](const boost::system::error_code& ec) {
+                        if (ec)
+                        {
+                            // TODO Handle for specific error code
+                            BMCWEB_LOG_ERROR(
+                                "doClearPostCodes resp_handler got error {}",
+                                ec);
+                            asyncResp->res.result(boost::beast::http::status::
+                                                      internal_server_error);
+                            messages::internalError(asyncResp->res);
+                            return;
+                        }
+                        messages::success(asyncResp->res);
+                    },
+                    "xyz.openbmc_project.State.Boot.PostCode0",
+                    "/xyz/openbmc_project/State/Boot/PostCode0",
+                    "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
+            });
+}
+
+/**
+ * @brief Parse post code ID and get the current value and index value
+ *        eg: postCodeID=B1-2, currentValue=1, index=2
+ *
+ * @param[in]  postCodeID     Post Code ID
+ * @param[out] currentValue   Current value
+ * @param[out] index          Index value
+ *
+ * @return bool true if the parsing is successful, false the parsing fails
+ */
+inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue,
+                          uint16_t& index)
+{
+    std::vector<std::string> split;
+    bmcweb::split(split, postCodeID, '-');
+    if (split.size() != 2)
+    {
+        return false;
+    }
+    std::string_view postCodeNumber = split[0];
+    if (postCodeNumber.size() < 2)
+    {
+        return false;
+    }
+    if (postCodeNumber[0] != 'B')
+    {
+        return false;
+    }
+    postCodeNumber.remove_prefix(1);
+    auto [ptrIndex, ecIndex] =
+        std::from_chars(postCodeNumber.begin(), postCodeNumber.end(), index);
+    if (ptrIndex != postCodeNumber.end() || ecIndex != std::errc())
+    {
+        return false;
+    }
+
+    std::string_view postCodeIndex = split[1];
+
+    auto [ptrValue, ecValue] = std::from_chars(
+        postCodeIndex.begin(), postCodeIndex.end(), currentValue);
+
+    return ptrValue == postCodeIndex.end() && ecValue == std::errc();
+}
+
+static bool fillPostCodeEntry(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const boost::container::flat_map<
+        uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
+    const uint16_t bootIndex, const uint64_t codeIndex = 0,
+    const uint64_t skip = 0, const uint64_t top = 0)
+{
+    // Get the Message from the MessageRegistry
+    const registries::Message* message =
+        registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
+    if (message == nullptr)
+    {
+        BMCWEB_LOG_ERROR("Couldn't find known message?");
+        return false;
+    }
+    uint64_t currentCodeIndex = 0;
+    uint64_t firstCodeTimeUs = 0;
+    for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
+             code : postcode)
+    {
+        currentCodeIndex++;
+        std::string postcodeEntryID =
+            "B" + std::to_string(bootIndex) + "-" +
+            std::to_string(currentCodeIndex); // 1 based index in EntryID string
+
+        uint64_t usecSinceEpoch = code.first;
+        uint64_t usTimeOffset = 0;
+
+        if (1 == currentCodeIndex)
+        { // already incremented
+            firstCodeTimeUs = code.first;
+        }
+        else
+        {
+            usTimeOffset = code.first - firstCodeTimeUs;
+        }
+
+        // skip if no specific codeIndex is specified and currentCodeIndex does
+        // not fall between top and skip
+        if ((codeIndex == 0) &&
+            (currentCodeIndex <= skip || currentCodeIndex > top))
+        {
+            continue;
+        }
+
+        // skip if a specific codeIndex is specified and does not match the
+        // currentIndex
+        if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
+        {
+            // This is done for simplicity. 1st entry is needed to calculate
+            // time offset. To improve efficiency, one can get to the entry
+            // directly (possibly with flatmap's nth method)
+            continue;
+        }
+
+        // currentCodeIndex is within top and skip or equal to specified code
+        // index
+
+        // Get the Created time from the timestamp
+        std::string entryTimeStr;
+        entryTimeStr = redfish::time_utils::getDateTimeUintUs(usecSinceEpoch);
+
+        // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
+        std::ostringstream hexCode;
+        hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
+                << std::get<0>(code.second);
+        std::ostringstream timeOffsetStr;
+        // Set Fixed -Point Notation
+        timeOffsetStr << std::fixed;
+        // Set precision to 4 digits
+        timeOffsetStr << std::setprecision(4);
+        // Add double to stream
+        timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
+
+        std::string bootIndexStr = std::to_string(bootIndex);
+        std::string timeOffsetString = timeOffsetStr.str();
+        std::string hexCodeStr = hexCode.str();
+
+        std::array<std::string_view, 3> messageArgs = {
+            bootIndexStr, timeOffsetString, hexCodeStr};
+
+        std::string msg =
+            redfish::registries::fillMessageArgs(messageArgs, message->message);
+        if (msg.empty())
+        {
+            messages::internalError(asyncResp->res);
+            return false;
+        }
+
+        // Get Severity template from message registry
+        std::string severity;
+        if (message != nullptr)
+        {
+            severity = message->messageSeverity;
+        }
+
+        // Format entry
+        nlohmann::json::object_t bmcLogEntry;
+        bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
+        bmcLogEntry["@odata.id"] = boost::urls::format(
+            "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}",
+            BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID);
+        bmcLogEntry["Name"] = "POST Code Log Entry";
+        bmcLogEntry["Id"] = postcodeEntryID;
+        bmcLogEntry["Message"] = std::move(msg);
+        bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
+        bmcLogEntry["MessageArgs"] = messageArgs;
+        bmcLogEntry["EntryType"] = "Event";
+        bmcLogEntry["Severity"] = std::move(severity);
+        bmcLogEntry["Created"] = entryTimeStr;
+        if (!std::get<std::vector<uint8_t>>(code.second).empty())
+        {
+            bmcLogEntry["AdditionalDataURI"] =
+                std::format(
+                    "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/",
+                    BMCWEB_REDFISH_SYSTEM_URI_NAME) +
+                postcodeEntryID + "/attachment";
+        }
+
+        // codeIndex is only specified when querying single entry, return only
+        // that entry in this case
+        if (codeIndex != 0)
+        {
+            asyncResp->res.jsonValue.update(bmcLogEntry);
+            return true;
+        }
+
+        nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
+        logEntryArray.emplace_back(std::move(bmcLogEntry));
+    }
+
+    // Return value is always false when querying multiple entries
+    return false;
+}
+
+static void
+    getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                        const std::string& entryId)
+{
+    uint16_t bootIndex = 0;
+    uint64_t codeIndex = 0;
+    if (!parsePostCode(entryId, codeIndex, bootIndex))
+    {
+        // Requested ID was not found
+        messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
+        return;
+    }
+
+    if (bootIndex == 0 || codeIndex == 0)
+    {
+        // 0 is an invalid index
+        messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
+        return;
+    }
+
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, entryId, bootIndex,
+         codeIndex](const boost::system::error_code& ec,
+                    const boost::container::flat_map<
+                        uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
+                        postcode) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
+                messages::internalError(asyncResp->res);
+                return;
+            }
+
+            if (postcode.empty())
+            {
+                messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
+                return;
+            }
+
+            if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex))
+            {
+                messages::resourceNotFound(asyncResp->res, "LogEntry", entryId);
+                return;
+            }
+        },
+        "xyz.openbmc_project.State.Boot.PostCode0",
+        "/xyz/openbmc_project/State/Boot/PostCode0",
+        "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
+        bootIndex);
+}
+
+static void
+    getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                       const uint16_t bootIndex, const uint16_t bootCount,
+                       const uint64_t entryCount, size_t skip, size_t top)
+{
+    crow::connections::systemBus->async_method_call(
+        [asyncResp, bootIndex, bootCount, entryCount, skip,
+         top](const boost::system::error_code& ec,
+              const boost::container::flat_map<
+                  uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
+                  postcode) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG("DBUS POST CODE PostCode response error");
+                messages::internalError(asyncResp->res);
+                return;
+            }
+
+            uint64_t endCount = entryCount;
+            if (!postcode.empty())
+            {
+                endCount = entryCount + postcode.size();
+                if (skip < endCount && (top + skip) > entryCount)
+                {
+                    uint64_t thisBootSkip =
+                        std::max(static_cast<uint64_t>(skip), entryCount) -
+                        entryCount;
+                    uint64_t thisBootTop =
+                        std::min(static_cast<uint64_t>(top + skip), endCount) -
+                        entryCount;
+
+                    fillPostCodeEntry(asyncResp, postcode, bootIndex, 0,
+                                      thisBootSkip, thisBootTop);
+                }
+                asyncResp->res.jsonValue["Members@odata.count"] = endCount;
+            }
+
+            // continue to previous bootIndex
+            if (bootIndex < bootCount)
+            {
+                getPostCodeForBoot(asyncResp,
+                                   static_cast<uint16_t>(bootIndex + 1),
+                                   bootCount, endCount, skip, top);
+            }
+            else if (skip + top < endCount)
+            {
+                asyncResp->res.jsonValue["Members@odata.nextLink"] =
+                    std::format(
+                        "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=",
+                        BMCWEB_REDFISH_SYSTEM_URI_NAME) +
+                    std::to_string(skip + top);
+            }
+        },
+        "xyz.openbmc_project.State.Boot.PostCode0",
+        "/xyz/openbmc_project/State/Boot/PostCode0",
+        "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
+        bootIndex);
+}
+
+static void
+    getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                         size_t skip, size_t top)
+{
+    uint64_t entryCount = 0;
+    sdbusplus::asio::getProperty<uint16_t>(
+        *crow::connections::systemBus,
+        "xyz.openbmc_project.State.Boot.PostCode0",
+        "/xyz/openbmc_project/State/Boot/PostCode0",
+        "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
+        [asyncResp, entryCount, skip,
+         top](const boost::system::error_code& ec, const uint16_t bootCount) {
+            if (ec)
+            {
+                BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
+                messages::internalError(asyncResp->res);
+                return;
+            }
+            getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top);
+        });
+}
+
+inline void requestRoutesPostCodesEntryCollection(App& app)
+{
+    BMCWEB_ROUTE(app,
+                 "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/")
+        .privileges(redfish::privileges::getLogEntryCollection)
+        .methods(boost::beast::http::verb::get)(
+            [&app](const crow::Request& req,
+                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                   const std::string& systemName) {
+                query_param::QueryCapabilities capabilities = {
+                    .canDelegateTop = true,
+                    .canDelegateSkip = true,
+                };
+                query_param::Query delegatedQuery;
+                if (!redfish::setUpRedfishRouteWithDelegation(
+                        app, req, asyncResp, delegatedQuery, capabilities))
+                {
+                    return;
+                }
+                if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+                {
+                    // Option currently returns no systems.  TBD
+                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                               systemName);
+                    return;
+                }
+
+                if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+                {
+                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                               systemName);
+                    return;
+                }
+                asyncResp->res.jsonValue["@odata.type"] =
+                    "#LogEntryCollection.LogEntryCollection";
+                asyncResp->res.jsonValue["@odata.id"] = std::format(
+                    "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries",
+                    BMCWEB_REDFISH_SYSTEM_URI_NAME);
+                asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
+                asyncResp->res.jsonValue["Description"] =
+                    "Collection of POST Code Log Entries";
+                asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
+                asyncResp->res.jsonValue["Members@odata.count"] = 0;
+                size_t skip = delegatedQuery.skip.value_or(0);
+                size_t top =
+                    delegatedQuery.top.value_or(query_param::Query::maxTop);
+                getCurrentBootNumber(asyncResp, skip, top);
+            });
+}
+
+inline void requestRoutesPostCodesEntryAdditionalData(App& app)
+{
+    BMCWEB_ROUTE(
+        app,
+        "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/attachment/")
+        .privileges(redfish::privileges::getLogEntry)
+        .methods(
+            boost::beast::http::verb::
+                get)([&app](const crow::Request& req,
+                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                            const std::string& systemName,
+                            const std::string& postCodeID) {
+            if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+            {
+                return;
+            }
+            if (!http_helpers::isContentTypeAllowed(
+                    req.getHeaderValue("Accept"),
+                    http_helpers::ContentType::OctetStream, true))
+            {
+                asyncResp->res.result(boost::beast::http::status::bad_request);
+                return;
+            }
+            if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+            {
+                // Option currently returns no systems.  TBD
+                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                           systemName);
+                return;
+            }
+            if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+            {
+                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                           systemName);
+                return;
+            }
+
+            uint64_t currentValue = 0;
+            uint16_t index = 0;
+            if (!parsePostCode(postCodeID, currentValue, index))
+            {
+                messages::resourceNotFound(asyncResp->res, "LogEntry",
+                                           postCodeID);
+                return;
+            }
+
+            crow::connections::systemBus->async_method_call(
+                [asyncResp, postCodeID, currentValue](
+                    const boost::system::error_code& ec,
+                    const std::vector<std::tuple<
+                        uint64_t, std::vector<uint8_t>>>& postcodes) {
+                    if (ec.value() == EBADR)
+                    {
+                        messages::resourceNotFound(asyncResp->res, "LogEntry",
+                                                   postCodeID);
+                        return;
+                    }
+                    if (ec)
+                    {
+                        BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
+                        messages::internalError(asyncResp->res);
+                        return;
+                    }
+
+                    size_t value = static_cast<size_t>(currentValue) - 1;
+                    if (value == std::string::npos ||
+                        postcodes.size() < currentValue)
+                    {
+                        BMCWEB_LOG_WARNING("Wrong currentValue value");
+                        messages::resourceNotFound(asyncResp->res, "LogEntry",
+                                                   postCodeID);
+                        return;
+                    }
+
+                    const auto& [tID, c] = postcodes[value];
+                    if (c.empty())
+                    {
+                        BMCWEB_LOG_WARNING("No found post code data");
+                        messages::resourceNotFound(asyncResp->res, "LogEntry",
+                                                   postCodeID);
+                        return;
+                    }
+                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+                    const char* d = reinterpret_cast<const char*>(c.data());
+                    std::string_view strData(d, c.size());
+
+                    asyncResp->res.addHeader(
+                        boost::beast::http::field::content_type,
+                        "application/octet-stream");
+                    asyncResp->res.addHeader(
+                        boost::beast::http::field::content_transfer_encoding,
+                        "Base64");
+                    asyncResp->res.write(crow::utility::base64encode(strData));
+                },
+                "xyz.openbmc_project.State.Boot.PostCode0",
+                "/xyz/openbmc_project/State/Boot/PostCode0",
+                "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
+                index);
+        });
+}
+
+inline void requestRoutesPostCodesEntry(App& app)
+{
+    BMCWEB_ROUTE(
+        app, "/redfish/v1/Systems/<str>/LogServices/PostCodes/Entries/<str>/")
+        .privileges(redfish::privileges::getLogEntry)
+        .methods(boost::beast::http::verb::get)(
+            [&app](const crow::Request& req,
+                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                   const std::string& systemName, const std::string& targetID) {
+                if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+                {
+                    return;
+                }
+                if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+                {
+                    // Option currently returns no systems.  TBD
+                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                               systemName);
+                    return;
+                }
+                if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+                {
+                    messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                               systemName);
+                    return;
+                }
+
+                getPostCodeForEntry(asyncResp, targetID);
+            });
+}
+
+inline void requestRoutesSystemsLogServicesPostCode(App& app)
+{
+    requestRoutesPostCodesClear(app);
+    requestRoutesPostCodesEntry(app);
+    requestRoutesPostCodesEntryAdditionalData(app);
+    requestRoutesPostCodesEntryCollection(app);
+    requestRoutesPostCodesLogService(app);
+}
+} // namespace redfish
diff --git a/redfish-core/src/redfish.cpp b/redfish-core/src/redfish.cpp
index e81691f..95f50ca 100644
--- a/redfish-core/src/redfish.cpp
+++ b/redfish-core/src/redfish.cpp
@@ -39,6 +39,7 @@
 #include "service_root.hpp"
 #include "storage.hpp"
 #include "systems.hpp"
+#include "systems_logservices_postcodes.hpp"
 #include "task.hpp"
 #include "telemetry_service.hpp"
 #include "thermal.hpp"
@@ -108,12 +109,8 @@
 
     requestRoutesSystemLogServiceCollection(app);
     requestRoutesEventLogService(app);
-    requestRoutesPostCodesEntryAdditionalData(app);
 
-    requestRoutesPostCodesLogService(app);
-    requestRoutesPostCodesClear(app);
-    requestRoutesPostCodesEntry(app);
-    requestRoutesPostCodesEntryCollection(app);
+    requestRoutesSystemsLogServicesPostCode(app);
 
     if constexpr (BMCWEB_REDFISH_DUMP_LOG)
     {
diff --git a/test/redfish-core/lib/log_services_test.cpp b/test/redfish-core/lib/manager_logservices_journal_test.cpp
similarity index 67%
rename from test/redfish-core/lib/log_services_test.cpp
rename to test/redfish-core/lib/manager_logservices_journal_test.cpp
index e413d0e..86c201c 100644
--- a/test/redfish-core/lib/log_services_test.cpp
+++ b/test/redfish-core/lib/manager_logservices_journal_test.cpp
@@ -1,5 +1,4 @@
 #include "async_resp.hpp"
-#include "log_services.hpp"
 #include "manager_logservices_journal.hpp"
 
 #include <systemd/sd-id128.h>
@@ -68,34 +67,5 @@
     EXPECT_EQ(indexOut, 1);
 }
 
-TEST(LogServicesPostCodeParse, PostCodeParse)
-{
-    uint64_t currentValue = 0;
-    uint16_t index = 0;
-    EXPECT_TRUE(parsePostCode("B1-2", currentValue, index));
-    EXPECT_EQ(currentValue, 2);
-    EXPECT_EQ(index, 1);
-    EXPECT_TRUE(parsePostCode("B200-300", currentValue, index));
-    EXPECT_EQ(currentValue, 300);
-    EXPECT_EQ(index, 200);
-
-    EXPECT_FALSE(parsePostCode("", currentValue, index));
-    EXPECT_FALSE(parsePostCode("B", currentValue, index));
-    EXPECT_FALSE(parsePostCode("B1", currentValue, index));
-    EXPECT_FALSE(parsePostCode("B1-", currentValue, index));
-    EXPECT_FALSE(parsePostCode("B1A-2", currentValue, index));
-    EXPECT_FALSE(parsePostCode("B1A-2", currentValue, index));
-    EXPECT_FALSE(parsePostCode("B1A-2z", currentValue, index));
-    // Uint16_t max + 1
-    EXPECT_FALSE(parsePostCode("B65536-1", currentValue, index));
-
-    // Uint64_t max + 1
-    EXPECT_FALSE(parsePostCode("B1-18446744073709551616", currentValue, index));
-
-    // Negative numbers
-    EXPECT_FALSE(parsePostCode("B-1-2", currentValue, index));
-    EXPECT_FALSE(parsePostCode("B-1--2", currentValue, index));
-}
-
 } // namespace
 } // namespace redfish
diff --git a/test/redfish-core/lib/systems_logservices_postcode.cpp b/test/redfish-core/lib/systems_logservices_postcode.cpp
new file mode 100644
index 0000000..aa98cb1
--- /dev/null
+++ b/test/redfish-core/lib/systems_logservices_postcode.cpp
@@ -0,0 +1,43 @@
+
+#include "systems_logservices_postcodes.hpp"
+
+#include <cstdint>
+
+#include <gtest/gtest.h>
+
+namespace redfish
+{
+namespace
+{
+
+TEST(LogServicesPostCodeParse, PostCodeParse)
+{
+    uint64_t currentValue = 0;
+    uint16_t index = 0;
+    EXPECT_TRUE(parsePostCode("B1-2", currentValue, index));
+    EXPECT_EQ(currentValue, 2);
+    EXPECT_EQ(index, 1);
+    EXPECT_TRUE(parsePostCode("B200-300", currentValue, index));
+    EXPECT_EQ(currentValue, 300);
+    EXPECT_EQ(index, 200);
+
+    EXPECT_FALSE(parsePostCode("", currentValue, index));
+    EXPECT_FALSE(parsePostCode("B", currentValue, index));
+    EXPECT_FALSE(parsePostCode("B1", currentValue, index));
+    EXPECT_FALSE(parsePostCode("B1-", currentValue, index));
+    EXPECT_FALSE(parsePostCode("B1A-2", currentValue, index));
+    EXPECT_FALSE(parsePostCode("B1A-2", currentValue, index));
+    EXPECT_FALSE(parsePostCode("B1A-2z", currentValue, index));
+    // Uint16_t max + 1
+    EXPECT_FALSE(parsePostCode("B65536-1", currentValue, index));
+
+    // Uint64_t max + 1
+    EXPECT_FALSE(parsePostCode("B1-18446744073709551616", currentValue, index));
+
+    // Negative numbers
+    EXPECT_FALSE(parsePostCode("B-1-2", currentValue, index));
+    EXPECT_FALSE(parsePostCode("B-1--2", currentValue, index));
+}
+
+} // namespace
+} // namespace redfish