/*
// Copyright (c) 2019 Intel 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 "dimmSpdReader.hpp"
#include "i3cdev.h"
#include <gpiod.hpp>
#include <phosphor-logging/lg2.hpp>
#include <stdint.h>

#include <chrono>
#include <cmath>
#include <functional>
#include <limits>
#include <memory>
#include <numeric>
#include <string>
#include <vector>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <ctime>
#include <sstream>
#include <iomanip>
#include <unistd.h>
#include <nlohmann/json.hpp>

static constexpr bool debug = false;

#define PECI_MBX_INDEX_DDR_DIMM_TEMP 0x0E
constexpr auto dimmSensorInterface = "xyz.openbmc_project.Sensor.dimmLED";
constexpr auto dimmSensorPath = "/xyz/openbmc_project/sensors/temperature/";
constexpr auto HostMiscDbusName = "xyz.openbmc_project.Host.Misc.Manager";
constexpr auto platformStatePath = "/xyz/openbmc_project/misc/platform_state";
constexpr auto platformStateInterface = "xyz.openbmc_project.State.Host.Misc";
constexpr auto postCompleteProperty = "PostComplete";
static constexpr const char* dimmInfoPath = "/var/log/dimmInfo.json";
static constexpr const char* dimmInfoTempPath = "/usr/share/dimm-spd-reader/dimmInfo_template.json";
static int lastIndex = -1;

#define NUM_TO_SPD(n) (((n) % 3) * 2)
#define NUM_TO_PMIC(n) (((n) % 3) * 2 + 8)
#define I3C_ADDR(cpuid) ("/dev/i3c-" + std::to_string(cpuid) + "-3c00000000")
#define I3C_TS0_ADDR(cpuid) ("/dev/i3c-" + std::to_string(cpuid) + "-3c00000001")
#define I3C_TS1_ADDR(cpuid) ("/dev/i3c-" + std::to_string(cpuid) + "-3c00000003")

static boost::container::flat_map<std::string, DimmData> dimmDataTable;

const boost::container::flat_map<std::string, bool> cfgCpu0DimmABC = {
    {"I3C_SPD_BMC_MUX0_SEL", false},
    {"I3C_SPD_BMC_MUX0_EN", false},
    {"I3C_SPD_BMC_MUX1_SEL", true},
    {"I3C_SPD_BMC_MUX1_EN", true}};

const boost::container::flat_map<std::string, bool> cfgCpu0DimmDEF = {
    {"I3C_SPD_BMC_MUX0_SEL", true},
    {"I3C_SPD_BMC_MUX0_EN", false},
    {"I3C_SPD_BMC_MUX1_SEL", true},
    {"I3C_SPD_BMC_MUX1_EN", true}};

const boost::container::flat_map<std::string, bool> cfgCpu0DimmGHI = {
    {"I3C_SPD_BMC_MUX0_SEL", true},
    {"I3C_SPD_BMC_MUX0_EN", true},
    {"I3C_SPD_BMC_MUX1_SEL", false},
    {"I3C_SPD_BMC_MUX1_EN", false}};

const boost::container::flat_map<std::string, bool> cfgCpu0DimmJKL = {
    {"I3C_SPD_BMC_MUX0_SEL", true},
    {"I3C_SPD_BMC_MUX0_EN", true},
    {"I3C_SPD_BMC_MUX1_SEL", true},
    {"I3C_SPD_BMC_MUX1_EN", false}};

const boost::container::flat_map<std::string, bool> cfgCpu1DimmABC = {
    {"I3C_SPD_BMC_MUX2_SEL", false},
    {"I3C_SPD_BMC_MUX2_EN", false},
    {"I3C_SPD_BMC_MUX3_SEL", true},
    {"I3C_SPD_BMC_MUX3_EN", true}};

const boost::container::flat_map<std::string, bool> cfgCpu1DimmDEF = {
    {"I3C_SPD_BMC_MUX2_SEL", true},
    {"I3C_SPD_BMC_MUX2_EN", false},
    {"I3C_SPD_BMC_MUX3_SEL", true},
    {"I3C_SPD_BMC_MUX3_EN", true}};

const boost::container::flat_map<std::string, bool> cfgCpu1DimmGHI = {
    {"I3C_SPD_BMC_MUX2_SEL", true},
    {"I3C_SPD_BMC_MUX2_EN", true},
    {"I3C_SPD_BMC_MUX3_SEL", false},
    {"I3C_SPD_BMC_MUX3_EN", false}};

const boost::container::flat_map<std::string, bool> cfgCpu1DimmJKL = {
    {"I3C_SPD_BMC_MUX2_SEL", true},
    {"I3C_SPD_BMC_MUX2_EN", true},
    {"I3C_SPD_BMC_MUX3_SEL", true},
    {"I3C_SPD_BMC_MUX3_EN", false}};


static std::vector<boost::container::flat_map<std::string, bool>> cfgGPIO = {
    cfgCpu0DimmABC,
    cfgCpu0DimmDEF,
    cfgCpu0DimmGHI,
    cfgCpu0DimmJKL,
    cfgCpu1DimmABC,
    cfgCpu1DimmDEF,
    cfgCpu1DimmGHI,
    cfgCpu1DimmJKL
};

static inline std::string to_hex(int num) {
    std::stringstream ss;
    ss << std::hex << num;
    return ss.str();
}

static inline std::string get_current_timestamp() {
    auto now = std::chrono::system_clock::now();
    std::time_t now_time = std::chrono::system_clock::to_time_t(now);

    std::tm tm = *std::localtime(&now_time);
    std::stringstream ss;
    ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");  // 格式化为 "YYYY-MM-DD HH:MM:SS"
    return ss.str();
}

static bool getDbusMsgState(sdbusplus::message_t& msg, bool& value)
{
    std::string pltStateInterface;
    std::string event;
    boost::container::flat_map<std::string, std::variant<bool>>
        propertiesChanged;
    try
    {
        msg.read(pltStateInterface, propertiesChanged);
        if (propertiesChanged.empty())
        {
            return false;
        }

        std::string property = propertiesChanged.begin()->first;

        if (property.empty() || property != "dimmI3cSwitch")
        {
            return false;
        }

        value = std::get<bool>(propertiesChanged.begin()->second);
        return true;
    }
    catch (const std::exception& e)
    {
        std::cerr << "exception while reading dbus property dimmI3cSwitch" << "\n";
        return false;
    }
}

static int getADCUpdateTime(int value) {
    static const int updateTimes[] = {1, 2, 4, 8};

    if (value >= 0 && value < 4) {
        return updateTimes[value];
    } else {
        return 8;
    }
}

template <typename T>
void updateField(const std::string& dimmName, FieldType field, T newValues[], size_t size) {
    auto it = dimmDataTable.find(dimmName);
    if (it != dimmDataTable.end()) {
        switch (field) {
            case VOLTAGE:
                if (size == STATUS_PMIC_RAIL_NUM) {
                    std::copy(newValues, newValues + size, std::begin(it->second.voltage));
                } else {
                    std::cout << "Invalid size for voltage array!" << std::endl;
                }
                break;

            case CURRENT:
                if (size == STATUS_PMIC_RAIL_NUM) {
                    std::copy(newValues, newValues + size, std::begin(it->second.current));
                } else {
                    std::cout << "Invalid size for current array!" << std::endl;
                }
                break;

             case POWER:
                if (size == STATUS_PMIC_RAIL_NUM) {
                    std::copy(newValues, newValues + size, std::begin(it->second.power));
                } else {
                    std::cout << "Invalid size for power array!" << std::endl;
                }
                break;

            case TEMP:
                if (size == STATUS_TEMP_NUM) {
                    std::copy(newValues, newValues + size, std::begin(it->second.temp));
                } else {
                    std::cout << "Invalid size for temp array!" << std::endl;
                }
                break;

            case STATUS:
                if (size == STATUS_REG_NUM) {
                    std::copy(newValues, newValues + size, std::begin(it->second.reg));
                } else {
                    std::cout << "Invalid size for status array!" << std::endl;
                }
                break;				
            case ERROR:
                if (size == STATUS_ERROR_NUM) {
                    std::copy(newValues, newValues + size, std::begin(it->second.error));
                } else {
                    std::cout << "Invalid size for error array!" << std::endl;
                }
                break;
            default:
                std::cout << "Unknown field type!" << std::endl;
                break;
        }
    } else {
        std::cout << "DIMM not found!" << std::endl;
    }
}

void DIMMSpdReader::getdimmI3cSwitchState(const std::shared_ptr<sdbusplus::asio::connection>& conn,
                   size_t retries)
{
    conn->async_method_call(
        [conn, retries, this](boost::system::error_code ec,
                        const std::variant<bool>& state) {
        if (ec)
        {
            if (retries != 0U)
            {
                auto timer = std::make_shared<boost::asio::steady_timer>(
                    conn->get_io_context());
                timer->expires_after(std::chrono::seconds(15));
                timer->async_wait(
                    [timer, conn, retries, this](boost::system::error_code) {
                    this->getdimmI3cSwitchState(conn, retries - 1);
                });
                return;
            }

            std::cerr << "error getting dimm I3cSwitchState" << ec.message() << "\n";
            return;
        }
        bool i3cSwitchState = std::get<bool>(state);
        std::cout << "i3cSwitch state: " << (int)i3cSwitchState << std::endl;
        if(i3cSwitchState)
        {
            int ret = system("modprobe dw-i3c-master");
            if (ret != 0) {
                std::cout << "Failed to modprobe dw-i3c-master module" << std::endl;
                return;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            std::cout << "Start dimm spd reader" << std::endl;
            this->startRead();
        }
    },
    HostMiscDbusName, platformStatePath, "org.freedesktop.DBus.Properties", "Get",
    platformStateInterface, "dimmI3cSwitch");
}

void DIMMSpdReader::dimmI3cSwitchMatcher(
    std::vector<sdbusplus::bus::match_t>& matches, sdbusplus::bus_t& connection,
    std::function<void(bool)>&& onMatch)
{
    auto pulseEventMatcherCallback =
        [onMatch{std::move(onMatch)}, this](sdbusplus::message_t& msg) {
        bool value = false;
        if (!getDbusMsgState(msg, value))
        {
            return;
        }
        onMatch(value);
    };

    matches.emplace_back(
        connection,
        "type='signal',interface='org.freedesktop.DBus.Properties',member='"
        "PropertiesChanged',arg0='" +
        std::string(platformStateInterface) + "'",
        std::move(pulseEventMatcherCallback));
}

static bool setGPIOOutput(const std::string& name, const int value,
                          gpiod::line& gpioLine)
{
    // Find the GPIO line
    gpioLine = gpiod::find_line(name);
    if (!gpioLine)
    {
        lg2::error("Failed to find the {GPIO_NAME} line", "GPIO_NAME", name);
        return false;
    }

    // Request GPIO output to specified value
    try
    {
        gpioLine.request({"dimm-spd-reader", gpiod::line_request::DIRECTION_OUTPUT, {}},
                         value);
    }
    catch (const std::exception& e)
    {
        lg2::error("Failed to request {GPIO_NAME} output: {ERROR}", "GPIO_NAME",
                   name, "ERROR", e);
        return false;
    }

    lg2::debug("{GPIO_NAME} set to {GPIO_VALUE}", "GPIO_NAME", name,
              "GPIO_VALUE", value);
    return true;
}

static void setGPIOToSelectDimm(const boost::container::flat_map<std::string, bool>& cfgMap)
{
    for(auto cfg : cfgMap)
    {
        gpiod::line line;
        if (!setGPIOOutput(cfg.first, (int)cfg.second, line))
        {
            std::cerr << "Setting I3CSwitch GPIO failed \n";
            return;
        }
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

static int i3cWriteRead(std::string& i3cDev, uint8_t wBuf[], uint16_t wLen,
                            uint8_t rBuf[], uint16_t rLen)
{
    struct i3c_ioc_priv_xfer xfers[2];
    int nxfers = 0;

    if constexpr (debug)
    {
        std::cout << "i3c data to write:" << std::endl;
        for(int i=0; i<wLen; i++)
        {
			std::cout << "wBuf[" << i << "]: 0x" << std::hex << (int)wBuf[i] << std::dec << " ";
        }
        std::cout << std::endl;
    }

    int fd = open(i3cDev.c_str(), O_RDWR);
    if (fd < 0)
    {
        std::cerr << " unable to open i3c device " << i3cDev << "\n";
        goto err;
    }

    memset(xfers, 0, sizeof(xfers));
    if (wLen)
    {
        xfers[nxfers].len = wLen;
        xfers[nxfers].rnw = 0;
        xfers[nxfers].data = (uintptr_t)wBuf;
        nxfers++;
    }

    if (rLen)
    {
        xfers[nxfers].len = rLen;
        xfers[nxfers].rnw = 1;
        xfers[nxfers].data = (uintptr_t)rBuf;
        nxfers++;
    }

    if (ioctl(fd, I3C_IOC_PRIV_XFER(nxfers), xfers) < 0) {
        if constexpr (debug)
        {
		    std::cerr << "i3c transfer failed "<< "\n";
        }
        goto err;
	}

    for (int i = 0; i < nxfers; i++) {
		if (xfers[i].rnw) {
             memcpy((void *)rBuf, (void *)(uintptr_t)xfers[i].data, xfers[i].len * sizeof(uint8_t));
        }
	}

    if constexpr (debug)
    {
        std::cout << "i3c received data:\n";
        for (int i = 0; i < (int)rLen; i++)
        {
			std::cout << "rBuf[" << i << "]: 0x" << std::hex << (int)rBuf[i] << std::dec << " ";
        }
        std::cout << "\n";
    }

    close(fd);
    return 0;

err:
    close(fd);
    return -1;
}

void DIMMSpdReader::startRead()
{
    waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
    waitTimer.async_wait([this](const boost::system::error_code& ec) {
        if (ec == boost::asio::error::operation_aborted)
        {
            return; // we're being cancelled
        }
        // read timer error
        if (ec)
        {
            std::cerr << "timer error\n";
            return;
        }

        readDimmInfo();
        updateDbus();
        updateToDimmInfoFile();
        if(regCounter == 0)
        {
            regCounter = 6000;  //read status registers per 10 minutes.
        }
        else
        {
            regCounter--;
        }

        if(pmicCounter == 0)
        {
            pmicCounter = 300;  //read status registers per 30s.
        }
        else
        {
            pmicCounter--;
        }

        startRead();
    });
}

int DIMMSpdReader::getPmicType(uint8_t cpuid, uint8_t rank)
{
    int ret = -1;
    if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;

    if(gotType[cpuid][rank]) return 0;

    uint8_t index[] = {0xc8, 0xcc, 0xd0};
    uint16_t wLen = 2;
    uint8_t wrBuf[] = {0, 0, 0};
    uint8_t rdBuf[] = {0};
    uint16_t rLen = 1;
	uint8_t typeVal = 0;
    int spd = NUM_TO_SPD(rank);
    const uint8_t memReg = 0x80;

    std::string i3cSpdName = I3C_ADDR(cpuid) + to_hex(spd);

    wrBuf[0] = 0xb;
    ret = i3cWriteRead(i3cSpdName, wrBuf, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }

    size_t size = sizeof(index) / sizeof(index[0]);
    for(size_t i = 0; i < size; i++)
    {

        wrBuf[0] = static_cast<uint8_t>(memReg | (index[i] & 0x40) | (index[i] & 0x3F));
        wrBuf[1] = static_cast<uint8_t>((index[i] >> 7) & 0x0F);

        ret = i3cWriteRead(i3cSpdName, wrBuf, wLen, rdBuf, rLen);
        if (ret < 0)
        {
            return -1;
        }
				
		//SPD 0xc8, 0xcc, 0xd0 is Device Types
		//Check DeviceType[7] = 1 is Installed
		if((rdBuf[0] & 0x80) != 0)
		{
			//read DeviceType Bits[3:0]
			typeVal = rdBuf[0] & 0xf;
			pmicType[cpuid][rank] = (typeVal <= PMIC5030) ? static_cast<PMICType>(typeVal) : Unknown;
			break;
		}
    }

    if(pmicType[cpuid][rank] == PMIC5030)
    {
        twoByteMode[cpuid][rank] = true;
    }

    gotType[cpuid][rank] = true;

//----------------------------------------------------------------------------
//A. 1B Write R30 = 0x84 // 1 byte format, set 2 byte mode
//B. 2B Read R0030 //check 2 byte mode
//	if(R0030 == 0x84)
// 		2 byte mode + 1ms delay: R30 success
//	Else // Original 2 byte mode
//		C. 2B Write R0030 = 0x84
//		D. 2B Read R0030
//		if(R0030 == 0x84) //check 2 byte mode
// 			2 byte mode + 1ms delay: R30 success
	if (pmicType[cpuid][rank] == PMIC5030)
	{

	    int pmic = NUM_TO_PMIC(rank);
	    std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);

		if constexpr (debug)
        {
		    std::cout << "is PMIC5030"<< std::endl;
        }
		// 1. 1B Write R30 = 0x84
	    wLen = 2;
		rLen = 0;
	    wrBuf[0] = 0x30; wrBuf[1] = 0x84;
	    rdBuf[0] = 0x00;
	    ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
	    if (ret < 0)
	    {
	        return -1;
	    }

	    if constexpr (debug)
	    {
			std::cout << "first 1B Write R30 = 0x84" << std::endl;
	    }
		std::this_thread::sleep_for(std::chrono::milliseconds(9));

		// 2. 2B Read R0030
	    wLen = 2;
		rLen = 1;
	    wrBuf[0] = 0x30; wrBuf[1] = 0x00;
	    rdBuf[0] = 0x00;
	    ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
	    if (ret < 0)
	    {
	        return -1;
	    }

	    if constexpr (debug)
	    {
			std::cout << "2B Read R0030 = 0x" << std::hex << (int)rdBuf[0] << std::endl;
	    }


		// Successful switch to 2 Byte mode
		if(rdBuf[0] == 0x84)
		{
			if constexpr (debug)
	        {
			    std::cout << "Is 2 Byte mode" << std::endl;
			}
		}
		// It was originally 2 bytes mode
		else
		{
			//C2-2. 2B Write R30 = 0x84
			wrBuf[0] = 0x30; wrBuf[1] = 0x00; wrBuf[2] = 0x84;
			wLen = 3;
			rLen = 0;
	    	ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);

			std::this_thread::sleep_for(std::chrono::milliseconds(9));
			//C2-2. 2B Read R30 = 0x84
			wrBuf[0] = 0x30; wrBuf[1] = 0x00;
			wLen = 2;
			rLen = 1;
	    	ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
			if constexpr (debug)
	        {
			    std::cout << "2B Read R0030 = " << std::hex << (int)rdBuf[0] << std::endl;
			}

			//Force the use of 1-byte mode
			if(rdBuf[0] != 0x84)
	        {
	        	twoByteMode[cpuid][rank] = false;
			    std::cout << "Force the use of 1-byte mode" << std::endl;
			}

		}
	}


	
    if constexpr (debug)
    {
        std::cout << "pmicType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)pmicType[cpuid][rank] << std::endl;
        std::cout << "gotType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)gotType[cpuid][rank] << std::endl;
    }

    return ret;
}

int DIMMSpdReader::getDIMMRegsTemp(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
    int ret = -1;
    if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;

    if constexpr (debug)
    {
        std::cout << "Start to get DIMM temperature "<< std::endl;
    }

    uint8_t wrBuf[] = {0x31};
    uint16_t wLen = 1;
    uint8_t rdBuf[] = {0, 0};
    uint16_t rLen = 2;
    uint16_t integerPart;
    uint16_t fractionalPart;
    int spd = NUM_TO_SPD(rank);

#if 0
    std::string i3cSpdName = I3C_ADDR(cpuid) + to_hex(spd);
    ret = i3cWriteRead(i3cSpdName, wrBuf, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }

    integerPart = ((rdBuf[0] & 0xf0) >> 4) | ((rdBuf[1] & 0x0f) << 4);
    fractionalPart = (rdBuf[0] & 0x0C) >> 2;

    // check if a negative reading
    if (rdBuf[1] & 0x10)
    {
        uint16_t tmp = (integerPart << 2) | fractionalPart;
        tmp = ((~tmp) & 0x3ff) + 1;
        fractionalPart = tmp & 0x3;
        integerPart = tmp >> 2;
        dimmData.temp[2] = -1.0 * (integerPart + fractionalPart * 0.25);
    }
    else
    {
        dimmData.temp[2] = integerPart + fractionalPart * 0.25;
    }
#endif

    std::string i3cTS0Name = I3C_TS0_ADDR(cpuid) + to_hex(spd);
    memset(rdBuf, 0, sizeof(rdBuf));
    ret = i3cWriteRead(i3cTS0Name, wrBuf, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }

    integerPart = ((rdBuf[0] & 0xf0) >> 4) | ((rdBuf[1] & 0x0f) << 4);
    fractionalPart = (rdBuf[0] & 0x0C) >> 2;

    // check if a negative reading
    if (rdBuf[1] & 0x10)
    {
        uint16_t tmp = (integerPart << 2) | fractionalPart;
        tmp = ((~tmp) & 0x3ff) + 1;
        fractionalPart = tmp & 0x3;
        integerPart = tmp >> 2;
        dimmData.temp[0] = -1.0 * (integerPart + fractionalPart * 0.25);
    }
    else
    {
        dimmData.temp[0] = integerPart + fractionalPart * 0.25;
    }

    std::string i3cTS1Name = I3C_TS1_ADDR(cpuid) + to_hex(spd);
    memset(rdBuf, 0, sizeof(rdBuf));
    ret = i3cWriteRead(i3cTS1Name, wrBuf, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }

    integerPart = ((rdBuf[0] & 0xf0) >> 4) | ((rdBuf[1] & 0x0f) << 4);
    fractionalPart = (rdBuf[0] & 0x0C) >> 2;

    // check if a negative reading
    if (rdBuf[1] & 0x10)
    {
        uint16_t tmp = (integerPart << 2) | fractionalPart;
        tmp = ((~tmp) & 0x3ff) + 1;
        fractionalPart = tmp & 0x3;
        integerPart = tmp >> 2;
        dimmData.temp[1] = -1.0 * (integerPart + fractionalPart * 0.25);
    }
    else
    {
        dimmData.temp[1] = integerPart + fractionalPart * 0.25;
    }
    return 0;
}

int DIMMSpdReader::getDIMMRegsVol(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
    int ret = -1;
    if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;

    if constexpr (debug)
    {
        std::cout << "Start to get DIMM voltage "<< std::endl;
        std::cout << "pmicType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)pmicType[cpuid][rank] << std::endl;
    }

    uint16_t wLen;
    uint16_t rLen;
    uint8_t adcSel[6] = {0x80, 0x88, 0x90, 0x98, 0xa0, 0xd0};
    uint8_t adcOut[] = {0x31, 0};
    uint8_t rdBuf[] = {0};
    uint8_t wrBuf[3] = {0x30, 0, 0};
    uint8_t adcValue = 0;

    int pmic = NUM_TO_PMIC(rank);
    std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);

    rLen = 1;
    wLen = twoByteMode[cpuid][rank] ? 2 : 1;
    ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, &adcValue, rLen);
    if (ret < 0)
    {
        return -1;
    }

    adcValue &= 0x7;
    int adcValue10 = (int)(adcValue & 0x3);
    adcDelay[cpuid][rank] = getADCUpdateTime(adcValue10);

    int index = (pmicType[cpuid][rank] == PMIC5030) ? 6 : 4;
    for(int i=0; i<index; i++) {
		//Write R30
        rLen = 0; //For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
        if(twoByteMode[cpuid][rank])
        {
        	adcValue = 0x4; //PMIC5030 fixed the 2 byte mode + 1ms
            wrBuf[2] = adcValue | adcSel[i];
            wLen = 3;
        }
        else
        {
            wrBuf[1] = adcValue | adcSel[i];
            wLen = 2;
        }

        ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
        if (ret < 0)
        {
            return -1;
        }

		// For Renesas requirements, the JESD spec PMIC5030 needs a 9ms delay.
		if(pmicType[cpuid][rank] == PMIC5030)
        	std::this_thread::sleep_for(std::chrono::milliseconds(9));
		else
			std::this_thread::sleep_for(std::chrono::milliseconds(adcDelay[cpuid][rank]));

        wLen = twoByteMode[cpuid][rank] ? 2 : 1;
        rLen = 1;
        rdBuf[0] = 0;
        ret = i3cWriteRead(i3cPmicName, adcOut, wLen, rdBuf, rLen);
        if (ret < 0)
        {
            return -1;
        }
        dimmData.voltage[i] = rdBuf[0] * 15.0 / 1000.0;
    }

    return 0;
}

static double calStep31CurPower(int index, const uint8_t (&temp)[8])
{
    if (index < 0 || index > 5)
        return 0.0;

    uint8_t reg;
    uint8_t shift;
    if(index < 4)
    {
        reg = temp[0];
        shift = 6 - index * 2;
    }
    else
    {
        reg = temp[1];
        shift = 6 - (index - 4) * 2;
    }

    uint8_t msb = (reg >> shift) & 0x03;
    uint8_t lsb = temp[2 + index];
    uint16_t raw10 = (static_cast<uint16_t>(msb) << 8) | lsb;

    if constexpr (debug)
    {
        std::cout << "calStep31CurPower raw10: " << (int)raw10 << std::endl;
    }

    double result = std::round(raw10 * 31.25) / 1000.0;
    return result;
}

int DIMMSpdReader::checkADCAccuracyStepSize(uint8_t cpuid, uint8_t rank, DimmData& dimmData, const uint8_t* TargetData)
{
    int ret = -1;
    if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;

    if constexpr (debug)
    {
        std::cout << "Start to check ADC Accuracy Step Size."<< std::endl;
    }

    uint16_t wLen;
    uint16_t rLen;
    uint8_t rdBuf[] = {0};
    uint8_t wrBuf[3] = {0, 0, 0};
	uint16_t ThresholdSel[6] = {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B};     //OUTPUT_HIGH_CURRENT_CONSUMPTION_WARNING_THRESHOLD
	
    int pmic = NUM_TO_PMIC(rank);
    std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);

	// 1. Read R32
    rLen = 1;
    wLen = 2;
    wrBuf[0] = 0x32;			//address_Low
    wrBuf[1] = 0;				//address_High	
    ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }
	if((rdBuf[0] & 0x3) != 1)
	{
		dimmData.error[0] |= 1;
	}
	
	// 2. Read ThresholdSel {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B}
    rLen = 1;
    wLen = 2;
	for(int i=0; i<6; i++) {
	    wrBuf[0] = static_cast<uint8_t>(ThresholdSel[i] & 0xFF);			//address_Low
	    wrBuf[1] = static_cast<uint8_t>((ThresholdSel[i]>>8) & 0xFF);		//address_High	
	    ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
	    if (ret < 0)
	    {
	        return -1;
	    }
		if(rdBuf[0] != TargetData[i])
		{
			dimmData.error[0] |= 1<<(i+1);
		}
	}
    return ret;
}

int DIMMSpdReader::setADCAccuracyStepSize(uint8_t cpuid, uint8_t rank, DimmData& dimmData, uint8_t R32)
{
    int ret = -1;
    if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;


    if constexpr (debug)
    {
        std::cout << "Start to set Pmic5030 ADC Accuracy Step Size."<< std::endl;
    }

    uint16_t wLen;
    uint16_t rLen;
    uint8_t rdBuf[] = {0};
    uint8_t wrBuf[3] = {0, 0, 0};
	uint16_t ThresholdSel[6] = {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B};     //OUTPUT_HIGH_CURRENT_CONSUMPTION_WARNING_THRESHOLD
	uint8_t ThresholdData[6] = {0, 0, 0, 0, 0, 0};
	uint8_t TargetData[6] = {0, 0, 0, 0, 0, 0};
    uint8_t adcAcc[] = {0x32, 0, 0};
	uint32_t calcData = 0;
	constexpr int PMIC_CHANNEL_SWD_INDEX = 3;
	constexpr uint8_t PMIC_MAX_CUR_7500MA_REG_VAL = 0xF0; // SWA, SWB, SWC, SWE, SWF
	constexpr uint8_t PMIC_MAX_CUR_5000MA_REG_VAL = 0xA0; // SWD
	
    int pmic = NUM_TO_PMIC(rank);
    std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);

	// 1. Read ThresholdSel {0x1C, 0x1D, 0x1E, 0x1F, 0x11A, 0x11B}
    rLen = 1;
    wLen = 2;
	for(int i=0; i<6; i++) {
	    wrBuf[0] = static_cast<uint8_t>(ThresholdSel[i] & 0xFF);			//address_Low
	    wrBuf[1] = static_cast<uint8_t>((ThresholdSel[i]>>8) & 0xFF);		//address_High	
	    ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, &ThresholdData[i], rLen);
	    if (ret < 0)
	    {
	        return -1;
	    }	
	}

	// 2. Write R32[1:0] = 01b
    adcAcc[2] = (R32&0xFC)|0x1;
    wLen = 3;
    rLen = 0;	
    ret = i3cWriteRead(i3cPmicName, adcAcc, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        //std::cerr << " read word data failed. \n";
        return -1;
    }
	
	// 3. Write ThresholdData
    for(int i=0; i<6; i++) {
        rLen = 0;
        wLen = 3;		
        wrBuf[0] = static_cast<uint8_t>(ThresholdSel[i] & 0xFF);			//address_Low
        wrBuf[1] = static_cast<uint8_t>((ThresholdSel[i]>>8) & 0xFF);		//address_High

		//data
		calcData = (ThresholdData[i] * 4); // 125/31.25 = 4

		if (i == PMIC_CHANNEL_SWD_INDEX) {
			// SWD Maximum current is 5000mA        
			TargetData[i] = (calcData > PMIC_MAX_CUR_5000MA_REG_VAL) ? PMIC_MAX_CUR_5000MA_REG_VAL : static_cast<uint8_t>(calcData);     
		} 
		else {
			// SWA, SWB, SWC, SWE, SWF Maximum current is 7500mA
			TargetData[i] = (calcData > PMIC_MAX_CUR_7500MA_REG_VAL) ? PMIC_MAX_CUR_7500MA_REG_VAL : static_cast<uint8_t>(calcData);
		}
		wrBuf[2] = TargetData[i];

        ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
        if (ret < 0)
        {
            return -1;
        }
    }

	ret = checkADCAccuracyStepSize(cpuid, rank, dimmData, TargetData);
    if (ret < 0)
    {
        //std::cerr << " set ADCAccuracy Step Size failed. \n";
        return -1;
    }

    return ret;
}


int DIMMSpdReader::getDIMMRegsCurAndPow(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
    int ret = -1;
    if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;

    if constexpr (debug)
    {
        std::cout << "Start to get DIMM current and power "<< std::endl;
        std::cout << "pmicType[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)pmicType[cpuid][rank] << std::endl;
    }

    int index = 0;
    uint16_t wLen;
    uint16_t rLen = 1;
    bool accStep31 = false;

    uint8_t adcSelect[] = {0x1b, 0, 0};
    uint16_t curOut[8];
    uint8_t temp[8];
    uint8_t rdBuf[] = {0, 0, 0, 0, 0, 0, 0, 0}; //For Renesas requirements, I3C command: Continuously read R100~R107
    uint8_t wrBuf[] = {0, 0};

    int pmic = NUM_TO_PMIC(rank);
    std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);

    if(pmicType[cpuid][rank] == PMIC5030)
    {
        uint8_t adcAcc[] = {0x32, 0};
        wLen = twoByteMode[cpuid][rank] ? 2 : 1;
        ret = i3cWriteRead(i3cPmicName, adcAcc, wLen, rdBuf, rLen);
        if (ret < 0)
        {
            //std::cerr << " read word data failed. \n";
            return -1;
        }
        if((rdBuf[0] & 0x3) == 1) accStep31 = true;
		else
		{
			if(twoByteMode[cpuid][rank])
			{
				//if two Byte Mode + PMIC5030, R32[1:0] set 01b			
				ret = setADCAccuracyStepSize(cpuid, rank, dimmData, rdBuf[0]);
				accStep31 = true;
		        if (ret < 0)
		        {
		            //std::cerr << " set ADCAccuracy Step Size failed. \n";
		            return -1;
		        }
			}
		}
        if constexpr (debug)
        {
            std::cout << "accStep31: " << (int)accStep31 << std::endl;
        }
    }

    if(accStep31)
    {
        index = 8;
        for(int i=0; i<index; i++)
        {
            curOut[i] = 0x100 + i;
        }
    }
    else
    {
        index = 4;
        for(int i=0; i<index; i++)
        {
            curOut[i] = 0xc + i;
        }
    }

    wLen = twoByteMode[cpuid][rank] ? 2 : 1;
    ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }

    if(twoByteMode[cpuid][rank])
    {
        adcSelect[2] = rdBuf[0] & 0xbf;   //Read current
        wLen = 3;
    }
    else
    {
        adcSelect[1] = rdBuf[0] & 0xbf;   //Read current
        wLen = 2;
    }
	rLen = 0; //For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
    ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(adcDelay[cpuid][rank]));


    if(accStep31)
    {
        rLen = 8;
		memset(rdBuf, 0, sizeof(rdBuf));
        if(twoByteMode[cpuid][rank])
        {
            wrBuf[0] = static_cast<uint8_t>(curOut[0] & 0xFF);
            wrBuf[1] = static_cast<uint8_t>((curOut[0] >> 8) & 0xFF);
            wLen = 2;
        }
        else
        {
            if constexpr (debug)
            {
            	std::cout << "twoByteMode[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)twoByteMode[cpuid][rank] << std::endl;
            }
			if(pmicType[cpuid][rank] == PMIC5030)
			{
				return -1;
			}
        }

        ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
        if (ret < 0)
        {
            return -1;
        }
		for(int i=0; i<index; i++) {
        	temp[i] = rdBuf[i];
		}
	    
        for(int i=0; i<6; i++)
        {
            dimmData.current[i] = calStep31CurPower(i, temp);
        }
    }
	else
    {
		for(int i=0; i<index; i++) {
	        rLen = 1;
	        rdBuf[0] = 0;
	        if(twoByteMode[cpuid][rank])
	        {
	            wrBuf[0] = static_cast<uint8_t>(curOut[i] & 0xFF);
	            wrBuf[1] = static_cast<uint8_t>((curOut[i] >> 8) & 0xFF);
	            wLen = 2;
	        }
	        else
	        {
	            wrBuf[0] = static_cast<uint8_t>(curOut[i]);
	            wLen = 1;
	        }

	        ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
	        if (ret < 0)
	        {
	            return -1;
	        }

	        dimmData.current[i] = rdBuf[0] * 125.0 / 1000.0;

	    }
	}


    //start to read power
    if(twoByteMode[cpuid][rank])
    {
        adcSelect[2] |= 0x40;
        wLen = 3;
    }
    else
    {
        adcSelect[1] |= 0x40;
        wLen = 2;
    }
	rLen = 0; //For Renesas requirements, I3C write commands cannot be directly read; reading requires a separate I3C command.
    ret = i3cWriteRead(i3cPmicName, adcSelect, wLen, rdBuf, rLen);
    if (ret < 0)
    {
        return -1;
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(adcDelay[cpuid][rank]));

	// For Renesas requirements, I3C command: Continuously read R100~R107
    if(accStep31)
    {
        rLen = 8;
        memset(rdBuf, 0, sizeof(rdBuf));
        if(twoByteMode[cpuid][rank])
        {
            wrBuf[0] = static_cast<uint8_t>(curOut[0] & 0xFF);
            wrBuf[1] = static_cast<uint8_t>((curOut[0] >> 8) & 0xFF);
            wLen = 2;
        }
        else
        {
            if constexpr (debug)
            {
            	std::cout << "twoByteMode[" << (int)cpuid << "][" << (int)rank << "] = "<< (int)twoByteMode[cpuid][rank] << std::endl;
            }
			if(pmicType[cpuid][rank] == PMIC5030)
			{
				return -1;
			}
        }

        ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
        if (ret < 0)
        {
            return -1;
        }
		for(int i=0; i<index; i++) {
        	temp[i] = rdBuf[i];
		}

        for(int i=0; i<6; i++)
        {
            dimmData.power[i] = calStep31CurPower(i, temp);
        }
    }
	else
    {
	    for(int i=0; i<index; i++)
		{
	        rLen = 1;
	        rdBuf[0] = 0;
	        if(twoByteMode[cpuid][rank])
	        {
	            wrBuf[0] = static_cast<uint8_t>(curOut[i] & 0xFF);
	            wrBuf[1] = static_cast<uint8_t>((curOut[i] >> 8) & 0xFF);
	            wLen = 2;
	        }
	        else
	        {
	            wrBuf[0] = static_cast<uint8_t>(curOut[i]);
	            wLen = 1;
	        }

	        ret = i3cWriteRead(i3cPmicName, wrBuf, wLen, rdBuf, rLen);
	        if (ret < 0)
	        {
	            return -1;
	        }
	        dimmData.power[i] = rdBuf[0] * 125.0 / 1000.0;

		}
	}

    return 0;
}

int DIMMSpdReader::getPmicStatusRegs(uint8_t cpuid, uint8_t rank, DimmData& dimmData)
{
    int ret = -1;
    if(cpuid >= MAX_CPU_ID || rank >= MAX_DIMM_RANK) return ret;

    uint16_t wLen = 1;
    uint16_t rLen = 1;
    uint8_t reg[] = {0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb};
    uint8_t rdBuf[] = {0};

    int pmic = NUM_TO_PMIC(rank);
    std::string i3cPmicName = I3C_ADDR(cpuid) + to_hex(pmic);

    size_t index = sizeof(reg) / sizeof(reg[0]);
    for(size_t i=0; i<index; i++) {
        ret = i3cWriteRead(i3cPmicName, &reg[i], wLen, rdBuf, rLen);
        if (ret < 0)
        {
            return -1;
        }

        dimmData.reg[i] = rdBuf[0];
        std::this_thread::sleep_for(std::chrono::milliseconds(2));
    }

    return 0;
}

void DIMMSpdReader::init()
{
    for(int cpuid=0; cpuid<MAX_CPU_ID; cpuid++) {
        for(int i=0; i<MAX_DIMM_RANK; i++) {
            DimmData dData = {
                {0, 0, 0, 0, 0, 0, 0, 0},
				{0},
                {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
                 std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
                 std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()},
                {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
                 std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
                 std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()},
                {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
                 std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
                 std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()},
                {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(),
                 std::numeric_limits<double>::quiet_NaN()}};

            std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid) + "_" + std::string(1, 'A' + i);

            dimmDataTable.insert_or_assign(dimmName, std::move(dData));

            pmicType[cpuid][i] = Unknown;
            gotType[cpuid][i] = false;
            twoByteMode[cpuid][i] = false;
        }
    }

    regCounter = 0;
    pmicCounter = 0;
    updateDbus();
    updateToDimmInfoFile();
}

void DIMMSpdReader::readDimmInfo()
{
    int ret = -1;

    for(int cpuid=0; cpuid<MAX_CPU_ID; cpuid++) {
        for(int i=0; i<MAX_DIMM_RANK; i++) {
            std::string dimmName = "DIMM_CPU" + std::string(1, '0' + cpuid) + "_" + std::string(1, 'A' + i);
            DimmData dData = dimmDataTable[dimmName];
            for (int j = 0; j < STATUS_TEMP_NUM; j++) {
                dData.temp[j] = std::numeric_limits<double>::quiet_NaN();
            }
            for (int j = 0; j < STATUS_PMIC_RAIL_NUM; j++) {
                dData.voltage[j] = std::numeric_limits<double>::quiet_NaN();
                dData.current[j] = std::numeric_limits<double>::quiet_NaN();
                dData.power[j] = std::numeric_limits<double>::quiet_NaN();
            }
            for (int j = 0; j < STATUS_REG_NUM; j++) {
                dData.reg[j] = 0;
            }

            int index = cpuid * 4 + i / 3;
            if(index != lastIndex) {
                setGPIOToSelectDimm(cfgGPIO[index]);
                lastIndex = index;
            }

            ret = getPmicType(cpuid, i);
            if (ret < 0)
            {
                if constexpr (debug)
                {
                    std::cerr << " get cpu" << cpuid << " rank" << i << " PMIC Type failed. \n";
                }
            }

            ret = getDIMMRegsTemp(cpuid, i, dData);
            if (ret < 0)
            {
                if constexpr (debug)
                {
                    std::cerr << " read cpu" << cpuid << " rank" << i << " dimm temp failed. \n";
                }
            }
            updateField(dimmName, TEMP, dData.temp, STATUS_TEMP_NUM);
#if 1
            if(regCounter == 0)
            {
                ret = getPmicStatusRegs(cpuid, i, dData);
                if (ret < 0)
                {
                    if constexpr (debug)
                    {
                        std::cerr << " read cpu" << cpuid << " rank" << i << " status registers failed. \n";
                    }
                }

                updateField(dimmName, STATUS, dData.reg, STATUS_REG_NUM);
            }

            if(pmicCounter == 0)
            {
                ret = getDIMMRegsVol(cpuid, i, dData);
                if (ret < 0)
                {
                    if constexpr (debug)
                    {
                        std::cerr << " read cpu" << cpuid << " rank" << i << " dimm voltage failed. \n";
                    }
                }

                updateField(dimmName, VOLTAGE, dData.voltage, STATUS_PMIC_RAIL_NUM);

                ret = getDIMMRegsCurAndPow(cpuid, i, dData);
                if (ret < 0)
                {
                    if constexpr (debug)
                    {
                        std::cerr << " read cpu" << cpuid << " rank" << i << " dimm current and power failed. \n";
                    }

                }

                updateField(dimmName, CURRENT, dData.current, STATUS_PMIC_RAIL_NUM);
                updateField(dimmName, POWER, dData.power, STATUS_PMIC_RAIL_NUM);
				updateField(dimmName, ERROR, dData.error, STATUS_ERROR_NUM);
            }
#endif
        }
    }

    return;
}

void DIMMSpdReader::updateDbus()
{
    for(auto data : dimmDataTable) {
        if(dimm_temp_inf.find(data.first) != dimm_temp_inf.end()) {
            dimm_temp_inf[data.first]->set_property("TS0", data.second.temp[0]);
            dimm_temp_inf[data.first]->set_property("TS1", data.second.temp[1]);
            dimm_temp_inf[data.first]->set_property("SPD_TS", data.second.temp[2]);
        }

        if(dimm_voltage_inf.find(data.first) != dimm_voltage_inf.end()) {
            dimm_voltage_inf[data.first]->set_property("SWA", data.second.voltage[0]);
            dimm_voltage_inf[data.first]->set_property("SWB", data.second.voltage[1]);
            dimm_voltage_inf[data.first]->set_property("SWC", data.second.voltage[2]);
            dimm_voltage_inf[data.first]->set_property("SWD", data.second.voltage[3]);
            dimm_voltage_inf[data.first]->set_property("SWE", data.second.voltage[4]);
            dimm_voltage_inf[data.first]->set_property("SWF", data.second.voltage[5]);

        }

        if(dimm_current_inf.find(data.first) != dimm_current_inf.end()) {
            dimm_current_inf[data.first]->set_property("SWA", data.second.current[0]);
            dimm_current_inf[data.first]->set_property("SWB", data.second.current[1]);
            dimm_current_inf[data.first]->set_property("SWC", data.second.current[2]);
            dimm_current_inf[data.first]->set_property("SWD", data.second.current[3]);
            dimm_current_inf[data.first]->set_property("SWE", data.second.current[4]);
            dimm_current_inf[data.first]->set_property("SWF", data.second.current[5]);
        }

        if(dimm_power_inf.find(data.first) != dimm_power_inf.end()) {
            dimm_power_inf[data.first]->set_property("SWA", data.second.power[0]);
            dimm_power_inf[data.first]->set_property("SWB", data.second.power[1]);
            dimm_power_inf[data.first]->set_property("SWC", data.second.power[2]);
            dimm_power_inf[data.first]->set_property("SWD", data.second.power[3]);
            dimm_power_inf[data.first]->set_property("SWE", data.second.power[4]);
            dimm_power_inf[data.first]->set_property("SWF", data.second.power[5]);
        }

        if(dimm_statusReg_inf.find(data.first) != dimm_statusReg_inf.end()) {
            dimm_statusReg_inf[data.first]->set_property("0x04", data.second.reg[0]);
            dimm_statusReg_inf[data.first]->set_property("0x05", data.second.reg[1]);
            dimm_statusReg_inf[data.first]->set_property("0x06", data.second.reg[2]);
            dimm_statusReg_inf[data.first]->set_property("0x07", data.second.reg[3]);
            dimm_statusReg_inf[data.first]->set_property("0x08", data.second.reg[4]);
            dimm_statusReg_inf[data.first]->set_property("0x09", data.second.reg[5]);
            dimm_statusReg_inf[data.first]->set_property("0x0a", data.second.reg[6]);
            dimm_statusReg_inf[data.first]->set_property("0x0b", data.second.reg[7]);
        }

        if(dimm_errCode_inf.find(data.first) != dimm_errCode_inf.end()) {
            dimm_errCode_inf[data.first]->set_property("0x32", data.second.error[0]);
        }
		
    }
}

void DIMMSpdReader::updateToDimmInfoFile()
{
    const std::filesystem::path outputPath(dimmInfoPath);
    std::ofstream jsonOutputFile(outputPath);
    if (!jsonOutputFile.is_open()) {
        std::cout << "Failed to open dimmInfo json file" << std::endl;
        return;
    }

    nlohmann::json js;
    std::string timestamp = get_current_timestamp();
    js["Current Time"] = timestamp;
    //std::cout << "timestamp: " << timestamp << std::endl;

    for(auto data : dimmDataTable) {
        //update temperature
        js[data.first]["Temperature_DegreesC"]["TS0"] = data.second.temp[0];
        js[data.first]["Temperature_DegreesC"]["TS1"] = data.second.temp[1];
        js[data.first]["Temperature_DegreesC"]["SPD_TS"] = data.second.temp[2];

        //update voltage
        js[data.first]["Voltage_Volt"]["SWA"] = data.second.voltage[0];
        js[data.first]["Voltage_Volt"]["SWB"] = data.second.voltage[1];
        js[data.first]["Voltage_Volt"]["SWC"] = data.second.voltage[2];
        js[data.first]["Voltage_Volt"]["SWD"] = data.second.voltage[3];
        js[data.first]["Voltage_Volt"]["SWE"] = data.second.voltage[4];
        js[data.first]["Voltage_Volt"]["SWF"] = data.second.voltage[5];
        //update current
        js[data.first]["Current_Ampere"]["SWA"] = data.second.current[0];
        js[data.first]["Current_Ampere"]["SWB"] = data.second.current[1];
        js[data.first]["Current_Ampere"]["SWC"] = data.second.current[2];
        js[data.first]["Current_Ampere"]["SWD"] = data.second.current[3];
        js[data.first]["Current_Ampere"]["SWE"] = data.second.current[4];
        js[data.first]["Current_Ampere"]["SWF"] = data.second.current[5];
        //update power
        js[data.first]["Power_Watt"]["SWA"] = data.second.power[0];
        js[data.first]["Power_Watt"]["SWB"] = data.second.power[1];
        js[data.first]["Power_Watt"]["SWC"] = data.second.power[2];
        js[data.first]["Power_Watt"]["SWD"] = data.second.power[3];
        js[data.first]["Power_Watt"]["SWE"] = data.second.power[4];
        js[data.first]["Power_Watt"]["SWF"] = data.second.power[5];

        //update status registers
        js[data.first]["Status_Reg"]["0x04"] = data.second.reg[0];
        js[data.first]["Status_Reg"]["0x05"] = data.second.reg[1];
        js[data.first]["Status_Reg"]["0x06"] = data.second.reg[2];
        js[data.first]["Status_Reg"]["0x07"] = data.second.reg[3];
        js[data.first]["Status_Reg"]["0x08"] = data.second.reg[4];
        js[data.first]["Status_Reg"]["0x09"] = data.second.reg[5];
        js[data.first]["Status_Reg"]["0x0a"] = data.second.reg[6];
        js[data.first]["Status_Reg"]["0x0b"] = data.second.reg[7];

		//update error code
        js[data.first]["Error_Code"]["0x32"] = data.second.error[0];
    }

    jsonOutputFile << js.dump(4);
    return;
}

void DIMMSpdReader::setupMatches(sdbusplus::bus_t& connection)
{
    std::weak_ptr<DIMMSpdReader> weakRef = weak_from_this();
    std::shared_ptr<DIMMSpdReader> sharedRef = weakRef.lock();
    dimmI3cSwitchMatcher(matches, connection, [sharedRef](bool state)
    {
        if (!sharedRef)
        {
            return;
        }
        if (!state)
        {
            //switch dimm i3c to bios
            sharedRef->waitTimer.cancel();
            sharedRef->init();
            std::cout << "waitTimer canceled" << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            int ret = system("rmmod dw-i3c-master");
            if (ret != 0) {
                std::cout << "Failed to rmmod dw-i3c-master module" << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        else
        {
            int ret = system("modprobe dw-i3c-master");
            if (ret != 0) {
                std::cout << "Failed to modprobe dw-i3c-master module" << std::endl;
                return;
            }	
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            //switch dimm i3c to bmc
            std::cout << "start to Read" << std::endl;
            sharedRef->startRead();
        }
    });
}
