import ctypes 
import time
import struct
import sys
import keyboard
from pyiec104.iec104api import *

# enbale to view traffic
VIEW_TRAFFIC = 1

# print the struct sIEC104DataAttributeID and sIEC104DataAttributeData
def vPrintDataInformation(psIEC104DataAttributeID , psIEC104DataAttributeData ):
    print(f" IP Address {psIEC104DataAttributeID.contents.ai8IPAddress}")
    print(f" Port Number {psIEC104DataAttributeID.contents.u16PortNumber}")
    print(f" Common Address {psIEC104DataAttributeID.contents.u16CommonAddress}")
    print(f" Typeid ID is {psIEC104DataAttributeID.contents.eTypeID} IOA   {psIEC104DataAttributeID.contents.u32IOA}")
    print(f" Datatype->{psIEC104DataAttributeData.contents.eDataType} Datasize->{ psIEC104DataAttributeData.contents.eDataSize}" )

    if(psIEC104DataAttributeData.contents.tQuality) != eIEC870QualityFlags.GD :
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.IV) == eIEC870QualityFlags.IV:
            print(" IEC_INVALID_FLAG")
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.NT) == eIEC870QualityFlags.NT:
             print(" IEC_NONTOPICAL_FLAG")
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.SB) == eIEC870QualityFlags.SB:
             print(" IEC_SUBSTITUTED_FLAG")
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.BL) == eIEC870QualityFlags.BL:
             print(" IEC_BLOCKED_FLAG")

    data_type = psIEC104DataAttributeData.contents.eDataType

    if data_type in (eDataTypes.SINGLE_POINT_DATA, eDataTypes.DOUBLE_POINT_DATA, eDataTypes.UNSIGNED_BYTE_DATA):
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 1))
        u8data = struct.unpack('B', data)[0] 
        print(f" Data : {u8data}")

    elif data_type == eDataTypes.SIGNED_BYTE_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 1))
        i8data = struct.unpack('b', data)[0]        
        print(f" Data : {i8data}")

    elif data_type == eDataTypes.UNSIGNED_WORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 2))
        u16data = struct.unpack('H', data)[0]        
        print(f" Data : {u16data}")

    elif data_type == eDataTypes.SIGNED_WORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 2))
        i16data = struct.unpack('h', data)[0]        
        print(f" Data : {i16data}")

    elif data_type == eDataTypes.UNSIGNED_DWORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 4))
        u32data = struct.unpack('I', data)[0]        
        print(f" Data : {u32data}")


    elif data_type == eDataTypes.SIGNED_DWORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 4))
        i32data = struct.unpack('i', data)[0]        
        print(f" Data : {i32data}")

    elif data_type == eDataTypes.FLOAT32_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 4))
        f32data = struct.unpack('f', data)[0] 
        print(f" Data : {f32data:.3f}")

    if psIEC104DataAttributeData.contents.sTimeStamp.u16Year != 0:
        print(f" Date : {psIEC104DataAttributeData.contents.sTimeStamp.u8Day:02}-{psIEC104DataAttributeData.contents.sTimeStamp.u8Month:02}-{psIEC104DataAttributeData.contents.sTimeStamp.u16Year:04}  DOW -{psIEC104DataAttributeData.contents.sTimeStamp.u8DayoftheWeek}")
        print(f" Time : {psIEC104DataAttributeData.contents.sTimeStamp.u8Hour:02}:{psIEC104DataAttributeData.contents.sTimeStamp.u8Minute:02}:{psIEC104DataAttributeData.contents.sTimeStamp.u8Seconds:02}:{psIEC104DataAttributeData.contents.sTimeStamp.u16MilliSeconds:03}")

# iec104 read callback 
def cbRead(u16ObjectId, psIEC104DataAttributeID, psIEC104DataAttributeData , psReadParams, ptErrorValue):
    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbRead() called - Read Command from IEC104 client")
    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psReadParams.contents.u8OrginatorAddress}"
    print(message)
    return i16rErrorCode

# iec104 write callback 
def cbWrite(u16ObjectId, psIEC104DataAttributeID, psIEC104DataAttributeData, psIEC104WriteParameters, ptErrorValue):
    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbWrite() called - Clock Sync Command from IEC104 client")
    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psIEC104WriteParameters.contents.u8OrginatorAddress}"
    print(message)
    return i16rErrorCode

# Freeze Callback
def cbFreeze( u16ObjectId, eCounterFreeze, psIEC104DataAttributeID , psIEC104DataAttributeData , psIEC104WriteParameters , ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbFreeze() called - freeze Command from IEC104 client")  

    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psIEC104WriteParameters.contents.u8OrginatorAddress}"
    print(message)

    print(f" Command Typeid : {psIEC104DataAttributeID.contents.eTypeID}")
    print(f" COT : {psIEC104WriteParameters.contents.eCause}")   

    return i16rErrorCode

# Select callback
def cbSelect(u16ObjectId, psIEC104DataAttributeID , psIEC104DataAttributeData ,psIEC104CommandParameters , ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbSelect() called - select Command from IEC104 client")

    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psIEC104CommandParameters.contents.u8OrginatorAddress}"
    print(message)
    
    print(f" Qualifier : {psIEC104CommandParameters.contents.eQOCQU}" )
    print(f" Pulse Duration : {psIEC104CommandParameters.contents.u32PulseDuration}" )

    return i16rErrorCode

# Operate callback
def cbOperate(u16ObjectId, psIEC104DataAttributeID , psIEC104DataAttributeData ,psIEC104CommandParameters , ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbOperate() called - operate Command from IEC104 client")

    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psIEC104CommandParameters.contents.u8OrginatorAddress}"
    print(message)

    print(f" Qualifier : {psIEC104CommandParameters.contents.eQOCQU}")
    print(f" Pulse Duration : {psIEC104CommandParameters.contents.u32PulseDuration}")
   

    return i16rErrorCode

# Operate pulse end callback
def cbpulseend(u16ObjectId, psIEC104DataAttributeID , psIEC104DataAttributeData ,psIEC104CommandParameters , ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbpulseend() called - pulse end Command from IEC104 client")


    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psIEC104CommandParameters.contents.u8OrginatorAddress}"
    print(message)

    print(f" Qualifier : {psIEC104CommandParameters.contents.eQOCQU}")
    print(f" Pulse Duration : {psIEC104CommandParameters.contents.u32PulseDuration}")
   

    return i16rErrorCode

# Cancel callback
def cbCancel(u16ObjectId, eOperation, psIEC104DataAttributeID , psIEC104DataAttributeData ,psIEC104CommandParameters , ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbCancel() called - cancel Command from IEC104 client")

    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psIEC104CommandParameters.contents.u8OrginatorAddress}"
    print(message)


    if eOperation   ==  eOperationFlag.OPERATE:
        print(" Operate operation to be cancel")
    elif eOperation   ==  eOperationFlag.SELECT:
        print(" Select operation to cancel")

    print(f" Qualifier : {psIEC104CommandParameters.contents.eQOCQU}")
    print(f" Pulse Duration :{psIEC104CommandParameters.contents.u32PulseDuration}")
    

    return i16rErrorCode

# Parameteract callback
def cbParameterAct(u16ObjectId, psIEC104DataAttributeID , psIEC104DataAttributeData , psIEC104ParameterActParameters , ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbParameterAct called - parameter act Command from IEC104 client")

    print(" Server ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f"Orginator Address {psIEC104ParameterActParameters.contents.u8OrginatorAddress}"
    print(message)

    print(f" Qualifier of Parameter Activation/Kind of Parameter - {psIEC104ParameterActParameters.contents.u8QPA}")
    

    return i16rErrorCode

# Debug callback
def cbDebug(u16ObjectId,  psIEC104DebugData , ptErrorValue):
    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0 

    u16nav = ctypes.c_ushort()
    u16nav = 0
    
   
    #printf(" cbDebug() called");
    
    print(f"{psIEC104DebugData.contents.sTimeStamp.u8Hour}:{psIEC104DebugData.contents.sTimeStamp.u8Minute}:{psIEC104DebugData.contents.sTimeStamp.u8Seconds} Server ID: {u16ObjectId}", end='')

    if (psIEC104DebugData.contents.u32DebugOptions & eDebugOptionsFlag.DEBUG_OPTION_TX) == eDebugOptionsFlag.DEBUG_OPTION_TX:
        print(f" IP {psIEC104DebugData.contents.ai8IPAddress} Port {psIEC104DebugData.contents.u16PortNumber}", end='')
        print(f" -> ", end='')
        
        #print(f"u16TxCount - {psIEC104DebugData.contents.u16TxCount} len aaray - {len(psIEC104DebugData.contents.au8TxData)}")
        

        for u16nav in range(psIEC104DebugData.contents.u16TxCount):
            print(f" {psIEC104DebugData.contents.au8TxData[u16nav]:02x}", end='')
        

        
    if (psIEC104DebugData.contents.u32DebugOptions & eDebugOptionsFlag.DEBUG_OPTION_RX) == eDebugOptionsFlag.DEBUG_OPTION_RX:
        print(f" IP {psIEC104DebugData.contents.ai8IPAddress} Port {psIEC104DebugData.contents.u16PortNumber}", end='')
        print(" <-", end='')

        #print(f"u16RxCount - {psIEC104DebugData.contents.u16RxCount} len aaray - {len(psIEC104DebugData.contents.au8RxData)}")

        
        for u16nav in range(psIEC104DebugData.contents.u16RxCount):
            print(f" {psIEC104DebugData.contents.au8RxData[u16nav]:02x}", end='')
        

        
    if (psIEC104DebugData.contents.u32DebugOptions & eDebugOptionsFlag.DEBUG_OPTION_ERROR) == eDebugOptionsFlag.DEBUG_OPTION_ERROR:
        print(f" Error message {psIEC104DebugData.contents.au8ErrorMessage}")
        print(f" ErrorCode {psIEC104DebugData.contents.iErrorCode}")
        print(f" ErrorValue {psIEC104DebugData.contents.tErrorvalue}")

    print("", flush=True)

    
    return i16rErrorCode

# Server Status Callback
def cbServerStatus(u16ObjectId, psIEC104ServerConnectionID , peSat, ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbServerStatus called ")

    print(" Server ID : %u" % u16ObjectId)

    if peSat.contents.value == eStatus.CONNECTED:
        print(" Status - Connected")
    else:
        print(" Status - Disconnected")
    
    print(f" Source IP Address %s Port %u " % (psIEC104ServerConnectionID.contents.ai8SourceIPAddress, psIEC104ServerConnectionID.contents.u16SourcePortNumber))
    print(f" Remote IP Address %s Port %u " % (psIEC104ServerConnectionID.contents.ai8RemoteIPAddress, psIEC104ServerConnectionID.contents.u16RemotePortNumber))

    return i16rErrorCode

# print error code and description
def errorcodestring(errorcode):
    sIEC104ErrorCodeDes = sIEC104ErrorCode()
    sIEC104ErrorCodeDes.iErrorCode = errorcode
    iec104_lib.IEC104ErrorCodeString(sIEC104ErrorCodeDes)
    return sIEC104ErrorCodeDes.LongDes.decode("utf-8")

# print error value and description
def errorvaluestring(errorvalue):
    sIEC104ErrorValueDes = sIEC104ErrorValue()
    sIEC104ErrorValueDes.iErrorValue = errorvalue   
    iec104_lib.IEC104ErrorValueString(sIEC104ErrorValueDes)
    return sIEC104ErrorValueDes.LongDes.decode("utf-8")

# update particular typeid and IOA FROM USER INPUT
def update(myServer):

    i16ErrorCode = ctypes.c_short()
    tErrorValue =  ctypes.c_short()
    
    print("UPDATE CALLED")
    while True:
        try:
            u32ioa = ctypes.c_uint32(int(input("MeasuredFloat(M_ME_TF_1) Enter Information object address(IOA)- 100 to 109 ")))
        except ValueError:
            print("Please enter a number 100 to 109")
        else:
            break

    while True:
        try:
            f32value = ctypes.c_float(float(input("Enter update float value: ")))
        except ValueError:
            print("Please enter a float number ")
        else:
            break

   
    psDAID = sIEC104DataAttributeID()
    psNewValue  = sIEC104DataAttributeData()

    psDAID.u16CommonAddress                     =  1
    psDAID.eTypeID                              =  eIEC870TypeID.M_ME_TF_1
    psDAID.u32IOA                               =   u32ioa
    psDAID.pvUserData                           =   None
    psNewValue.tQuality                         =   eIEC870QualityFlags.GD

    #psNewValue.pvData                           =   ctypes.cast(ctypes.pointer(f32value),ctypes.c_void_p)
    psNewValue.pvData = ctypes.c_void_p(ctypes.addressof(f32value))
    
    psNewValue.eDataType                        =   eDataTypes.FLOAT32_DATA
    psNewValue.eDataSize                        =   eDataSizes.FLOAT32_SIZE

    now = time.time()
    timeinfo = time.localtime(now)
    
    #current date
    psNewValue.sTimeStamp.u8Day = timeinfo.tm_mday
    psNewValue.sTimeStamp.u8Month = timeinfo.tm_mon
    psNewValue.sTimeStamp.u16Year = timeinfo.tm_year 

    psNewValue.sTimeStamp.u8Hour = timeinfo.tm_hour
    psNewValue.sTimeStamp.u8Minute = timeinfo.tm_min
    psNewValue.sTimeStamp.u8Seconds = timeinfo.tm_sec
    psNewValue.sTimeStamp.u16MilliSeconds = 0
    psNewValue.sTimeStamp.u16MicroSeconds = 0
    psNewValue.sTimeStamp.i8DSTTime = 0
    psNewValue.sTimeStamp.u8DayoftheWeek = 4
    psNewValue.bTimeInvalid = False

    #printf(" update float value %f",f32data);
    # Update server
   
    i16ErrorCode = iec104_lib.IEC104Update(myServer, True,ctypes.byref(psDAID),ctypes.byref(psNewValue),1,ctypes.byref((tErrorValue)))
    if i16ErrorCode != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Update() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)     

    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Update() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 


# main program
def main():
    print(" \t\t**** IEC 60870-5-104 Protocol Server Library Test ****")
    # Check library version against the library header file
    if iec104_lib.IEC104GetLibraryVersion().decode("utf-8") != IEC104_VERSION:
        print(" Error: Version Number Mismatch")
        print(" Library Version is  : {}".format(iec104_lib.IEC104GetLibraryVersion().decode("utf-8")))
        print(" The Header Version used is : {}".format(IEC104_VERSION))
        print("")
        input(" Press Enter to free IEC 104 Server object")
        exit(0)

    print("Library Version is : {}".format(iec104_lib.IEC104GetLibraryVersion().decode("utf-8")))
    print("Library Build on   : {}".format(iec104_lib.IEC104GetLibraryBuildTime().decode("utf-8")))
    print("Library License Information   : {}\r\n".format(iec104_lib.IEC104GetLibraryLicenseInfo().decode("utf-8")))

    i16ErrorCode = ctypes.c_short()
    tErrorValue =  ctypes.c_short()
    sParameters = sIEC104Parameters()

   

    # Initialize IEC 60870-5-104 Server object parameters
    sParameters.eAppFlag          =  eApplicationFlag.APP_SERVER        # This is a IEC104 Server      
    sParameters.ptReadCallback    = IEC104ReadCallback(cbRead)               # Read Callback
    sParameters.ptWriteCallback   = IEC104WriteCallback(cbWrite)                # Write Callback
    sParameters.ptUpdateCallback  = ctypes.cast(None,IEC104UpdateCallback) #IEC104UpdateCallback(0)                 # Update Callback
    sParameters.ptSelectCallback  = IEC104ControlSelectCallback(cbSelect)               # Select Callback
    sParameters.ptOperateCallback = IEC104ControlOperateCallback(cbOperate)              # Operate Callback
    sParameters.ptCancelCallback  = IEC104ControlCancelCallback(cbCancel)              # Cancel Callback
    sParameters.ptFreezeCallback  = IEC104ControlFreezeCallback(cbFreeze)              # Freeze Callback
    sParameters.ptDebugCallback   = IEC104DebugMessageCallback(cbDebug)                # Debug Callback
    sParameters.ptPulseEndActTermCallback = IEC104ControlPulseEndActTermCallback(cbpulseend)    # pulse end callback
    sParameters.ptParameterActCallback = IEC104ParameterActCallback(cbParameterAct)   # Parameter activation callback
    sParameters.ptServerStatusCallback = IEC104ServerStatusCallback(cbServerStatus)   # server status callback
    sParameters.ptDirectoryCallback    = IEC104DirectoryCallback(0)              # Directory Callback
    sParameters.ptClientStatusCallback   = IEC104ClientStatusCallback(0)           # client connection status Callback
    sParameters.u32Options        = 0
    sParameters.u16ObjectId				= 1				#Server ID which used in callbacks to identify the iec 104 server object   


    # Create a server object

    myServer =  iec104_lib.IEC104Create(ctypes.byref(sParameters), ctypes.byref((i16ErrorCode)), ctypes.byref((tErrorValue)))
    if i16ErrorCode.value != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Create() failed: {i16ErrorCode.value} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)    
        exit(0) 
    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Create() success: {i16ErrorCode.value} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 

    while(True):

        sIEC104Config = sIEC104ConfigurationParameters()

        sIEC104Config.sServerSet.sServerConParameters.ai8SourceIPAddress = "127.0.0.1".encode('utf-8') # own server bind address
        sIEC104Config.sServerSet.sServerConParameters.u16PortNumber             =   2404

        sIEC104Config.sServerSet.sServerConParameters.u16MaxNumberofRemoteConnection = 1

        arraypointer = ( sIEC104ServerRemoteIPAddressList * sIEC104Config.sServerSet.sServerConParameters.u16MaxNumberofRemoteConnection)()

        sIEC104Config.sServerSet.sServerConParameters.psServerRemoteIPAddressList = ctypes.cast(arraypointer, ctypes.POINTER(sIEC104ServerRemoteIPAddressList))


        arraypointer[0].ai8RemoteIPAddress = "0.0.0.0".encode('utf-8')

        
        sIEC104Config.sServerSet.sServerConParameters.u16PortNumber = 2404
        sIEC104Config.sServerSet.sServerConParameters.i16k = 12
        sIEC104Config.sServerSet.sServerConParameters.i16w = 8
        sIEC104Config.sServerSet.sServerConParameters.u8t0 = 30
        sIEC104Config.sServerSet.sServerConParameters.u8t1 = 15
        sIEC104Config.sServerSet.sServerConParameters.u8t2 = 10
        sIEC104Config.sServerSet.sServerConParameters.u16t3 = 20
        sIEC104Config.sServerSet.sServerConParameters.u16EventBufferSize = 50
        sIEC104Config.sServerSet.sServerConParameters.u32ClockSyncPeriod = 0
        sIEC104Config.sServerSet.sServerConParameters.bGenerateACTTERMrespond = True
        sIEC104Config.sServerSet.sServerConParameters.bEnableRedundancy = False
        sIEC104Config.sServerSet.sServerConParameters.ai8RedundSourceIPAddress = "127.0.0.1".encode('utf-8')
        sIEC104Config.sServerSet.sServerConParameters.ai8RedundRemoteIPAddress = "0.0.0.0".encode('utf-8')
        sIEC104Config.sServerSet.sServerConParameters.u16RedundPortNumber = 2400

        sIEC104Config.sServerSet.bServerInitiateTCPconnection = False; # In this server will connect to client, so always FALSE.
        sIEC104Config.sServerSet.u16LongPulseTime = 20000
        sIEC104Config.sServerSet.u16ShortPulseTime = 5000

        sIEC104Config.sServerSet.u8TotalNumberofStations    =   1
        sIEC104Config.sServerSet.au16CommonAddress[0]   =   1
        sIEC104Config.sServerSet.au16CommonAddress[1]   =   0
        sIEC104Config.sServerSet.au16CommonAddress[2]   =   0
        sIEC104Config.sServerSet.au16CommonAddress[3]   =   0
        sIEC104Config.sServerSet.au16CommonAddress[4]   =   0


        sIEC104Config.sServerSet.sServerConParameters.bGenerateACTTERMrespond   =   True
        sIEC104Config.sServerSet.bEnableDoubleTransmission = False

        sIEC104Config.sServerSet.bEnablefileftransfer   = False
        sIEC104Config.sServerSet.ai8FileTransferDirPath = "\\FileTransferServer".encode('utf-8')
        sIEC104Config.sServerSet.u16MaxFilesInDirectory     =   10

        sIEC104Config.sServerSet.sServerConParameters.bEnableRedundancy =   False
        sIEC104Config.sServerSet.sServerConParameters.ai8RedundSourceIPAddress = "127.0.0.1".encode('utf-8')
        sIEC104Config.sServerSet.sServerConParameters.ai8RedundRemoteIPAddress = "0.0.0.0".encode('utf-8')
        sIEC104Config.sServerSet.sServerConParameters.u16RedundPortNumber      =   2400

        sIEC104Config.sServerSet.bTransmitSpontMeasuredValue = True
        sIEC104Config.sServerSet.bTranmitInterrogationMeasuredValue = True
        sIEC104Config.sServerSet.bTransmitBackScanMeasuredValue = True
        sIEC104Config.sServerSet.eCOTsize = eCauseofTransmissionSize.COT_TWO_BYTE
        sIEC104Config.sServerSet.bSequencebitSet = False

        
        # Debug option settings
        if  'VIEW_TRAFFIC' in globals():
            sIEC104Config.sServerSet.sDebug.u32DebugOptions   =   (eDebugOptionsFlag.DEBUG_OPTION_RX | eDebugOptionsFlag.DEBUG_OPTION_TX)
        else:
            sIEC104Config.sServerSet.sDebug.u32DebugOptions  =   0


        
        sIEC104Config.sServerSet.benabaleUTCtime =  False
        sIEC104Config.sServerSet.u8InitialdatabaseQualityFlag   =   eIEC870QualityFlags.GD     

        sIEC104Config.sServerSet.bServerInitiateTCPconnection = False


        # configure number of point in server database
        sIEC104Config.sServerSet.u16NoofObject              =   2        # Define number of objects
        sIEC104Config.sServerSet.psIEC104Objects  = ( sIEC104Object * sIEC104Config.sServerSet.u16NoofObject)()

        sIEC104Config.sServerSet.psIEC104Objects[0].ai8Name = "M_ME_TF_1 100-109".encode('utf-8')
        sIEC104Config.sServerSet.psIEC104Objects[0].eTypeID     =  eIEC870TypeID.M_ME_TF_1
        sIEC104Config.sServerSet.psIEC104Objects[0].u32IOA          = 100
        sIEC104Config.sServerSet.psIEC104Objects[0].u16Range        = 10
        sIEC104Config.sServerSet.psIEC104Objects[0].eIntroCOT       = eIEC870COTCause.INRO6
        sIEC104Config.sServerSet.psIEC104Objects[0].eControlModel   =   eControlModelConfig.STATUS_ONLY
        sIEC104Config.sServerSet.psIEC104Objects[0].u32SBOTimeOut   =   0
        sIEC104Config.sServerSet.psIEC104Objects[0].u16CommonAddress    =   1

        #Second object detail
        sIEC104Config.sServerSet.psIEC104Objects[1].ai8Name = "C_SE_TC_1".encode('utf-8')
        sIEC104Config.sServerSet.psIEC104Objects[1].eTypeID     =  eIEC870TypeID.C_SE_TC_1
        sIEC104Config.sServerSet.psIEC104Objects[1].u32IOA          = 100
        sIEC104Config.sServerSet.psIEC104Objects[1].eIntroCOT       = eIEC870COTCause.NOTUSED
        sIEC104Config.sServerSet.psIEC104Objects[1].u16Range        = 10
        sIEC104Config.sServerSet.psIEC104Objects[1].eControlModel  = eControlModelConfig.DIRECT_OPERATE
        sIEC104Config.sServerSet.psIEC104Objects[1].u32SBOTimeOut   = 0
        sIEC104Config.sServerSet.psIEC104Objects[1].u16CommonAddress    =   1

            

        i16ErrorCode =  iec104_lib.IEC104LoadConfiguration(myServer, ctypes.byref(sIEC104Config), ctypes.byref((tErrorValue)))
        if i16ErrorCode != 0:
            message = f"IEC 60870-5-104 Library API Function - IEC104IEC104LoadConfiguration() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message)    
            break

        else:
            message = f"IEC 60870-5-104 Library API Function - IEC104IEC104LoadConfiguration() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message) 



        i16ErrorCode =  iec104_lib.IEC104Start(myServer, ctypes.byref((tErrorValue)))
        if i16ErrorCode != 0:
            message = f"IEC 60870-5-104 Library API Function - IEC104Start() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message)    
            break

        else:
            message = f"IEC 60870-5-104 Library API Function - IEC104Start() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message) 

        print("press x to exit")

        while(True):
            if keyboard.is_pressed('x'):
                print("x pressed, exiting main program")
                keyboard.release('x')
                time.sleep(0.1)
                break
            elif keyboard.is_pressed('u'):
                print("u pressed, update called")
                keyboard.release('u')
                time.sleep(0.1)
                update(myServer)

            #Xprint("sleep called")
            time.sleep(50 /1000)

        break
            
            

      


    # iec 104 stop api fn
    i16ErrorCode =  iec104_lib.IEC104Stop(myServer, ctypes.byref((tErrorValue)))
    if i16ErrorCode != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Stop() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)        
    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Stop() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 


     # iec 104 free api fn
    i16ErrorCode =  iec104_lib.IEC104Free(myServer, ctypes.byref((tErrorValue)))
    if i16ErrorCode != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Free() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)    
    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Free() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 


    print("Exiting the program...")
    

if __name__ == "__main__":
    main()