The goal of this software is to automatically generate C/C++ code which reads and writes GOOSE and Sampled Value packets. Any valid IEC 61850 Substation Configuration Description (SCD) file, describing GOOSE and/or SV communications, can be used as the input. The output code is lightweight and platform-independent, so it can run on a variety of devices, including low-cost microcontrollers. It\'s ideal for rapid-prototyping new protection and control systems that require communications. This mbed project is a simple example of this functionality. Other code: https://github.com/stevenblair/rapid61850 Project homepage: http://personal.strath.ac.uk/steven.m.blair/

ctypes.c

Committer:
sblair
Date:
2011-10-07
Revision:
1:9399d44c2b1a
Parent:
0:230c10b228ea

File content as of revision 1:9399d44c2b1a:

/**
 * Rapid-prototyping protection schemes with IEC 61850
 *
 * Copyright (c) 2011 Steven Blair
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "ctypes.h"
#include "gse.h"
#include "sv.h"
#ifdef TIMESTAMP_SUPPORTED
#include <sys\time.h>
#endif

unsigned char    LOCAL_MAC_ADDRESS[] = {0x01, 0x0C, 0xCD, 0x01, 0x00, 0x02};
unsigned char    endian_buf[16] = {0};


int ber_integer_length(void *value, int maxLength) {
    netmemcpy(endian_buf, value, maxLength);    // ensure bytes are in big-endian order

    unsigned char *buf = endian_buf;
    unsigned char *end1 = buf + maxLength - 1;
    int shift = 0;

    /* Compute the number of superfluous leading bytes */
    for(; buf < end1; buf++) {
        /*
         * If the contents octets of an integer value encoding
         * consist of more than one octet, then the bits of the
         * first octet and bit 8 of the second octet:
         * a) shall not all be ones; and
         * b) shall not all be zero.
         */
        switch(*buf) {
        case 0x00:
            if((buf[1] & 0x80) == 0) {
                continue;
            }
            break;
        case 0xff:
            if((buf[1] & 0x80)) {
                continue;
            }
            break;
        }
        break;
    }

    shift = buf - endian_buf;

    return maxLength - shift;
}

int ber_encode_integer(unsigned char *bufDst, void *value, int maxLength) {
    netmemcpy(endian_buf, value, maxLength);    // ensure bytes are in big-endian order

    unsigned char *buf = endian_buf;
    unsigned char *end1 = buf + maxLength - 1;
    int shift = 0;

    /* Compute the number of superfluous leading bytes */
    for(; buf < end1; buf++) {
        /*
         * If the contents octets of an integer value encoding
         * consist of more than one octet, then the bits of the
         * first octet and bit 8 of the second octet:
         * a) shall not all be ones; and
         * b) shall not all be zero.
         */
        switch(*buf) {
        case 0x00:
            if((buf[1] & 0x80) == 0) {
                continue;
            }
            break;
        case 0xff:
            if((buf[1] & 0x80)) {
                continue;
            }
            break;
        }
        break;
    }

    shift = buf - endian_buf;

    /* Remove leading superfluous bytes from the integer */
    //if (shift) {
        unsigned char *nb = endian_buf;
        unsigned char *end;

        maxLength -= shift;    /* New size, minus bad bytes */
        end = nb + maxLength;

        int i = 0;
        for(; nb < end; nb++, buf++, i++) {
            //*nb = *buf;
            bufDst[i] = *buf;
        }

        //printf("st->size: %d, end: %d\n", st->size, *end);
    //}

    //memcpy((unsigned char *) bufDst, (const unsigned char *) endian_buf, maxLength);

    //printf("%d  %d  %d\n", (int) *st->buf, st->size, shift);
    return maxLength;
}

void ber_decode_integer(unsigned char *buf, int length, void *value, int maxLength) {
    unsigned char padding = (buf[0] & 0x80) ? 0xFF : 0x00;
    int i = 0;
    unsigned char *dest = (unsigned char *) value;

    for (i = maxLength - 1; i >= 0; i--) {
        if ((i + length) < maxLength) {
            endian_buf[i] = padding;
        }
        else {
            endian_buf[i] = buf[i - (maxLength - length)];
        }
    }

    netmemcpy(dest, endian_buf, maxLength);
}

//TODO need cast to, for example, (unsigned char *) for calls to reversememcpy()?

// a simple memcpy implementation, that reverses endian-ness
void reversememcpy(unsigned char *dst, const unsigned char *src, unsigned int len) {
    while (len--) {
        *dst++ = src[len];
    }
}

void setTimestamp(CTYPE_TIMESTAMP *dest) {
#ifdef TIMESTAMP_SUPPORTED
    unsigned char *buf = (unsigned char *) dest;
    struct timeval tv;
    CTYPE_INT32U frac = 0;

    gettimeofday(&tv, NULL);
    frac = (CTYPE_INT32U) ((float) tv.tv_usec * 4294.967296);    // * 2^32 / 1000000;

    netmemcpy(&buf[0], &tv.tv_sec, 4);
    netmemcpy(&buf[4], &frac, 4);

    buf[7] = 0x18;    // quality: 24 bits of accuracy
#endif
}

// if the recommended MAC address ranges are used, this function filters GOOSE and SV packets
void gse_sv_packet_filter(unsigned char *buf, int len) {
    if (buf[0] == 0x01 && buf[1] == 0x0C && buf[2] == 0xCD) {
        if (buf[3] == 0x01) {
            //GOOSE: 01-0C-CD-01-00-00 to 01-0C-CD-01-01-FF
            gseDecode(buf, len);
        }
        else if (buf[3] == 0x04) {
            //SV: 01-0C-CD-04-00-00 to 01-0C-CD-04-01-FF
            svDecode(buf, len);
        }
    }
}

// copies bytes to network format (big-endian)
void netmemcpy(void *dst, const void *src, unsigned int len) {
#ifdef LITTLE_ENDIAN
    reversememcpy((unsigned char *) dst, (const unsigned char *) src, len);
#else
    memcpy((unsigned char *) dst, (const unsigned char *) src, len);
#endif
}

// copies bytes to host format (little-endian)
void hostmemcpy(void *dst, const void *src, unsigned int len) {
#ifdef LITTLE_ENDIAN
    memcpy((unsigned char *) dst, (const unsigned char *) src, len);
#else
    reversememcpy((unsigned char *) dst, (const unsigned char *) src, len);
#endif
}