/**
 *
 * @author    seagle
 * @date      2025-3-21
 * @Description 
 *   Replace old Accessor
 */  
#include"TcPch.h"
#pragma hdrstop
#include"pmc_types.h"
#include"pmc_exception.h"
#include"DeviceAttribute.h"
#include"public.h"
#include"DEVICE.h"
#include"debug.h"
static DeviceAttribute* holdDoTable[MAX_HOLD_DO_COUNT] = { NULL };
static DeviceAttribute* waitDiTable[MAX_WAIT_DI_COUNT] = { NULL };
DeviceAttribute::DeviceAttribute() {
    addr = NULL;
    typeMax = 0;
    typeMin = 0;
    memset(name, '\0', sizeof(name));
    memset(referName, '\0', sizeof(referName));
    type = 0;
    isReadOnly = FALSE;
    isEmpty = TRUE;
}
void DeviceAttribute::create(const char* name) {
    addr = NULL;
    if (name == NULL) {
        this->name[0] = '\0';
    }
    else {
        strcpy (this->name, name);
        
    }
    isEmpty = TRUE;
}
void DeviceAttribute::create(const char* name,const char*referName) {
    create(name);
    if (referName == NULL) {
        this->referName[0] = '\0';
    }
    else {
        strcpy (this->referName, referName);
    }
    isEmpty = TRUE;
}



void DeviceAttribute::create(const char* name,char *addr,const char* referName,PMCBOOL isReadOnly){
    create(name,referName);
    isEmpty = FALSE;
    this->type=ATTR_TYPE_CHAR;
    this->addr=addr;
    typeMax=127;
    typeMin=-128;
    this->isReadOnly=isReadOnly;
}
void DeviceAttribute::create(const char* name,unsigned char *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_UCHAR;
    this->addr=addr;
    typeMax=255;
    typeMin=0;
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,bool * addr, const char* referName, PMCBOOL isReadOnly) {
    create(name, referName);
    this->type = ATTR_TYPE_BOOL;
    this->addr = addr;
    typeMax = 1;
    typeMin = 0;
    this->isReadOnly = isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,short *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_SHORT;
    this->addr=addr;
    typeMax=32767;
    typeMin=-32768;
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,unsigned short *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_USHORT;
    this->addr=addr;
    typeMax=65535;
    typeMin=0;
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,int *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_INT;
    this->addr=addr;
    typeMax= (double)2147483647;
    typeMin=-1*(double)2147483648;
    
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,unsigned int *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_UINT;
    this->addr=addr;
    typeMax = (double)4294967295;
    typeMin=0;
    
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,long *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_LONG;
    this->addr=addr;
    typeMax=(double)0x7fffffffffffffffL;
    typeMin=(double)0x8000000000000000L;
    
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,unsigned long *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_ULONG;
    this->addr=addr;
    typeMax=(double)0xffffffffffffffffUL;
    typeMin=0;
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,float *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_FLOAT;
    this->addr=addr;
    typeMax=4194303;
    typeMin=-4194303;
    
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
void DeviceAttribute::create(const char* name,double *addr,const char* referName,PMCBOOL isReadOnly){
    create(name, referName);
    this->type=ATTR_TYPE_DOUBLE;
    this->addr=addr;
    typeMax= (double)4503599627370495L;
    typeMin= -1.0*(double)4503599627370495L;
    
    this->isReadOnly=isReadOnly;
    isEmpty = FALSE;
}
PMCBOOL DeviceAttribute::getBoolValue(){
    if (addr == NULL) {
        return FALSE;
    }
    switch(type){
        case ATTR_TYPE_BOOL:
        case ATTR_TYPE_CHAR:
            if(*(char *)addr){
                return TRUE;
            }else{
                return FALSE;
            }
        case ATTR_TYPE_UCHAR:
            if(*(unsigned char *)addr){
                return TRUE;
            }else{
                return FALSE;
            }
        case ATTR_TYPE_SHORT:
            if(*(short *)addr){
                return TRUE;
            }else{
                return FALSE;
            }
        case ATTR_TYPE_USHORT:
            if(*(unsigned short *)addr){
                return TRUE;
            }else{
                return FALSE;
            }  
        case ATTR_TYPE_INT:
            if(*(int *)addr){
                return TRUE;
            }else{
                return FALSE;
            }
        case ATTR_TYPE_UINT:
            if(*(unsigned int *)addr){
                return TRUE;
            }else{
                return FALSE;
            }
        case ATTR_TYPE_LONG:
            if(*(long *)addr){
                return TRUE;
            }else{
                return FALSE;
            }
        case ATTR_TYPE_ULONG:
            if(*(unsigned long *)addr){
                return TRUE;
            }else{
                return FALSE;
            }
    }
    EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by bool but it is %d",name,type);
    return FALSE;
    
}
#define BLOCK_TRANSFER_TYPE_GET(retType)                                                                     \
    if(type==ATTR_TYPE_CHAR){                                                                      \
        char theValue=*((char *)(addr));                                                            \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by char but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    }else if(type==ATTR_TYPE_UCHAR){                                                                \
        unsigned char theValue=*((unsigned char *)(addr));                                              \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by uchar but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    }else if(type==ATTR_TYPE_SHORT){                                                                \
        short theValue=*((short *)(addr));                                                          \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by short but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    }else if(type==ATTR_TYPE_USHORT){                                                                \
        unsigned short theValue=*((unsigned short *)(addr));                                                 \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by ushort but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    }else if(type==ATTR_TYPE_INT){                                                                   \
        int theValue=*((int *)(addr));                                                               \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by int but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    }else if(type==ATTR_TYPE_UINT){                                                                \
        unsigned int theValue=*((unsigned int *)(addr));                                            \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by uint but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
     }else if(type==ATTR_TYPE_LONG){                                                                   \
        long theValue=*((long *)(addr));                                                               \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by long but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    }else if(type==ATTR_TYPE_ULONG){                                                                \
        unsigned long theValue=*((unsigned long *)(addr));                                            \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by char ulong it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
     }else if(type==ATTR_TYPE_FLOAT){                                                                   \
        float theValue=*((float *)(addr));                                                               \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by float but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    }else if(type==ATTR_TYPE_DOUBLE){                                                                \
        double theValue=*((double *)(addr));                                                         \
        if(theValue>theMax||theValue<theMin){                                                       \
            EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by double but it is %d",name,type); \
        }                                                                                           \
        return (retType)theValue;                                                                            \
    } else{                                                                                         \
        EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,query by xxx but it is %d",name,type); \
    }


char DeviceAttribute::getCharValue(){
    if(addr==NULL){
        return 0;
    }
    if(type!=ATTR_TYPE_CHAR){
        static double theMax=127;
        static double theMin=-128;
        BLOCK_TRANSFER_TYPE_GET(char)
    }
    return *((char *)(addr));
}
unsigned char DeviceAttribute::getUChaValue(){
    if(addr==NULL){
        return 0;
    }
    if(type!=ATTR_TYPE_UCHAR){
        static double theMax=255;
        static double theMin=0;
        BLOCK_TRANSFER_TYPE_GET(unsigned char)
    }
    return *((unsigned char *)(addr));
}
short DeviceAttribute::getShortValue(){
    if(addr==NULL){
        return 0;
    }
    if(type!=ATTR_TYPE_UCHAR){
        static double theMax=32767;
        static double theMin= -32768;
        BLOCK_TRANSFER_TYPE_GET(short)
    }
    return *((short *)(addr));
    
}
unsigned short DeviceAttribute::getUShortValue(){
    if(addr==NULL){
        return 0;
    }
    if(type!=ATTR_TYPE_USHORT){
        static double theMax=65535;
        static double theMin=0;
        BLOCK_TRANSFER_TYPE_GET(unsigned short)
    }
    return *((unsigned short *)(addr));
};
int DeviceAttribute::getIntValue(){
    if(addr==NULL){
        return 0;
    }
   if(type!=ATTR_TYPE_INT){
        static double theMax= (double)2147483647;
        static double theMin= -1*(double)2147483648;
        BLOCK_TRANSFER_TYPE_GET(int)
    }
    return *((int *)(addr));
}

unsigned int DeviceAttribute::getUIntValue(){
    if(addr==NULL){
        return 0;
    }
   if(type!=ATTR_TYPE_UINT){
        static double theMax= (double)4294967295;
        static double theMin= 0;
        BLOCK_TRANSFER_TYPE_GET(unsigned int)
    }
    return *((unsigned int *)(addr));
}
long DeviceAttribute::getLongValue(){
    if(addr==NULL){
        return 0;
    }
   if(type!=ATTR_TYPE_LONG){       
        static  double theMax= (double)0x7fffffffffffffffL;
        static double theMin= (double)0x8000000000000000L;
        BLOCK_TRANSFER_TYPE_GET(long)
    }
    return *((long *)(addr));
}
unsigned long DeviceAttribute::getULongValue(){
    if(addr==NULL){
        return 0;
    }
   if(type!=ATTR_TYPE_ULONG){
        static double theMax= (double)0xffffffffffffffffUL;
        static double  theMin=0;
        BLOCK_TRANSFER_TYPE_GET(unsigned long)
    }
    return *((unsigned long *)(addr));
}
float DeviceAttribute::getFloatValue(){
    if(addr==NULL){
        return 0;
    }
   if(type!=ATTR_TYPE_FLOAT){
        static double theMax= (double)4194303;
        static double theMin= -1.0*(double)4194303;
        BLOCK_TRANSFER_TYPE_GET(float)
    }
    return *((float *)(addr));
}
double DeviceAttribute::getDoubleValue(){
    if(addr==NULL){
        return 0;
    }
   if(type!=ATTR_TYPE_DOUBLE){
        static double theMax= (double)4503599627370495L;;
        static double theMin= -1.0*(double)4503599627370495L;;
        BLOCK_TRANSFER_TYPE_GET(double)
    }
    return *((double *)(addr));
}

///////////

#define BLOCK_TRANSFER_TYPE_SET(outType)                                                                 \
    if(value>typeMax||value<typeMin){                                                          \
        EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,overflow",name);  \
    }else{                                                                                      \
        switch(type){                                                                           \
            case ATTR_TYPE_BOOL:                                                                \
                *((PMCBOOL *)(addr))=(PMCBOOL)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_CHAR:                                                                \
                *((char *)(addr))=(char)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_UCHAR:                                                                \
                *((unsigned char *)(addr))=(unsigned char)value;                                                \
                break;                                                                          \
            case ATTR_TYPE_SHORT:                                                                \
                *((short *)(addr))=(short)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_USHORT:                                                                \
                *((unsigned short *)(addr))=(unsigned short)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_INT:                                                                \
                *((int *)(addr))=(int)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_UINT:                                                                \
                *((unsigned int *)(addr))=(unsigned int)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_LONG:                                                                \
                *((long *)(addr))=(long)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_ULONG:                                                                \
                *((unsigned long *)(addr))=(unsigned long)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_FLOAT:                                                                \
                *((float *)(addr))=(float)value;                                                        \
                break;                                                                          \
            case ATTR_TYPE_DOUBLE:                                                                \
                *((double *)(addr))=(double)value;                                                        \
                break;                                                                          \
            default:                                                                            \
                EV->postAlarmLog(ALARM_USER,"text=IO Variable:%s type error,set by %d  but it is %d",name,outType,type); \
        }                                                                                           \
    }

void DeviceAttribute::setBoolValue(PMCBOOL value){
    if(addr==NULL){
        return;
    }
    if (value == getBoolValue()) {
        return;
    }
    //后面肯定都是传入值与当前值不同的了
    if (isPulse || isHold) {
        EV->postAlarmLog(ALARM_DO_ILK, "reason=%s is hold",referName);
        return;
    }
    //TODO:interlock处理
    if(type!=ATTR_TYPE_BOOL){
        BLOCK_TRANSFER_TYPE_SET("bool")
        return;
    }
    *((PMCBOOL *)(addr))=value;
}


void DeviceAttribute::setCharValue(char value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_CHAR){
        BLOCK_TRANSFER_TYPE_SET("char")
        return;
    }
    *((char *)(addr))=value;
}
void DeviceAttribute::setUChaValue(unsigned char value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_UCHAR){
         BLOCK_TRANSFER_TYPE_SET("uchar")
         return;
    }
    *((unsigned char *)(addr))=value;
}
void DeviceAttribute::setShortValue(short value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_SHORT){
         BLOCK_TRANSFER_TYPE_SET("short")
         return;
    }
    *((short *)(addr))=value;
}
void DeviceAttribute::setUShortValue(unsigned short value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_USHORT){
         BLOCK_TRANSFER_TYPE_SET("ushort")
         return;
    }
    *((unsigned short *)(addr))=value;
}
void DeviceAttribute::setIntValue(int value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_INT){
         BLOCK_TRANSFER_TYPE_SET("int")
         return;
    }
    *((int *)(addr))=value;
}
void DeviceAttribute::setUIntValue(unsigned int value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_UINT){
         BLOCK_TRANSFER_TYPE_SET("uint")
         return;
    }
    *((unsigned int *)(addr))=value;
}
void DeviceAttribute::setLongValue(long value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_LONG){
         BLOCK_TRANSFER_TYPE_SET("long")
         return;
    }
    *((long *)(addr))=value;
}
void DeviceAttribute::setULongValue(unsigned long value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_ULONG){
         BLOCK_TRANSFER_TYPE_SET("ulong")
         return;
    }
    *((unsigned long *)(addr))=value;
}
void DeviceAttribute::setFloatValue(float value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_FLOAT){
         BLOCK_TRANSFER_TYPE_SET("float")
         return;
    }
    *((float *)(addr))=value;
}
void DeviceAttribute::setDoubleValue(double value){
    if(addr==NULL){
        return;
    }
    if(type!=ATTR_TYPE_DOUBLE){
        BLOCK_TRANSFER_TYPE_SET("double")
        return;
    }
    *((double *)(addr))=value;
}

void DeviceAttribute::setPluseValue(PMCBOOL value, void (*callback)(DeviceAttribute* var),long holdTime)
{
    if (addr == NULL) {
        return;
    }
    
    if (isPulse || isHold) {
        if (value == getBoolValue()) {
            return;
        }
        else {
            EV->postAlarmLog(ALARM_DO_ILK, "reason=%s is hold", referName);
        }
        return;
    }
    setBoolValue(value);
    if (value != getBoolValue()) {
        return;
    }
    isPulse = TRUE;
    setHoldTime = holdTime;
    elapsedHoldTime = 0;
    this->holdCallback = callback;
    int pos = ((unsigned long)this)/256 % MAX_HOLD_DO_COUNT;
    if (holdDoTable[pos] == NULL) {
        holdDoTable[pos] = this;
        return;
    }
    for (pos = 0; pos < MAX_HOLD_DO_COUNT; pos++) {
        if (holdDoTable[pos] == NULL) {
            break;
        }
    }
    if (pos < MAX_HOLD_DO_COUNT) {
        holdDoTable[pos] = this;
        return;
    }
    else {
        logger->error("Too more holding DO");
    }
}

void DeviceAttribute::setHoldValue(PMCBOOL value, void (*callback)(DeviceAttribute* var),long holdTime)
{
    
    if (addr == NULL) {
        return;
    }

    if (isPulse || isHold) {
        if (value == getBoolValue()) {
            return;
        }
        else {
            EV->postAlarmLog(ALARM_DO_ILK, "reason=%s is hold", referName);
        }
        return;
    }
    setBoolValue(value);
    if (value != getBoolValue()) {
        return;
    }
    isHold = TRUE;
    setHoldTime = holdTime;
    elapsedHoldTime = 0;
    this->holdCallback = callback;
    int pos = ((unsigned long)this)/256 % MAX_HOLD_DO_COUNT;
    if (holdDoTable[pos] == NULL) {
        holdDoTable[pos] = this;
        return;
    }
    for (pos = 0; pos < MAX_HOLD_DO_COUNT; pos++) {
        if (holdDoTable[pos] == NULL) {
            break;
        }
    }
    if (pos < MAX_HOLD_DO_COUNT) {
        holdDoTable[pos] = this;
        return;
    }
    else {
        logger->error("Too more holding DO");
    }

}
void DeviceAttribute::waitBoolValue(PMCBOOL value, void (*callback)(DeviceAttribute* var),long waitTime , void (*timeoutCallback)(DeviceAttribute* var) )
{

    if (addr == NULL) {
        return;
    }
    waitValue = value;
    waitCallback = callback;
    waitTimeoutCallback = timeoutCallback;
    setHoldTime = waitTime;
    elapsedHoldTime = 0;
    isWait = TRUE;
    
    int pos = ((unsigned long)this) / 256 % MAX_WAIT_DI_COUNT;
    if (waitDiTable[pos] == NULL) {
        waitDiTable[pos] = this;
        return;
    }
    for (pos = 0; pos < MAX_WAIT_DI_COUNT; pos++) {
        if (waitDiTable[pos] == NULL) {
            break;
        }
    }
    if (pos < MAX_WAIT_DI_COUNT) {
        waitDiTable[pos] = this;
        return;
    }
    else {
        logger->error("Too more waiting DI");
    }

}

void DeviceAttribute::checkHoldDoTable()
{
    for (int index = 0; index < MAX_HOLD_DO_COUNT; index++) {
        DeviceAttribute* var = holdDoTable[index];
        if (var == NULL) {
            continue;
        }
        var->elapsedHoldTime += TASK_TM;
        if (var->elapsedHoldTime >= var->setHoldTime) {            
            var->elapsedHoldTime = var->setHoldTime = 0;
            if (var->isPulse) {               
                var->isPulse = var->isHold = FALSE;
                var->setBoolValue(!var->getBoolValue());
            }
            var->isPulse = var->isHold = FALSE;
            if (var->holdCallback != NULL) {
                var->holdCallback(var);
                var->holdCallback = NULL;
            }
            holdDoTable[index] = NULL;
        }
    }
}

void DeviceAttribute::checkWaitDiTable()
{
    for (int index = 0; index < MAX_WAIT_DI_COUNT; index++) {
        DeviceAttribute* var = waitDiTable[index];
        if (var == NULL) {
            continue;
        }
        if (var->getBoolValue() == var->waitValue) {

            if (var->isWait && var->waitCallback != NULL) {
                var->waitCallback(var);

            }
            var->isWait = FALSE;
            var->waitValue = TRUE;
            var->waitCallback = NULL;
            var->waitTimeoutCallback = NULL;
            var->setHoldTime = var->elapsedHoldTime = 0;
            waitDiTable[index] = NULL;
        }
        else {
            var->elapsedHoldTime += TASK_TM;
            if (var->elapsedHoldTime >= var->setHoldTime) {
                var->elapsedHoldTime = var->setHoldTime;
            }
            if (var->setHoldTime == 0) {
                continue;
            }
            if (var->elapsedHoldTime >= var->setHoldTime) {
                if (var->isWait && var->waitTimeoutCallback != NULL) {
                    var->waitTimeoutCallback(var);
                }
                var->isWait = FALSE;
                var->waitValue = TRUE;
                var->waitCallback = NULL;
                var->waitTimeoutCallback = NULL;
                var->setHoldTime = var->elapsedHoldTime = 0;
                waitDiTable[index] = NULL;
            }
        }

    }
}


