You are on page 1of 17

/****************************************************************************** * * (c) 2012 * ******************************************************************************* * * Disclaimer: This program is an example and should be used as such.

* * * * * Module: * * Function: * * *******************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /****************************************************************************** * Includes ******************************************************************************/ #include <stdio.h> #include <string.h> #include "IEC61850API.h" #include <termios.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/time.h> #include <stdlib.h> #include <time.h> #include "kbhit.h" /****************************************************************************** * Constants This program demonstrates the usage of SystemCORP Embedded Technology IEC61850 DLL stack on a Linux platform. IEC61850Demo.c If you wish to use this program or parts of it in your application, you must validate the code yourself. SystemCORP Embedded Technology Pty Ltd can not be held responsible for the correct functioning or coding of this example. by SystemCORP Embedded Technology Pty Ltd

******************************************************************************/ #define UPDATEMAX update #define DEFAULT_CID_FILE /* Maximum Object types */ #define OBJECT_TYPES /* Total Objects in each object type */ #define OBJECTS #define STRING_ITEM /* Keys */ #define CTRL_C #define CTRL_X #define KEY_PRESS_MIN #define KEY_A #define KEY_B #define KEY_C #define KEY_D #define KEY_E #define KEY_F #define KEY_G #define KEY_H 0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x03 0x18 8 1 2 32 // How many maximum value you want to

"SERVER.CID"

/****************************************************************************** * Type definitions ******************************************************************************/

/* Objects */ typedef struct tag_DK61Object { Unsigned8 /* Object Number */ Unsigned8 /* Object Type */ Unsigned8 /* Object Value */ Unsigned16 /* Object Quality */ struct IEC61850_TimeStamp /* Obect Time */ }tDK61Object; u8ObjectNo; u8ObjectType; u8ObjectValue; u16ObjectQuality; tObjectTime;

/* Object Types */

enum { DIGITAL_INPUT ) DIGITAL_OUTPUT STRING_VALUE }eObjectTypes; enum { DIGINPUT_INDEX DIGOUTPUT_INDEX }eObjectIndex; /* Object Information Index */ enum { VALUE_INDEX QUALITY_INDEX TIME_STAMP_INDEX }eObjectInfoIndex; /* Application Database */ tDK61Object atObj[OBJECT_TYPES][OBJECTS]; char ai8DescString[255] = {0}; IEC61850 Object MyIEC61850Server = NULL; // IEC61850 Server = 3, = 1, = 2, // Value Index // Quality Index = 1, = 0, // Digital Input // Digital Output = 2, = 3, // Digital Output (LED ) // String Read and Write = 1, // Digital Input (DIP Switch

// Time Stamp Index

/****************************************************************************** * Function Prototypes ******************************************************************************/ void CreateApplicationObjects(void); void InputHandler(Unsigned8 u8Input); /* Read Callback function */ int MyReadFunction(void * ptUserData, struct IEC61850_DataAttributeID * ptObjectID, struct IEC61850_DataAttributeData * ptReturnedValue); /* Write Callback function */ int MyWriteFunction(void * ptUserData, struct IEC61850_DataAttributeID * ptObjectID, const struct IEC61850_DataAttributeData * ptNewValue);

int MyOperateFunction(void * ptUserData, struct IEC61850_DataAttributeID * ptControlID, const struct IEC61850_DataAttributeData * ptOperateValue, int iSyncroCheck, int iInterlockCheck);

/*****************************************************************************/ /*****************************************************************************/ int main(int argc, char *argv[0]) { // Declare and Init enum eError Initalise the Error codes struct tServerParam IEC61850 Parameters Integer8 i8RxChar Character struct atUpdateValue[UPDATEMAX]; on Change struct atObject[UPDATEMAX]; char i8CIDFile[80] File Name; Boolean blExit Unsigned8 u8Input = 0; = FALSE; = 0; IEC61850_DataAttributeData // Value to send IEC61850_DataAttributeID_Generic // ID of the Objec = {0}; // ICD // Received IEC61850_ErrorCodes = IEC61850_ERROR_NONE; IEC61850_Parameters = {0}; //

//

system("clear"); printf("\r\n ----------------------------------------------------------------"); // Say Hi printf("\r\n Hello IEC 61850 From SystemCORP Embedded Technology Pty Ltd"); printf("\r\n Usage IEC61850Demo <FILENAME>"); printf("\r\n Please contact support@systemcorp.com.au for further queries"); printf("\r\n ----------------------------------------------------------------"); printf("\r\n IEC61850 Library Version number: %s Build : %s", IEC61850_GetLibraryVersion(), IEC61850_GetLibraryBuildTime());

printf("\r\n ----------------------------------------------------------------"); strcpy(i8CIDFile, (char *)DEFAULT_CID_FILE); // Check if CID file is provided if(argc == 2) { strcpy(i8CIDFile, (char *)argv[1]); }

do { // Set Parameters to create IEC61850 Server tServerParam.ClientServerFlag // This is a Server tServerParam.uiOptions = IEC61850_OPTION_NONE; // See IEC 61850 Server for more options tServerParam.ptReadCallback // Assign Read Callback function tServerParam.ptWriteCallback // Assign Write Callback function tServerParam.ptUpdateCallback // No Update callback for Server tServerParam.ptSelectCallback // Ignoring Select commands MyOperateFunction; = MyReadFunction; = MyWriteFunction; = NULL; = NULL; = IEC61850_SERVER;

tServerParam.ptOperateCallback = // Assign Operate Callback function = NULL;

tServerParam.ptCancelCallback // Ignoring Cancel commands printf("\r\n Server Create");

MyIEC61850Server = IEC61850_Create(&tServerParam,&eError); //Create a server if( MyIEC61850Server== NULL) { printf(" Failed : %d : %s", eError, IEC61850_ErrorString(eError)); break; } fflush(stdout); printf("\r\n Server Load CID : %s", i8CIDFile); eError = IEC61850_LoadSCLFile( MyIEC61850Server,i8CIDFile); // Load in ICD file

if(eError != IEC61850_ERROR_NONE) { printf(" Failed : %d : %s", eError, IEC61850_ErrorString(eError)); break; } fflush(stdout); // Create Application Objects CreateApplicationObjects(); printf("\r\n Server Start"); eError = IEC61850_Start( MyIEC61850Server); // Starts myServer if(eError != IEC61850_ERROR_NONE) { printf(" Failed : %d : %s", eError, IEC61850_ErrorString(eError)); break; } fflush(stdout); set_tty_raw(); /* set up character-at-a-time */ }while(0); /* Dummy while loop to avoid nested If's */ printf("\r\n Press A to H to Toggle Input"); printf("\r\n Controls must be issued from the Client"); printf("\r\n Press CTRL-X to Exit"); printf("\r\n ----------------------------------------------------------------"); printf("\r\n"); fflush(stdout); /* Check for Any Error */ if(eError == IEC61850_ERROR_NONE) { /* Do not Exit */ while(blExit == FALSE) { memset(&atUpdateValue[0], 0, (sizeof(struct IEC61850_DataAttributeData) * UPDATEMAX)); memset(&atObject[0], 0, (sizeof(struct IEC61850_DataAttributeID_Generic) * UPDATEMAX));

i8RxChar = kb_getc(); /* char typed by user? */ if(i8RxChar == 0x00) { continue; } else { switch(i8RxChar) { case CTRL_X : /* Ctrl - X */ case CTRL_C : /* Ctrl - C */ set_tty_cooked(); /* restore normal TTY mode */ blExit = TRUE; break; /* and get out */ case KEY_A : /* A */ case KEY_B : /* B */ case KEY_C : /* C */ case KEY_D : /* D */ case KEY_E : /* E */ case KEY_F : /* F */ case KEY_G : /* G */ case KEY_H : /* H */ u8Input = (i8RxChar - KEY_PRESS_MIN); // Call Input Handler Function InputHandler(u8Input); break; default : break; } }

} } if(MyIEC61850Server != NULL) { /* If any errors in Create and Starting the Server */ /* Stop the Server */ printf("\r\n Server Stop"); eError = IEC61850_Stop(MyIEC61850Server); if(eError != IEC61850_ERROR_NONE) { printf(" Failed : %d : %s", eError, IEC61850_ErrorString(eError)); } fflush(stdout); /* Free all Memory */ printf("\r\n Server Free"); IEC61850_Free(MyIEC61850Server); fflush(stdout); } fflush(stdout); return 0; } /*****************************************************************************/ /*****************************************************************************/ void CreateApplicationObjects(void) { Unsigned8 = 0; Unsigned8 = 0; // No of Objects // Value to send struct IEC61850_DataAttributeData atUpdateValue[UPDATEMAX]; on Change struct IEC61850_DataAttributeID_Generic atObject[UPDATEMAX]; Object Unsigned8 = 0; // Update Count eError u16Field // Field enum IEC61850_ErrorCodes = IEC61850_ERROR_NONE; // Error Unsigned16 = 0; // Object Type Count nu8Objects nu8ObjTypeCnt

// ID of the nu8UpdateCnt

/* Initalise memory */ memset(&atUpdateValue[0], 0, (sizeof(struct IEC61850_DataAttributeData) * UPDATEMAX)); memset(&atObject[0], 0, (sizeof(struct IEC61850_DataAttributeID_Generic) * UPDATEMAX)); strcpy(ai8DescString, "Welcome to SystemCorp IEC61850 Demo");

/* For all Object Types */ for(nu8ObjTypeCnt = 0; nu8ObjTypeCnt < OBJECT_TYPES; nu8ObjTypeCnt++) { /* Each Object within Object Type */ for(nu8Objects = 0; nu8Objects < OBJECTS; { /* Assign Object Number */ atObj[nu8ObjTypeCnt][nu8Objects].u8ObjectNo = (nu8Objects + 1); /* Assign Object Type DIP Switch : 1 , LED : 2 */ atObj[nu8ObjTypeCnt][nu8Objects].u8ObjectType = (nu8ObjTypeCnt + 1); nu8Objects++)

/* Initialise Value to 0 */ atObj[nu8ObjTypeCnt][nu8Objects].u8ObjectValue = 0; atObject[nu8UpdateCnt].Generic_type = IEC61850_DAID_GENERIC; // Set Object Type to Generic atObject[nu8UpdateCnt].uiField1 = (nu8Objects + 1); // Object Number 1); atObject[nu8UpdateCnt].uiField2 = (nu8ObjTypeCnt + // Object Type atObject[nu8UpdateCnt].uiField3 = VALUE_INDEX; atUpdateValue[nu8UpdateCnt].pvData = &atObj[nu8ObjTypeCnt][nu8Objects].u8ObjectValue; atUpdateValue[nu8UpdateCnt].ucType = IEC61850_DATATYPE_BOOLEAN; atUpdateValue[nu8UpdateCnt].uiBitLength = 8; u16Field = atObject[nu8UpdateCnt].uiField2; nu8UpdateCnt++; if(u16Field == DIGITAL_INPUT) {

/* Initialise Quality */ atObj[nu8ObjTypeCnt][nu8Objects].u16ObjectQuality = (IEC61850_QUALITY_FAILURE | IEC61850_QUALITY_INVALID | IEC61850_QUALITY_OLDDATA | IEC61850_QUALITY_QUESTIONABLE );

atObject[nu8UpdateCnt].Generic_type = IEC61850_DAID_GENERIC; // Set Object Type to Generic (nu8Objects + 1); (nu8ObjTypeCnt + 1); QUALITY_INDEX; atUpdateValue[nu8UpdateCnt].pvData = &atObj[nu8ObjTypeCnt][nu8Objects].u16ObjectQuality; atUpdateValue[nu8UpdateCnt].ucType = IEC61850_DATATYPE_QUALITY; atUpdateValue[nu8UpdateCnt].uiBitLength = IEC61850_QUALITY_BITSIZE; nu8UpdateCnt++; /* Initialise Time */ IEC61850_GetTime(&atObj[nu8ObjTypeCnt][nu8Objects].tObjectTime); atObject[nu8UpdateCnt].Generic_type = IEC61850_DAID_GENERIC; // Set Object Type to Generic (nu8Objects + 1); (nu8ObjTypeCnt + 1); TIME_STAMP_INDEX; atUpdateValue[nu8UpdateCnt].pvData = &atObj[nu8ObjTypeCnt][nu8Objects].tObjectTime; atUpdateValue[nu8UpdateCnt].ucType = IEC61850_DATATYPE_TIMESTAMP; atUpdateValue[nu8UpdateCnt].uiBitLength = IEC61850_TIMESTAMP_BITSIZE; nu8UpdateCnt++; } } } if(nu8UpdateCnt != 0) { atObject[nu8UpdateCnt].uiField1 = // Object Number atObject[nu8UpdateCnt].uiField2 = // Object Type atObject[nu8UpdateCnt].uiField3 = atObject[nu8UpdateCnt].uiField1 = // Object Number atObject[nu8UpdateCnt].uiField2 = // Object Type atObject[nu8UpdateCnt].uiField3 =

/* Send Local Value to IEC 61850 Stack */ printf("\r\n IEC61850 Update Application Objects "); eError = IEC61850_Update(MyIEC61850Server, (struct IEC61850_DataAttributeID *)&atObject[0], &atUpdateValue[0], nu8UpdateCnt); if(eError != IEC61850_ERROR_NONE) { printf(" Failed : %d : %s", eError, IEC61850_ErrorString(eError)); } nu8UpdateCnt = 0; } } /*****************************************************************************/ /*****************************************************************************/ int MyReadFunction(void * ptUserData, struct IEC61850_DataAttributeID * ptObjectID, struct IEC61850_DataAttributeData * ptReturnedValue) { struct IEC61850_DataAttributeID_Generic = NULL; enum IEC61850_ErrorCodes = IEC61850_CB_ERROR_NONE; (void)ptUserData; // If not used avoid compiler warning if(ptObjectID->daid_type == IEC61850_DAID_GENERIC) { ptGenObjID = (struct IEC61850_DataAttributeID_Generic *)ptObjectID; /* Each Object within Object type */ /* Check if the Field matches */ if((ptGenObjID->uiField1 == STRING_ITEM) && (ptGenObjID>uiField2 == STRING_VALUE)) { if(ptGenObjID->uiField3 == VALUE_INDEX) { /* Return Value */ memcpy(ptReturnedValue->pvData, ai8DescString,sizeof(ai8DescString)); } } eError *ptGenObjID

return eError; }

/*****************************************************************************/ /*****************************************************************************/ int MyWriteFunction(void * ptUserData, struct IEC61850_DataAttributeID * ptObjectID, const struct IEC61850_DataAttributeData * ptNewValue) { struct IEC61850_DataAttributeID_Generic = NULL; enum IEC61850_ErrorCodes = IEC61850_CB_ERROR_NONE; // Nothing to write (void)ptUserData; // If not used avoid compiler warning eError *ptGenObjID

if(ptObjectID->daid_type == IEC61850_DAID_GENERIC) { ptGenObjID = (struct IEC61850_DataAttributeID_Generic *)ptObjectID; /* Each Object within Object type */ /* Check if the Field matches */ if((ptGenObjID->uiField1 == STRING_ITEM) && (ptGenObjID>uiField2 == STRING_VALUE)) { if(ptGenObjID->uiField3 == VALUE_INDEX) { /* Return Value */ memset(ai8DescString, 0, 255); memcpy(ai8DescString, ptNewValue>pvData, (ptNewValue->uiBitLength/8)); } } } return eError; }

/*****************************************************************************/ /*****************************************************************************/ int MyOperateFunction(void * ptUserData, struct IEC61850_DataAttributeID * ptControlID, const struct IEC61850_DataAttributeData * ptOperateValue, int iSyncroCheck, int iInterlockCheck) { Unsigned8 = 0; Unsigned8 Boolean blLEDChange Boolean = 0; struct IEC61850_DataAttributeID_Generic (void)ptUserData; (void)iSyncroCheck; (void)iInterlockCheck; warning if(ptControlID->daid_type == IEC61850_DAID_GENERIC) { ptGenObjID = (struct IEC61850_DataAttributeID_Generic *)ptControlID; /* Each Object within Object type */ for(u8Objects = 0; u8Objects < OBJECTS; { /* Check if the Field matches */ if((ptGenObjID->uiField1 == atObj[DIGOUTPUT_INDEX][u8Objects].u8ObjectNo) && ((ptGenObjID->uiField2 == atObj[DIGOUTPUT_INDEX][u8Objects].u8ObjectType))) { if(ptGenObjID->uiField3 == VALUE_INDEX) { /* Get the Value of the */ memcpy(&u8LED, ptOperateValue->pvData, sizeof(unsigned char)); if(u8LED != 0) { u8LED = 1; } u8Objects++) *ptGenObjID = NULL; = 0; blFound u8Objects = 0; u8LED

// If not used avoid compiler warning // If not used avoid compiler warning // If not used avoid compiler

/* Check if the LED has changed */ if(atObj[DIGOUTPUT_INDEX][u8Objects].u8ObjectValue != u8LED) { /* Set the Value */ atObj[DIGOUTPUT_INDEX][u8Objects].u8ObjectValue = u8LED; blLEDChange = 1; blFound = 1; } } } if(blFound) break; } /* LED Changed */ if(blLEDChange) { u8LED = 0; /* Get all the values of the LED */ printf("\r\n ----------------------------------------------------------------"); printf("\r\n \t\t\t\t\tOUTPUT"); printf("\r\n \t\t\t\t\t------"); printf("\r\n\tA\tB\tC\tD\tE\tF\tG\tH\r\n"); for(u8Objects = 0; u8Objects < OBJECTS; u8Objects++) { printf("\t%u",atObj[DIGOUTPUT_INDEX][u8Objects].u8ObjectValue); /* Form Byte to Output */ u8LED = (u8LED | (atObj[DIGOUTPUT_INDEX][u8Objects].u8ObjectValue << u8Objects)); } printf("\r\n ----------------------------------------------------------------"); } } return IEC61850_COMMAND_ERROR_NONE; } /*****************************************************************************/

/*****************************************************************************/ void InputHandler(Unsigned8 u8Input) { Unsigned8 = 0; // Total Objects // Value to send struct IEC61850_DataAttributeData atUpdateValue[UPDATEMAX]; on Change struct IEC61850_DataAttributeID_Generic atObject[UPDATEMAX]; Object Unsigned16 u16Quality Unsigned8 = 0; struct IEC61850_TimeStamp = {0}; enum IEC61850_ErrorCodes = IEC61850_ERROR_NONE; eError tIEC61850Time = 0; // Local Quality nu8UpdateCnt nu8Objects

// ID of the

nu8UpdateCnt = 0; memset(&atUpdateValue[0], 0, (sizeof(struct IEC61850_DataAttributeData) * UPDATEMAX)); memset(&atObject[0], 0, (sizeof(struct IEC61850_DataAttributeID_Generic) * UPDATEMAX)); nu8Objects = u8Input - 1; if(atObj[DIGINPUT_INDEX][nu8Objects].u8ObjectValue == 1) { atObj[DIGINPUT_INDEX][nu8Objects].u8ObjectValue } else { atObj[DIGINPUT_INDEX][nu8Objects].u8ObjectValue } printf("\r\n ----------------------------------------------------------------"); printf("\r\n \t\t\t\t\tINPUT"); = 1; = 0;

printf("\r\n \t\t\t\t\t-----"); printf("\r\n\tA\tB\tC\tD\tE\tF\tG\tH\r\n"); for(nu8Objects = 0; nu8Objects < OBJECTS; { printf("\t%u",atObj[DIGINPUT_INDEX][nu8Objects].u8ObjectValue); } printf("\r\n ----------------------------------------------------------------"); nu8Objects = u8Input - 1; nu8Objects++)

/* Object Value */ /* Object Value Index */ atObject[nu8UpdateCnt].Generic_type = IEC61850_DAID_GENERIC; // Set Object Type to Generic atObject[nu8UpdateCnt].uiField1 = nu8Objects + 1; // Object Number atObject[nu8UpdateCnt].uiField2 = DIGITAL_INPUT; atObject[nu8UpdateCnt].uiField3 = VALUE_INDEX; atUpdateValue[nu8UpdateCnt].pvData = &atObj[DIGINPUT_INDEX][nu8Objects].u8ObjectValue; atUpdateValue[nu8UpdateCnt].ucType = IEC61850_DATATYPE_BOOLEAN; atUpdateValue[nu8UpdateCnt].uiBitLength = 8; /* Send Update for Value */ nu8UpdateCnt++; u16Quality = 0; /* Object Quality */ /* No way to determine if DIP Switch failed so */ atObject[nu8UpdateCnt].Generic_type = IEC61850_DAID_GENERIC; // Set Object Type to Generic atObject[nu8UpdateCnt].uiField1 = nu8Objects + 1; // Object Number atObject[nu8UpdateCnt].uiField2 = DIGITAL_INPUT; atObject[nu8UpdateCnt].uiField3 = QUALITY_INDEX; /* Update Quality in the database */ atObj[DIGINPUT_INDEX][nu8Objects].u16ObjectQuality atUpdateValue[nu8UpdateCnt].pvData = &atObj[DIGINPUT_INDEX][nu8Objects].u16ObjectQuality; atUpdateValue[nu8UpdateCnt].ucType = IEC61850_DATATYPE_QUALITY; atUpdateValue[nu8UpdateCnt].uiBitLength = IEC61850_QUALITY_BITSIZE; /* Send Update for Quality */ = u16Quality; // Object Type // Object Type

nu8UpdateCnt++; /* Send Time */ /* Convert to 61850 Time */ IEC61850_GetTime(&tIEC61850Time); atObject[nu8UpdateCnt].Generic_type = IEC61850_DAID_GENERIC; // Set Object Type to Generic atObject[nu8UpdateCnt].uiField1 = nu8Objects + 1; // Object Number atObject[nu8UpdateCnt].uiField2 = DIGITAL_INPUT; /* Update Time in the database */ memcpy(&atObj[DIGINPUT_INDEX][nu8Objects].tObjectTime, &tIEC61850Time, sizeof(struct IEC61850_TimeStamp)); atUpdateValue[nu8UpdateCnt].pvData = &atObj[DIGINPUT_INDEX][nu8Objects].tObjectTime; atUpdateValue[nu8UpdateCnt].ucType = IEC61850_DATATYPE_TIMESTAMP; atUpdateValue[nu8UpdateCnt].uiBitLength = IEC61850_TIMESTAMP_BITSIZE; /* Send Update for Time Stamp */ nu8UpdateCnt++; if(nu8UpdateCnt != 0) { eError = IEC61850_Update(MyIEC61850Server, (struct IEC61850_DataAttributeID *)&atObject[0], &atUpdateValue[0], nu8UpdateCnt); if(eError != IEC61850_ERROR_NONE) { printf(" Update Failed : %d : %s ",eError, IEC61850_ErrorString(eError)); } nu8UpdateCnt = 0; } } // Object Type atObject[nu8UpdateCnt].uiField3 = TIME_STAMP_INDEX;

You might also like