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/

Committer:
sblair
Date:
Fri Oct 07 13:48:18 2011 +0000
Revision:
1:9399d44c2b1a
Parent:
0:230c10b228ea

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
sblair 1:9399d44c2b1a 1 /**
sblair 1:9399d44c2b1a 2 * Rapid-prototyping protection schemes with IEC 61850
sblair 1:9399d44c2b1a 3 *
sblair 1:9399d44c2b1a 4 * Copyright (c) 2011 Steven Blair
sblair 1:9399d44c2b1a 5 *
sblair 1:9399d44c2b1a 6 * This program is free software; you can redistribute it and/or
sblair 1:9399d44c2b1a 7 * modify it under the terms of the GNU General Public License
sblair 1:9399d44c2b1a 8 * as published by the Free Software Foundation; either version 2
sblair 1:9399d44c2b1a 9 * of the License, or (at your option) any later version.
sblair 1:9399d44c2b1a 10
sblair 1:9399d44c2b1a 11 * This program is distributed in the hope that it will be useful,
sblair 1:9399d44c2b1a 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
sblair 1:9399d44c2b1a 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
sblair 1:9399d44c2b1a 14 * GNU General Public License for more details.
sblair 1:9399d44c2b1a 15
sblair 1:9399d44c2b1a 16 * You should have received a copy of the GNU General Public License
sblair 1:9399d44c2b1a 17 * along with this program; if not, write to the Free Software
sblair 1:9399d44c2b1a 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
sblair 1:9399d44c2b1a 19 */
sblair 1:9399d44c2b1a 20
sblair 0:230c10b228ea 21 #include "svPacketData.h"
sblair 0:230c10b228ea 22 #include "svEncode.h"
sblair 0:230c10b228ea 23 #include "gseEncode.h"
sblair 0:230c10b228ea 24 #include "encodePacket.h"
sblair 0:230c10b228ea 25
sblair 0:230c10b228ea 26
sblair 0:230c10b228ea 27 int svASDULength(struct svData *svData) {
sblair 1:9399d44c2b1a 28 int len = 0;
sblair 0:230c10b228ea 29
sblair 1:9399d44c2b1a 30 len += strlen((const char *) svData->ASDU[0].svID) + 2;
sblair 1:9399d44c2b1a 31 //printf("%i, %s\n", strlen(svData->ASDU[0].svID), svData->ASDU[0].svID);
sblair 1:9399d44c2b1a 32 //len += strlen((const char *) svData->ASDU[0].datset) + 2;
sblair 1:9399d44c2b1a 33 len += BER_GET_LENGTH_CTYPE_INT16U(&svData->ASDU[0].smpCnt) + 2;
sblair 1:9399d44c2b1a 34 len += BER_GET_LENGTH_CTYPE_INT32U(&svData->ASDU[0].confRev) + 2;
sblair 1:9399d44c2b1a 35 len += SV_GET_LENGTH_BOOLEAN + 2;
sblair 1:9399d44c2b1a 36 //len += BER_GET_LENGTH_CTYPE_INT16U(&svData->ASDU[0].smpRate) + 2;
sblair 1:9399d44c2b1a 37 len += svData->ASDU[0].data.size + getLengthBytes(svData->ASDU[0].data.size);
sblair 1:9399d44c2b1a 38 len++;
sblair 0:230c10b228ea 39
sblair 1:9399d44c2b1a 40 return len;
sblair 0:230c10b228ea 41 }
sblair 0:230c10b228ea 42
sblair 0:230c10b228ea 43 int svSeqLength(struct svData *svData) {
sblair 1:9399d44c2b1a 44 int len = svASDULength(svData);
sblair 1:9399d44c2b1a 45 len += getLengthBytes(len);
sblair 1:9399d44c2b1a 46 len++;
sblair 1:9399d44c2b1a 47 len = len * svData->noASDU; // assume all ASDUs are the same size
sblair 0:230c10b228ea 48
sblair 1:9399d44c2b1a 49 return len;
sblair 0:230c10b228ea 50 }
sblair 0:230c10b228ea 51
sblair 0:230c10b228ea 52 int svAPDULength(struct svData *svData) {
sblair 1:9399d44c2b1a 53 int len = svSeqLength(svData);
sblair 1:9399d44c2b1a 54 len += getLengthBytes(len);
sblair 1:9399d44c2b1a 55 len++;
sblair 0:230c10b228ea 56
sblair 1:9399d44c2b1a 57 len += 3;
sblair 0:230c10b228ea 58
sblair 1:9399d44c2b1a 59 return len;
sblair 0:230c10b228ea 60 }
sblair 0:230c10b228ea 61
sblair 0:230c10b228ea 62
sblair 0:230c10b228ea 63 //TODO: convert to proper BER sizes
sblair 0:230c10b228ea 64 // creates an SV packet, including frame header. returns 0 on fail; number of bytes on success
sblair 0:230c10b228ea 65 int svEncodePacket(struct svData *svData, unsigned char *buf) {
sblair 1:9399d44c2b1a 66 int offset = 0;
sblair 1:9399d44c2b1a 67 int len = svAPDULength(svData);
sblair 1:9399d44c2b1a 68 len += getLengthBytes(len);
sblair 1:9399d44c2b1a 69 len += 9; // savPdu tag size (1 byte), plus 8 "header" bytes
sblair 0:230c10b228ea 70
sblair 1:9399d44c2b1a 71 // frame header
sblair 1:9399d44c2b1a 72 memcpy(&buf[offset], svData->ethHeaderData.destMACAddress, 6); // destination MAC addresses
sblair 1:9399d44c2b1a 73 offset += 6;
sblair 1:9399d44c2b1a 74 memcpy(&buf[offset], LOCAL_MAC_ADDRESS, 6); // source MAC addresses
sblair 1:9399d44c2b1a 75 offset += 6;
sblair 0:230c10b228ea 76
sblair 1:9399d44c2b1a 77 buf[offset++] = 0x81; // TPID
sblair 1:9399d44c2b1a 78 buf[offset++] = 0x00;
sblair 0:230c10b228ea 79
sblair 1:9399d44c2b1a 80 netmemcpy(&buf[offset], &svData->ethHeaderData.VLAN_ID, 2); // TCI
sblair 1:9399d44c2b1a 81 buf[offset] |= (svData->ethHeaderData.VLAN_PRIORITY << 5);
sblair 1:9399d44c2b1a 82 offset += 2;
sblair 0:230c10b228ea 83
sblair 1:9399d44c2b1a 84 buf[offset++] = 0x88; // EtherType
sblair 1:9399d44c2b1a 85 buf[offset++] = 0xBA;
sblair 0:230c10b228ea 86
sblair 1:9399d44c2b1a 87 netmemcpy(&buf[offset], &svData->ethHeaderData.APPID, 2); // APPID
sblair 1:9399d44c2b1a 88 offset += 2;
sblair 0:230c10b228ea 89
sblair 1:9399d44c2b1a 90 netmemcpy(&buf[offset], &len, 2); // length
sblair 1:9399d44c2b1a 91 offset += 2;
sblair 0:230c10b228ea 92
sblair 1:9399d44c2b1a 93 buf[offset++] = 0x00; // reserved 1
sblair 1:9399d44c2b1a 94 buf[offset++] = 0x00;
sblair 1:9399d44c2b1a 95 buf[offset++] = 0x00; // reserved 2
sblair 1:9399d44c2b1a 96 buf[offset++] = 0x00;
sblair 0:230c10b228ea 97
sblair 1:9399d44c2b1a 98 buf[offset++] = 0x60;
sblair 1:9399d44c2b1a 99 offset += encodeLength(&buf[offset], svAPDULength(svData));
sblair 0:230c10b228ea 100
sblair 1:9399d44c2b1a 101 //TODO noASDU may be > 126?
sblair 1:9399d44c2b1a 102 buf[offset++] = 0x80;
sblair 1:9399d44c2b1a 103 buf[offset++] = 1;
sblair 1:9399d44c2b1a 104 buf[offset++] = (unsigned char) svData->noASDU;
sblair 0:230c10b228ea 105
sblair 1:9399d44c2b1a 106 buf[offset++] = 0xA2;
sblair 1:9399d44c2b1a 107 offset += encodeLength(&buf[offset], svSeqLength(svData));
sblair 0:230c10b228ea 108
sblair 1:9399d44c2b1a 109 int i = 0;
sblair 1:9399d44c2b1a 110 int size = 0;
sblair 1:9399d44c2b1a 111 for (i = 0; i < svData->noASDU; i++) {
sblair 1:9399d44c2b1a 112 buf[offset++] = 0x30;
sblair 1:9399d44c2b1a 113 offset += encodeLength(&buf[offset], svASDULength(svData));
sblair 0:230c10b228ea 114
sblair 1:9399d44c2b1a 115 size = strlen((const char *) svData->ASDU[i].svID);
sblair 1:9399d44c2b1a 116 buf[offset++] = 0x80;
sblair 1:9399d44c2b1a 117 buf[offset++] = size;
sblair 1:9399d44c2b1a 118 memcpy(&buf[offset], svData->ASDU[i].svID, size);
sblair 1:9399d44c2b1a 119 offset += size;
sblair 0:230c10b228ea 120
sblair 1:9399d44c2b1a 121 /*size = strlen(svData->ASDU[i].datset);
sblair 1:9399d44c2b1a 122 buf[offset++] = 0x81;
sblair 1:9399d44c2b1a 123 buf[offset++] = size;
sblair 1:9399d44c2b1a 124 memcpy(&buf[offset], svData->ASDU[i].datset, size);
sblair 1:9399d44c2b1a 125 offset += size;*/
sblair 0:230c10b228ea 126
sblair 1:9399d44c2b1a 127 buf[offset++] = 0x82;
sblair 1:9399d44c2b1a 128 offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT16U(&svData->ASDU[i].smpCnt));
sblair 1:9399d44c2b1a 129 offset += ber_encode_integer(&buf[offset], &svData->ASDU[i].smpCnt, SV_GET_LENGTH_INT16U);
sblair 1:9399d44c2b1a 130 //buf[offset++] = BER_GET_LENGTH_CTYPE_INT16U(&svData->ASDU[i].smpCnt);
sblair 1:9399d44c2b1a 131 //offset += BER_ENCODE_CTYPE_INT16U(&buf[offset], &svData->ASDU[i].smpCnt);
sblair 0:230c10b228ea 132
sblair 1:9399d44c2b1a 133 buf[offset++] = 0x83;
sblair 1:9399d44c2b1a 134 offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&svData->ASDU[i].confRev));
sblair 1:9399d44c2b1a 135 offset += ber_encode_integer(&buf[offset], &svData->ASDU[i].confRev, SV_GET_LENGTH_INT32U);
sblair 1:9399d44c2b1a 136 //buf[offset++] = BER_GET_LENGTH_CTYPE_INT32U(&svData->ASDU[i].confRev);
sblair 1:9399d44c2b1a 137 //offset += BER_ENCODE_CTYPE_INT32U(&buf[offset], &svData->ASDU[i].confRev);
sblair 0:230c10b228ea 138
sblair 1:9399d44c2b1a 139 buf[offset++] = 0x85;
sblair 1:9399d44c2b1a 140 buf[offset++] = SV_GET_LENGTH_BOOLEAN;
sblair 1:9399d44c2b1a 141 offset += ENCODE_CTYPE_BOOLEAN(&buf[offset], &svData->ASDU[i].smpSynch);
sblair 0:230c10b228ea 142
sblair 1:9399d44c2b1a 143 /*buf[offset++] = 0x86;
sblair 1:9399d44c2b1a 144 buf[offset++] = SV_GET_LENGTH_INT16U;
sblair 1:9399d44c2b1a 145 offset += ENCODE_CTYPE_INT16U(&buf[offset], &svData->ASDU[i].smpRate);*/
sblair 0:230c10b228ea 146
sblair 1:9399d44c2b1a 147 buf[offset++] = 0x87;
sblair 1:9399d44c2b1a 148 offset += encodeLength(&buf[offset], svData->ASDU[i].data.size);
sblair 1:9399d44c2b1a 149 memcpy(&buf[offset], svData->ASDU[i].data.data, svData->ASDU[i].data.size);
sblair 1:9399d44c2b1a 150 offset += svData->ASDU[i].data.size;
sblair 1:9399d44c2b1a 151 }
sblair 0:230c10b228ea 152
sblair 1:9399d44c2b1a 153 // assume network interface, such as WinPcap, generates CRC bytes
sblair 0:230c10b228ea 154
sblair 1:9399d44c2b1a 155 return offset;
sblair 0:230c10b228ea 156 }