├── CheatSheet ├── README.md ├── bro-scripts ├── DoubleConverterC++ ├── DoubleConverterPython.py ├── README.md ├── T104_CM_Events.bro ├── T104_DataTypes.bro ├── T104_MV_Events.bro ├── T104_PhysicalTags.bro ├── T104_PhysicalTags_Alpha.bro ├── T104_PhysicalTags_Masterthesis.bro ├── T104_STATS_Events.bro ├── T104_UtilityFunctions.bro ├── t104.evt └── t104.spicy ├── docker ├── .dockerignore ├── Dockerfile └── README.md ├── img └── ClassDiagram.pdf ├── policy-generator ├── GeneratePhysicalTagMap.py ├── PolicyGeneratorConstants.py ├── README.md ├── generated-bro-scripts │ ├── T104_PhysicalTags_Alpha.bro │ └── T104_PhysicalTags_Masterthesis.bro └── rtu-configs │ ├── Alpha_GlobalKnowledge_Normalized_RTU_Configuration.csv │ ├── Alpha_GlobalKnowledge_RTU_Configuration.csv │ ├── Alpha_LocalKnowledge_Normalized_RTU_Configuration.csv │ ├── Alpha_LocalKnowledge_RTU_Configuration.csv │ ├── Masterthesis_GlobalKnowledge_Normalized_RTU_Configuration.csv │ └── Masterthesis_GlobalKnowledge_RTU_Configuration.csv ├── state-manager ├── GridComponents │ ├── AbstractComponent.py │ ├── AbstractDecorator.py │ ├── AbstractInterlock.py │ ├── AbstractNode.py │ ├── AbstractProtectiveDevice.py │ ├── Bus.py │ ├── Consumer.py │ ├── DynamicInterlock.py │ ├── Fuse.py │ ├── Generator.py │ ├── LocalRTU.py │ ├── Meter.py │ ├── PowerLine.py │ ├── ProtectiveRelay.py │ ├── StaticInterlock.py │ ├── Switch.py │ ├── Transformer.py │ └── __init__.py ├── LoggerUtilities.py ├── README.md ├── ScenarioTestScripts │ ├── TestScenariosAlpha.py │ ├── TestScenariosInterlock.py │ ├── TestScenariosMasterthesis.py │ ├── TestScenariosTransfFuseRelay.py │ └── __init__.py ├── Scenarios │ ├── Alpha_GlobalKnowledge_BasicCase.state │ ├── Alpha_GlobalKnowledge_Scenario1.state │ ├── Alpha_GlobalKnowledge_Scenario2.state │ ├── Alpha_GlobalKnowledge_Scenario3.state │ ├── Alpha_GlobalKnowledge_Scenario4.state │ ├── Alpha_GlobalKnowledge_Scenario5.state │ ├── Alpha_GlobalKnowledge_Scenario6.state │ ├── Alpha_GlobalKnowledge_Scenario7.state │ ├── Alpha_GlobalKnowledge_Scenario8.state │ ├── Alpha_LocalKnowledge_BasicCase.state │ ├── Alpha_LocalKnowledge_Scenario1.state │ ├── Alpha_LocalKnowledge_Scenario2.state │ ├── Alpha_LocalKnowledge_Scenario3.state │ ├── Interlock_BasicCase.state │ ├── Interlock_Scenario1.state │ ├── Interlock_Scenario2.state │ ├── Interlock_Scenario3.state │ ├── Masterthesis_GlobalKnowledge_BasicCase.state │ ├── Masterthesis_GlobalKnowledge_Scenario1.state │ ├── Masterthesis_GlobalKnowledge_Scenario2.state │ ├── Masterthesis_GlobalKnowledge_Scenario3.state │ ├── Masterthesis_GlobalKnowledge_Scenario4.state │ ├── Masterthesis_GlobalKnowledge_Scenario5.state │ ├── Masterthesis_GlobalKnowledge_Scenario6.state │ ├── Masterthesis_GlobalKnowledge_Scenario7.state │ ├── Masterthesis_GlobalKnowledge_Scenario8.state │ ├── Masterthesis_GlobalKnowledge_Scenario9.state │ ├── TransfFuseRelay_BasicCase.state │ ├── TransfFuseRelay_Scenario1.state │ ├── TransfFuseRelay_Scenario2.state │ ├── TransfFuseRelay_Scenario3.state │ ├── TransfFuseRelay_Scenario4.state │ └── TransfFuseRelay_Scenario5.state ├── StateManager.py ├── StateManagerUtilities.py ├── T104_BroccoliStateManager_Alpha.bro ├── T104_BroccoliStateManager_Masterthesis.bro ├── TestScenariosBroAlpha.py ├── TestScenariosBroMasterthesis.py ├── TestTopologies.py ├── TestUtilities.py └── ValueStore.py ├── traffic-generator ├── APDUType01.py ├── APDUType03.py ├── APDUType09.py ├── APDUType102.py ├── APDUType13.py ├── APDUType21.py ├── APDUType30.py ├── APDUType31.py ├── APDUType34.py ├── APDUType36.py ├── APDUType45.py ├── APDUType46.py ├── APDUType48.py ├── APDUType50.py ├── APDUType58.py ├── APDUType59.py ├── APDUType61.py ├── APDUType63.py ├── AbstractAPDU.py ├── AutomaticTrafficGeneration.py ├── GenerateScenarioTrafficExtended.py ├── README.md ├── Server.py ├── TrafficConstants.py ├── TrafficUtilities.py └── generated-traffic │ ├── Alpha_GlobalKnowledge_Normalized_Scenario1.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario2.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario3.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario4.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario5.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario6.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario7.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario8.pcapng │ ├── Alpha_GlobalKnowledge_Scenario1.pcapng │ ├── Alpha_GlobalKnowledge_Scenario2.pcapng │ ├── Alpha_GlobalKnowledge_Scenario3.pcapng │ ├── Alpha_GlobalKnowledge_Scenario4.pcapng │ ├── Alpha_GlobalKnowledge_Scenario5.pcapng │ ├── Alpha_GlobalKnowledge_Scenario6.pcapng │ ├── Alpha_GlobalKnowledge_Scenario7.pcapng │ ├── Alpha_LocalKnowledge_Normalized_Scenario1.pcapng │ ├── Alpha_LocalKnowledge_Normalized_Scenario2.pcapng │ ├── Alpha_LocalKnowledge_Normalized_Scenario3.pcapng │ ├── Alpha_LocalKnowledge_Scenario1.pcapng │ ├── Alpha_LocalKnowledge_Scenario2.pcapng │ ├── Alpha_LocalKnowledge_Scenario3.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario2.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario3.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario4.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario5.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario6.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario7.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario8.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario9.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario1.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario2.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario3.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario4.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario5.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario6.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario7.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario8.pcapng │ └── Masterthesis_GlobalKnowledge_Scenario9.pcapng └── workspace ├── pcap └── scenarios │ ├── Alpha_GlobalKnowledge_Normalized_Scenario1.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario2.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario3.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario4.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario5.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario6.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario7.pcapng │ ├── Alpha_GlobalKnowledge_Normalized_Scenario8.pcapng │ ├── Alpha_GlobalKnowledge_Scenario1.pcapng │ ├── Alpha_GlobalKnowledge_Scenario2.pcapng │ ├── Alpha_GlobalKnowledge_Scenario3.pcapng │ ├── Alpha_GlobalKnowledge_Scenario4.pcapng │ ├── Alpha_GlobalKnowledge_Scenario5.pcapng │ ├── Alpha_GlobalKnowledge_Scenario6.pcapng │ ├── Alpha_GlobalKnowledge_Scenario7.pcapng │ ├── Alpha_LocalKnowledge_Normalized_Scenario1.pcapng │ ├── Alpha_LocalKnowledge_Normalized_Scenario2.pcapng │ ├── Alpha_LocalKnowledge_Normalized_Scenario3.pcapng │ ├── Alpha_LocalKnowledge_Scenario1.pcapng │ ├── Alpha_LocalKnowledge_Scenario2.pcapng │ ├── Alpha_LocalKnowledge_Scenario3.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario2.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario3.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario4.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario5.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario6.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario7.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario8.pcapng │ ├── Masterthesis_GlobalKnowledge_Normalized_Scenario9.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario1.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario2.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario3.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario4.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario5.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario6.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario7.pcapng │ ├── Masterthesis_GlobalKnowledge_Scenario8.pcapng │ └── Masterthesis_GlobalKnowledge_Scenario9.pcapng ├── pythontests ├── DoubleConverterC++ ├── DoubleConverterPython.py ├── GridComponents │ ├── AbstractComponent.py │ ├── AbstractDecorator.py │ ├── AbstractInterlock.py │ ├── AbstractNode.py │ ├── AbstractProtectiveDevice.py │ ├── Bus.py │ ├── Consumer.py │ ├── DynamicInterlock.py │ ├── Fuse.py │ ├── Generator.py │ ├── LocalRTU.py │ ├── Meter.py │ ├── PowerLine.py │ ├── ProtectiveRelay.py │ ├── StaticInterlock.py │ ├── Switch.py │ ├── Transformer.py │ └── __init__.py ├── LoggerUtilities.py ├── ScenarioTestScripts │ ├── TestScenariosAlpha.py │ ├── TestScenariosInterlock.py │ ├── TestScenariosMasterthesis.py │ ├── TestScenariosTransfFuseRelay.py │ └── __init__.py ├── Scenarios │ ├── Alpha_GlobalKnowledge_BasicCase.state │ ├── Alpha_GlobalKnowledge_Scenario1.state │ ├── Alpha_GlobalKnowledge_Scenario2.state │ ├── Alpha_GlobalKnowledge_Scenario3.state │ ├── Alpha_GlobalKnowledge_Scenario4.state │ ├── Alpha_GlobalKnowledge_Scenario5.state │ ├── Alpha_GlobalKnowledge_Scenario6.state │ ├── Alpha_GlobalKnowledge_Scenario7.state │ ├── Alpha_GlobalKnowledge_Scenario8.state │ ├── Alpha_LocalKnowledge_BasicCase.state │ ├── Alpha_LocalKnowledge_Scenario1.state │ ├── Alpha_LocalKnowledge_Scenario2.state │ ├── Alpha_LocalKnowledge_Scenario3.state │ ├── Interlock_BasicCase.state │ ├── Interlock_Scenario1.state │ ├── Interlock_Scenario2.state │ ├── Interlock_Scenario3.state │ ├── Masterthesis_GlobalKnowledge_BasicCase.state │ ├── Masterthesis_GlobalKnowledge_Scenario1.state │ ├── Masterthesis_GlobalKnowledge_Scenario2.state │ ├── Masterthesis_GlobalKnowledge_Scenario3.state │ ├── Masterthesis_GlobalKnowledge_Scenario4.state │ ├── Masterthesis_GlobalKnowledge_Scenario5.state │ ├── Masterthesis_GlobalKnowledge_Scenario6.state │ ├── Masterthesis_GlobalKnowledge_Scenario7.state │ ├── Masterthesis_GlobalKnowledge_Scenario8.state │ ├── Masterthesis_GlobalKnowledge_Scenario9.state │ ├── TransfFuseRelay_BasicCase.state │ ├── TransfFuseRelay_Scenario1.state │ ├── TransfFuseRelay_Scenario2.state │ ├── TransfFuseRelay_Scenario3.state │ ├── TransfFuseRelay_Scenario4.state │ └── TransfFuseRelay_Scenario5.state ├── StateManager.py ├── StateManagerUtilities.py ├── T104_BroccoliStateManager_Alpha.bro ├── T104_BroccoliStateManager_Masterthesis.bro ├── T104_CM_Events.bro ├── T104_DataTypes.bro ├── T104_MV_Events.bro ├── T104_PhysicalTags.bro ├── T104_PhysicalTags_Alpha.bro ├── T104_PhysicalTags_Masterthesis.bro ├── T104_STATS_Events.bro ├── T104_UtilityFunctions.bro ├── TestScenariosBroAlpha.py ├── TestScenariosBroMasterthesis.py ├── TestTopologies.py ├── TestUtilities.py ├── ValueStore.py ├── t104.evt └── t104.spicy └── valueDumps └── valueStoreAutosaveDump /CheatSheet: -------------------------------------------------------------------------------- 1 | # Bro 2 | ## Run analysis on Masterthesis scenarios (3 different shells): 3 | cd /data/pythontests/ && bro -i eth0 -C T104_BroccoliStateManager_Masterthesis.bro t104.evt 4 | cd /data/pythontests/ && python TestScenariosBroMasterthesis.py 5 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng 6 | 7 | ## Run analysis on other scenarios (3 different shells): 8 | cd /data/pythontests/ && bro -i eth0 -C T104_BroccoliStateManager_Alpha.bro t104.evt 9 | cd /data/pythontests/ && python TestScenariosBroAlpha.py 10 | tcpreplay --pps 100 --intf1=eth0 /data/pcap/scenarios/Alpha_GlobalKnowledge_Normalized_Scenario1.pcapng 11 | 12 | ## Run bro pcap summary analysis: 13 | cd /data/scripts && bro -C -r /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng t104.evt T104_STATS_Events.bro 14 | cd /data/scripts && bro -C -r /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng t104.evt T104_MV_Events.bro 15 | cd /data/scripts && bro -C -r /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng t104.evt T104_CM_Events.bro 16 | 17 | # Docker 18 | ## Run docker (Workspace, broccoli): 19 | docker run -i -t -v $(pwd)/workspace:/data "rf/broccoli-hilti" 20 | 21 | ## Open additional shells 22 | docker container ls 23 | docker exec -it bash 24 | docker exec -it $(docker container ls | grep rf/broccoli-hilti | awk '{ print $1 }' | head -1) bash 25 | 26 | ## Build image rf/broccoli-hilti: 27 | cd docker/Dockerfile && docker build -t rf/broccoli-hilti . 28 | 29 | ## Test image (this should produce NO output (python import errors) at all: 30 | docker run -t rf/broccoli-hilti python -c "import broccoli" 31 | 32 | # Spicy 33 | ## Run spicy isolated: 34 | cd /data/scripts && cat pcap/u_frame_hex.dat | spicy-driver t104.spicy -p T104::Apcis 35 | 36 | ## Compile spicy: 37 | hilti-build -o compiled.out /opt/hilti/tools/spicy-driver/spicy-driver.cc source.spicy 38 | 39 | ## Debug (does not work properly right now): 40 | cat httpinput | HILTI_DEBUG=spicy spicy-driver source.spicy -g -d 41 | 42 | # TCP Replay 43 | ## Replay packets 44 | tcpreplay --intf1=eth0 /data/pcap/Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng 45 | (Interesting options: --pps=100 --loop 100 --multiplier=3) -------------------------------------------------------------------------------- /bro-scripts/DoubleConverterC++: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjchromik/RuleGeneratorSCADA/ef289d8ba49cdb9e1f716599568c701bec5837d4/bro-scripts/DoubleConverterC++ -------------------------------------------------------------------------------- /bro-scripts/DoubleConverterPython.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This script takes an hexadecimal float and converts it to hexadecimal double. 6 | ''' 7 | 8 | import struct 9 | import sys 10 | 11 | if __name__ == '__main__': 12 | assert len(sys.argv)==2 13 | print struct.unpack('!f', ("%x" % int(sys.argv[1])).decode('hex'))[0] -------------------------------------------------------------------------------- /bro-scripts/README.md: -------------------------------------------------------------------------------- 1 | This folder contains Bro Scripts and Bro functions for parsing, evaluating and dissecting pcap files containing IEC-104 packets. 2 | 3 | Useful bash snippets: 4 | 5 | Run bro in docker with pcap-file (Example): 6 | ```bash 7 | cd /data/scripts && bro -C -r /data/pcap/traffic.pcap t104.evt T104_STATS_Events.bro 8 | ``` 9 | 10 | Alternative (2 bash shells in same docker container): 11 | ```bash 12 | bro -i eth0 -C T104_STATS_Events.bro t104.evt 13 | # Wait for "Listening on eth0.". Then: 14 | tcpreplay --pps 1000 --intf1=eth0 /data/pcap/traffic.pcap 15 | ``` 16 | 17 | Run broccoli-python bindings over pcap-file (3 bash shells): 18 | ```bash 19 | cd /data/scripts/ && bro -i eth0 -C T104_Broccoli.bro t104.evt 20 | # Wait for "Listening on eth0.". Then: 21 | cd /data/scripts/ && python T104_BroccoliInterface.py 22 | # Wait for "Connected.". Then: 23 | tcpreplay --pps 1000 --intf1=eth0 /data/pcap/traffic.pcap 24 | ``` -------------------------------------------------------------------------------- /bro-scripts/T104_DataTypes.bro: -------------------------------------------------------------------------------- 1 | # This Bro script defines data types required for the safety boundary checks 2 | 3 | module T104_DataTypes; 4 | 5 | export{ 6 | type rtu_number_t : count; 7 | type address_t : count; 8 | type normalized_value_t : double; 9 | type single_point_value_t : bool; 10 | type raw_value_t : count; 11 | 12 | # (rtuNo, address, normalized value) ; already converted 13 | type rtu_ioa_normalized_value_tuple_t:record { 14 | rtuNo: rtu_number_t; 15 | address: address_t; 16 | value: normalized_value_t; 17 | }; 18 | 19 | # (address, normalized value) ; already converted 20 | type ioa_normalized_value_pair_t:record { 21 | address: address_t; 22 | value: normalized_value_t; 23 | }; 24 | 25 | # (rtuNo, address, sp value) ; already converted 26 | type rtu_ioa_single_point_value_tuple_t:record { 27 | rtuNo: rtu_number_t; 28 | address: address_t; 29 | value: single_point_value_t; 30 | }; 31 | 32 | # (address, sp value) ; already converted 33 | type ioa_single_point_value_pair_t:record { 34 | address: address_t; 35 | value: single_point_value_t; 36 | }; 37 | 38 | # (rtuNo, address, value) ; raw value 39 | type rtu_ioa_raw_value_tuple_t:record { 40 | rtuNo: rtu_number_t; 41 | address: address_t; 42 | value: raw_value_t; 43 | }; 44 | 45 | # (address, value) ; raw value 46 | type ioa_raw_value_pair_t:record { 47 | address: address_t; 48 | value: raw_value_t; 49 | }; 50 | 51 | # interval of normalized - or generally double - values 52 | type interval_t: record{ 53 | min : normalized_value_t &log; 54 | max : normalized_value_t &log; 55 | }; 56 | 57 | # physical tag description from rtu config 58 | type physical_tag_t: record{ 59 | tagName : string &log; 60 | name: string &log; 61 | description : string &log; 62 | dimension : string &log; 63 | normalizationInterval : interval_t &log; 64 | }; 65 | 66 | # hashmap: rtu,address -> physical tag 67 | type physical_tag_map_t : table[rtu_number_t,address_t] of physical_tag_t; 68 | 69 | # log format of a physical tag 70 | type log_physical_tag_entry_t: record{ 71 | ts: time &log; 72 | id: conn_id &log; 73 | note: string &log; 74 | physicalTag : physical_tag_t &log; 75 | }; 76 | 77 | # cause of transmission information 78 | type cause_of_transmission:record { 79 | negative : bool; 80 | test : bool; 81 | common_addr : int; 82 | }; 83 | } -------------------------------------------------------------------------------- /bro-scripts/T104_STATS_Events.bro: -------------------------------------------------------------------------------- 1 | # This Bro script is printing used iec-104 function codes in realtime and a summary afterwards. 2 | 3 | @load base/protocols/conn 4 | @load T104_DataTypes 5 | @load T104_UtilityFunctions 6 | @load T104_PhysicalTags 7 | 8 | module T104_STATS_Events; 9 | 10 | export{ 11 | redef enum Log::ID += {LOG_stats}; 12 | redef T104_UtilityFunctions::DEBUG_LEVEL=2; 13 | const RTU_NUMBER = 1001; 14 | 15 | type function_id_counter_t : table[count] of count; 16 | global functionIdCounter : function_id_counter_t; 17 | 18 | type info_obj_code_lookup_t : table[count] of string; 19 | global infoObjCodeLookup : info_obj_code_lookup_t; 20 | } 21 | 22 | # Initialization code 23 | event bro_init(){ 24 | #Log::create_stream(T104_STATS_Events::LOG_stats, [$columns=T104_DataTypes::log_physical_tag_entry_t, $path="stats"]); 25 | } 26 | 27 | # Prints information about ASDU 28 | function print_asdu_details(c: connection, rtuNumber:int, infoObjectType: T104::Info_obj_code){ 29 | local note = fmt("[%s] ASDU! RTU %d (%s:%s -> %s:%s)",T104_UtilityFunctions::format_timestamp(network_time()),rtuNumber,c$id$orig_h,c$id$orig_p,c$id$resp_h,c$id$resp_p); 30 | note = note + fmt(": Function ID: %s (%d)",infoObjectType,infoObjectType); 31 | T104_UtilityFunctions::print_debug(note,2); 32 | } 33 | 34 | # Adds information of current ASDU to statistics 35 | function add_statistics(c: connection, rtuNumber:int, infoObjectType: T104::Info_obj_code){ 36 | local functionId = int_to_count(enum_to_int(infoObjectType)); 37 | infoObjCodeLookup[functionId] = fmt("%s",infoObjectType); 38 | if(functionId in functionIdCounter){ 39 | functionIdCounter[functionId]=functionIdCounter[functionId]+1; 40 | } 41 | else { 42 | functionIdCounter[functionId]=1; 43 | } 44 | } 45 | 46 | # Prints statistics of seen ASDUs 47 | function print_statistics(){ 48 | local note = ""; 49 | local usedIds : vector of count; 50 | for (id in functionIdCounter){ 51 | usedIds[|usedIds|]=id; 52 | } 53 | usedIds = sort(usedIds); 54 | T104_UtilityFunctions::print_debug("",2); 55 | T104_UtilityFunctions::print_debug(fmt("Summary:"),2); 56 | T104_UtilityFunctions::print_debug(fmt(":"),2); 57 | for (id in usedIds){ 58 | note= fmt("%4d: %4d",usedIds[id], functionIdCounter[usedIds[id]]); 59 | T104_UtilityFunctions::print_debug(note,2); 60 | } 61 | } 62 | 63 | # ASDU Event 64 | event t104::asdu(c: connection, cause:T104_DataTypes::cause_of_transmission, infoObjectType: T104::Info_obj_code) { 65 | local rtuNumber=RTU_NUMBER; 66 | print_asdu_details(c,rtuNumber,infoObjectType); 67 | add_statistics(c,rtuNumber,infoObjectType); 68 | } 69 | 70 | # Execute after parsing whole traffic dump 71 | event bro_done(){ 72 | print_statistics(); 73 | } 74 | -------------------------------------------------------------------------------- /bro-scripts/T104_UtilityFunctions.bro: -------------------------------------------------------------------------------- 1 | # This Bro script provides several utility functions like logging 2 | 3 | @load T104_DataTypes 4 | module T104_UtilityFunctions; 5 | 6 | export{ 7 | const DEBUG_ENABLED = T &redef; 8 | const DEBUG_LEVEL = 1 &redef; # 1... verbose, 2... less verbose 9 | const DEBUG_PRINT_LEVEL = F &redef; 10 | 11 | # Debug output 12 | function print_debug(message : string, level : int &default=1){ 13 | if (DEBUG_ENABLED && level>=DEBUG_LEVEL){ 14 | if (DEBUG_PRINT_LEVEL){ 15 | print fmt("[DEBUG L%d] %s",level,message); 16 | } 17 | else { 18 | print fmt("%s",message); 19 | } 20 | } 21 | } 22 | 23 | # Format a physical tag 24 | function get_physical_tag_note(rtuNumber : T104_DataTypes::rtu_number_t, address : T104_DataTypes::address_t, current_physical_tag : T104_DataTypes::physical_tag_t, printNormalizationInterval : bool &default=F) : string { 25 | if(printNormalizationInterval){ 26 | return fmt("",rtuNumber,address,current_physical_tag$tagName,current_physical_tag$name,current_physical_tag$description,current_physical_tag$dimension,current_physical_tag$normalizationInterval$min,current_physical_tag$normalizationInterval$max); 27 | } else { 28 | return fmt("",rtuNumber,address,current_physical_tag$tagName,current_physical_tag$name,current_physical_tag$description,current_physical_tag$dimension); 29 | } 30 | } 31 | 32 | # Format a timestamp 33 | function format_timestamp(timestamp : time):string { 34 | return strftime("%Y-%m-%d %H:%M:%S", timestamp); 35 | } 36 | 37 | # Convert raw value from bro event into normalized value between -1.0 and 1.0 38 | function normalize_value(raw : T104_DataTypes::raw_value_t):T104_DataTypes::normalized_value_t { 39 | local tmpValue : T104_DataTypes::normalized_value_t = raw/32768.0; 40 | if(tmpValue<=1.0) { 41 | # positive value 42 | return tmpValue; 43 | } else { 44 | # negative value 45 | return tmpValue-2; 46 | } 47 | } 48 | 49 | # Denormalize value from a normalized value between -1.0 and 1.0 into real float value 50 | function denormalize_value(normalized_value : T104_DataTypes::normalized_value_t,normalization_interval : T104_DataTypes::interval_t):T104_DataTypes::normalized_value_t { 51 | local interval_position = (normalized_value + 1)/2.0; 52 | local difference = normalization_interval$max - normalization_interval$min; 53 | return normalization_interval$min + difference * interval_position; 54 | } 55 | 56 | # This function is from T104_DoubleTests.bro 57 | function doublefy_value_test(raw : T104_DataTypes::raw_value_t):T104_DataTypes::normalized_value_t { 58 | local command = fmt("/data/scripts/DoubleConverterC++ %d",raw); 59 | local cmd = Exec::Command($cmd=command); 60 | when (local res = Exec::run(cmd)) 61 | { 62 | return to_double(res$stdout[0]); 63 | } 64 | return -1.0; 65 | } 66 | 67 | # Convert raw double value from bro event into a double value (this is damn slow, no bro builtin for that available) 68 | function doublefy_value(raw : T104_DataTypes::raw_value_t):T104_DataTypes::normalized_value_t { 69 | return doublefy_value_test(raw); 70 | } 71 | 72 | # Interval check (inclusive edges) for normalized (or generally double) values 73 | function interval_check(value: T104_DataTypes::normalized_value_t,allowed_interval: T104_DataTypes::interval_t) : bool{ 74 | return (allowed_interval$min <= value && value <= allowed_interval$max); 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /docker/.dockerignore: -------------------------------------------------------------------------------- 1 | # Sensitive data should never be included into docker build 2 | **/*.pcap 3 | **/*.pcapng 4 | README.md -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rsmmr/hilti:latest 2 | LABEL maintainer="RF" 3 | LABEL description="This docker image extends the rsmrr/hilti image by broccoli with python bindings and some network tools." 4 | 5 | ENV BROCCOLIPATH /opt/bro/broccoli 6 | ENV DATAPATH /data 7 | ENV PYTHONPATH=$PYTHONPATH:/usr/local/lib/python 8 | 9 | RUN apt-get -y update && apt-get -y install net-tools tcpreplay tcpdump 10 | 11 | WORKDIR $BROCCOLIPATH 12 | RUN git clone --recurse-submodules https://github.com/bro/broccoli 13 | RUN git clone --recurse-submodules https://github.com/bro/broccoli-python 14 | 15 | WORKDIR $BROCCOLIPATH/broccoli 16 | RUN ./configure && make && make install 17 | 18 | WORKDIR $BROCCOLIPATH/broccoli-python 19 | RUN ./configure && make 20 | 21 | WORKDIR $DATAPATH -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | This docker image extends the rsmrr/hilti image by intstalling broccoli with python bindings, tcpreplay and some other useful network tools. Tcpreplay can be used to run bro (and broccoli) in its normal listening mode while replaying previously captured pcap files. 2 | 3 | Run docker: 4 | ```bash 5 | docker run -i -t -v $(pwd)/workspace:/data "rf/broccoli-hilti" 6 | ``` 7 | 8 | Open additional shells: 9 | ```bash 10 | docker exec -it $(docker container ls | grep rf/broccoli-hilti | awk '{ print $1 }' | head -1) bash 11 | # Alternative (e.g. if you prefer not to make use of docker group membership and docker therefore requires sudo) 12 | docker exec -it bash 13 | ``` 14 | 15 | Build image rf/broccoli-hilti: 16 | ```bash 17 | cd $(pwd)/docker && docker build -t rf/broccoli-hilti . 18 | ``` 19 | 20 | Test image. This should produce NO output (python import errors) at all: 21 | ```bash 22 | docker run -t rf/broccoli-hilti python -c "import broccoli" 23 | ``` -------------------------------------------------------------------------------- /img/ClassDiagram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjchromik/RuleGeneratorSCADA/ef289d8ba49cdb9e1f716599568c701bec5837d4/img/ClassDiagram.pdf -------------------------------------------------------------------------------- /policy-generator/PolicyGeneratorConstants.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | BRO_SCRIPT_FILENAME_TEMPLATE = "T104_PhysicalTags_%s.bro" 9 | BRO_FORMAT_STRING = '\t\tPHYSICAL_TAG_MAP[%s,%d]=[$name="%s", $description="%s", $tagName="%s", $dimension="%s", $normalizationInterval=[$min=%f, $max=%f]];\n' 10 | 11 | BRO_SCRIPT_CONTENT_TRAILER = """# This Bro script is automatically generated and contains the RTU's config in PHYSICAL_TAG_MAP. 12 | # The values are extracted from the RTU configuration. 13 | 14 | @load T104_DataTypes 15 | 16 | module T104_PhysicalTags; 17 | 18 | export 19 | { 20 | global PHYSICAL_TAG_MAP : T104_DataTypes::physical_tag_map_t; 21 | 22 | function populate_physical_tag_map() 23 | { 24 | """ 25 | BRO_SCRIPT_CONTENT_FOOTER = """ } 26 | } 27 | # Initialization code 28 | event bro_init(){ 29 | populate_physical_tag_map(); 30 | } 31 | """ 32 | -------------------------------------------------------------------------------- /policy-generator/README.md: -------------------------------------------------------------------------------- 1 | The policy-generator generates a Bro script that is needed to interpret and convert the raw values from the traffic. 2 | It takes an RTU configuation as input and outputs a Bro script containing all relevant information of the configuration. 3 | 4 | Generate scenario policies (select desired RTU configuration in code): 5 | ```bash 6 | python GeneratePhysicalTagMap.py 7 | ``` 8 | -------------------------------------------------------------------------------- /policy-generator/rtu-configs/Alpha_GlobalKnowledge_Normalized_RTU_Configuration.csv: -------------------------------------------------------------------------------- 1 | RtuNo,IOA_M,IOA_Mtt,IOA_C,Description,Name,TagName,LowerBound,UpperBound,DimensionText 2 | 1001,1,5001,10001,Measured Current,M10_I,RTU_GLOBAL_M10_I,-10,10,A 3 | 1001,2,5002,10002,Measured Voltage,M10_V,RTU_GLOBAL_M10_V,-500,500,V 4 | 1001,3,5003,10003,Measured Current,M20_I,RTU_GLOBAL_M20_I,-10,10,A 5 | 1001,4,5004,10004,Measured Voltage,M20_V,RTU_GLOBAL_M20_V,-500,500,V 6 | 1001,5,5005,10005,Measured Current,M11_I,RTU_GLOBAL_M11_I,-10,10,A 7 | 1001,6,5006,10006,Measured Voltage,M11_V,RTU_GLOBAL_M11_V,-500,500,V 8 | 1001,7,5007,10007,Measured Current,M21_I,RTU_GLOBAL_M21_I,-10,10,A 9 | 1001,8,5008,10008,Measured Voltage,M21_V,RTU_GLOBAL_M21_V,-500,500,V 10 | 1001,9,5009,10009,Measured Current,M31_I,RTU_GLOBAL_M31_I,-10,10,A 11 | 1001,10,5010,10010,Measured Voltage,M31_V,RTU_GLOBAL_M31_V,-500,500,V 12 | 1001,11,5011,10011,Measured Current,M41_I,RTU_GLOBAL_M41_I,-10,10,A 13 | 1001,12,5012,10012,Measured Voltage,M41_V,RTU_GLOBAL_M41_V,-500,500,V 14 | 1001,13,5013,10013,Measured Current,M51_I,RTU_GLOBAL_M51_I,-10,10,A 15 | 1001,14,5014,10014,Measured Voltage,M51_V,RTU_GLOBAL_M51_V,-500,500,V 16 | 1001,15,5015,10015,Measured Current,M32_I,RTU_GLOBAL_M32_I,-10,10,A 17 | 1001,16,5016,10016,Measured Voltage,M32_V,RTU_GLOBAL_M32_V,-500,500,V 18 | 1001,17,5017,10017,Measured Current,M42_I,RTU_GLOBAL_M42_I,-10,10,A 19 | 1001,18,5018,10018,Measured Voltage,M42_V,RTU_GLOBAL_M42_V,-500,500,V 20 | 1001,19,5019,10019,Measured Current,M52_I,RTU_GLOBAL_M52_I,-10,10,A 21 | 1001,20,5020,10020,Measured Voltage,M52_V,RTU_GLOBAL_M52_V,-500,500,V 22 | 1001,21,5021,10021,Measured Current,M62_I,RTU_GLOBAL_M62_I,-10,10,A 23 | 1001,22,5022,10022,Measured Voltage,M62_V,RTU_GLOBAL_M62_V,-500,500,V 24 | 1001,23,5023,10023,Measured Current,M72_I,RTU_GLOBAL_M72_I,-10,10,A 25 | 1001,24,5024,10024,Measured Voltage,M72_V,RTU_GLOBAL_M72_V,-500,500,V 26 | 1001,25,5025,10025,Measured Current,M82_I,RTU_GLOBAL_M82_I,-10,10,A 27 | 1001,26,5026,10026,Measured Voltage,M82_V,RTU_GLOBAL_M82_V,-500,500,V 28 | 1001,27,5027,10027,Measured Current,M92_I,RTU_GLOBAL_M92_I,-10,10,A 29 | 1001,28,5028,10028,Measured Voltage,M92_V,RTU_GLOBAL_M92_V,-500,500,V 30 | 1001,29,5029,10029,Measured Current,M63_I,RTU_GLOBAL_M63_I,-10,10,A 31 | 1001,30,5030,10030,Measured Voltage,M63_V,RTU_GLOBAL_M63_V,-500,500,V 32 | 1001,31,5031,10031,Measured Current,M73_I,RTU_GLOBAL_M73_I,-10,10,A 33 | 1001,32,5032,10032,Measured Voltage,M73_V,RTU_GLOBAL_M73_V,-500,500,V 34 | 1001,33,5033,10033,Measured Current,M83_I,RTU_GLOBAL_M83_I,-10,10,A 35 | 1001,34,5034,10034,Measured Voltage,M83_V,RTU_GLOBAL_M83_V,-500,500,V 36 | 1001,35,5035,10035,Measured Current,M93_I,RTU_GLOBAL_M93_I,-10,10,A 37 | 1001,36,5036,10036,Measured Voltage,M93_V,RTU_GLOBAL_M93_V,-500,500,V 38 | 1001,1001,6001,11001,Switch State,SW10_STATE,RTU_GLOBAL_SW10_STATE,,,Closed/Open 39 | 1001,1002,6002,11002,Switch State,SW20_STATE,RTU_GLOBAL_SW20_STATE,,,Closed/Open 40 | 1001,1003,6003,11003,Switch State,SW11_STATE,RTU_GLOBAL_SW11_STATE,,,Closed/Open 41 | 1001,1004,6004,11004,Switch State,SW21_STATE,RTU_GLOBAL_SW21_STATE,,,Closed/Open 42 | 1001,1005,6005,11005,Switch State,SW31_STATE,RTU_GLOBAL_SW31_STATE,,,Closed/Open 43 | 1001,1006,6006,11006,Switch State,SW41_STATE,RTU_GLOBAL_SW41_STATE,,,Closed/Open 44 | 1001,1007,6007,11007,Switch State,SW51_STATE,RTU_GLOBAL_SW51_STATE,,,Closed/Open 45 | 1001,1008,6008,11008,Switch State,SW32_STATE,RTU_GLOBAL_SW32_STATE,,,Closed/Open 46 | 1001,1009,6009,11009,Switch State,SW42_STATE,RTU_GLOBAL_SW42_STATE,,,Closed/Open 47 | 1001,1010,6010,11010,Switch State,SW52_STATE,RTU_GLOBAL_SW52_STATE,,,Closed/Open 48 | 1001,1011,6011,11011,Switch State,SW62_STATE,RTU_GLOBAL_SW62_STATE,,,Closed/Open 49 | 1001,1012,6012,11012,Switch State,SW72_STATE,RTU_GLOBAL_SW72_STATE,,,Closed/Open 50 | 1001,1013,6013,11013,Switch State,SW82_STATE,RTU_GLOBAL_SW82_STATE,,,Closed/Open 51 | 1001,1014,6014,11014,Switch State,SW92_STATE,RTU_GLOBAL_SW92_STATE,,,Closed/Open 52 | 1001,1015,6015,11015,Switch State,SW63_STATE,RTU_GLOBAL_SW63_STATE,,,Closed/Open 53 | 1001,1016,6016,11016,Switch State,SW73_STATE,RTU_GLOBAL_SW73_STATE,,,Closed/Open 54 | 1001,1017,6017,11017,Switch State,SW83_STATE,RTU_GLOBAL_SW83_STATE,,,Closed/Open 55 | 1001,1018,6018,11018,Switch State,SW93_STATE,RTU_GLOBAL_SW93_STATE,,,Closed/Open 56 | 1001,2001,7001,12001,Generated Power,G1_P,RTU_GLOBAL_G1_P,-1000,1000,W 57 | 1001,2002,7002,12002,Generated Power,G2_P,RTU_GLOBAL_G2_P,-1000,1000,W 58 | 1001,2501,7501,12501,Consumed Power,C1_P,RTU_GLOBAL_C1_P,-1000,1000,W 59 | 1001,2502,7502,12502,Consumed Power,C2_P,RTU_GLOBAL_C2_P,-1000,1000,W 60 | 1001,2503,7503,12503,Consumed Power,C3_P,RTU_GLOBAL_C3_P,-1000,1000,W 61 | 1001,2504,7504,12504,Consumed Power,C4_P,RTU_GLOBAL_C4_P,-1000,1000,W 62 | -------------------------------------------------------------------------------- /policy-generator/rtu-configs/Alpha_GlobalKnowledge_RTU_Configuration.csv: -------------------------------------------------------------------------------- 1 | RtuNo,IOA_M,IOA_Mtt,IOA_C,Description,Name,TagName,LowerBound,UpperBound,DimensionText 2 | 1001,1,5001,10001,Measured Current,M10_I,RTU_GLOBAL_M10_I,,,A 3 | 1001,2,5002,10002,Measured Voltage,M10_V,RTU_GLOBAL_M10_V,,,V 4 | 1001,3,5003,10003,Measured Current,M20_I,RTU_GLOBAL_M20_I,,,A 5 | 1001,4,5004,10004,Measured Voltage,M20_V,RTU_GLOBAL_M20_V,,,V 6 | 1001,5,5005,10005,Measured Current,M11_I,RTU_GLOBAL_M11_I,,,A 7 | 1001,6,5006,10006,Measured Voltage,M11_V,RTU_GLOBAL_M11_V,,,V 8 | 1001,7,5007,10007,Measured Current,M21_I,RTU_GLOBAL_M21_I,,,A 9 | 1001,8,5008,10008,Measured Voltage,M21_V,RTU_GLOBAL_M21_V,,,V 10 | 1001,9,5009,10009,Measured Current,M31_I,RTU_GLOBAL_M31_I,,,A 11 | 1001,10,5010,10010,Measured Voltage,M31_V,RTU_GLOBAL_M31_V,,,V 12 | 1001,11,5011,10011,Measured Current,M41_I,RTU_GLOBAL_M41_I,,,A 13 | 1001,12,5012,10012,Measured Voltage,M41_V,RTU_GLOBAL_M41_V,,,V 14 | 1001,13,5013,10013,Measured Current,M51_I,RTU_GLOBAL_M51_I,,,A 15 | 1001,14,5014,10014,Measured Voltage,M51_V,RTU_GLOBAL_M51_V,,,V 16 | 1001,15,5015,10015,Measured Current,M32_I,RTU_GLOBAL_M32_I,,,A 17 | 1001,16,5016,10016,Measured Voltage,M32_V,RTU_GLOBAL_M32_V,,,V 18 | 1001,17,5017,10017,Measured Current,M42_I,RTU_GLOBAL_M42_I,,,A 19 | 1001,18,5018,10018,Measured Voltage,M42_V,RTU_GLOBAL_M42_V,,,V 20 | 1001,19,5019,10019,Measured Current,M52_I,RTU_GLOBAL_M52_I,,,A 21 | 1001,20,5020,10020,Measured Voltage,M52_V,RTU_GLOBAL_M52_V,,,V 22 | 1001,21,5021,10021,Measured Current,M62_I,RTU_GLOBAL_M62_I,,,A 23 | 1001,22,5022,10022,Measured Voltage,M62_V,RTU_GLOBAL_M62_V,,,V 24 | 1001,23,5023,10023,Measured Current,M72_I,RTU_GLOBAL_M72_I,,,A 25 | 1001,24,5024,10024,Measured Voltage,M72_V,RTU_GLOBAL_M72_V,,,V 26 | 1001,25,5025,10025,Measured Current,M82_I,RTU_GLOBAL_M82_I,,,A 27 | 1001,26,5026,10026,Measured Voltage,M82_V,RTU_GLOBAL_M82_V,,,V 28 | 1001,27,5027,10027,Measured Current,M92_I,RTU_GLOBAL_M92_I,,,A 29 | 1001,28,5028,10028,Measured Voltage,M92_V,RTU_GLOBAL_M92_V,,,V 30 | 1001,29,5029,10029,Measured Current,M63_I,RTU_GLOBAL_M63_I,,,A 31 | 1001,30,5030,10030,Measured Voltage,M63_V,RTU_GLOBAL_M63_V,,,V 32 | 1001,31,5031,10031,Measured Current,M73_I,RTU_GLOBAL_M73_I,,,A 33 | 1001,32,5032,10032,Measured Voltage,M73_V,RTU_GLOBAL_M73_V,,,V 34 | 1001,33,5033,10033,Measured Current,M83_I,RTU_GLOBAL_M83_I,,,A 35 | 1001,34,5034,10034,Measured Voltage,M83_V,RTU_GLOBAL_M83_V,,,V 36 | 1001,35,5035,10035,Measured Current,M93_I,RTU_GLOBAL_M93_I,,,A 37 | 1001,36,5036,10036,Measured Voltage,M93_V,RTU_GLOBAL_M93_V,,,V 38 | 1001,1001,6001,11001,Switch State,SW10_STATE,RTU_GLOBAL_SW10_STATE,,,Closed/Open 39 | 1001,1002,6002,11002,Switch State,SW20_STATE,RTU_GLOBAL_SW20_STATE,,,Closed/Open 40 | 1001,1003,6003,11003,Switch State,SW11_STATE,RTU_GLOBAL_SW11_STATE,,,Closed/Open 41 | 1001,1004,6004,11004,Switch State,SW21_STATE,RTU_GLOBAL_SW21_STATE,,,Closed/Open 42 | 1001,1005,6005,11005,Switch State,SW31_STATE,RTU_GLOBAL_SW31_STATE,,,Closed/Open 43 | 1001,1006,6006,11006,Switch State,SW41_STATE,RTU_GLOBAL_SW41_STATE,,,Closed/Open 44 | 1001,1007,6007,11007,Switch State,SW51_STATE,RTU_GLOBAL_SW51_STATE,,,Closed/Open 45 | 1001,1008,6008,11008,Switch State,SW32_STATE,RTU_GLOBAL_SW32_STATE,,,Closed/Open 46 | 1001,1009,6009,11009,Switch State,SW42_STATE,RTU_GLOBAL_SW42_STATE,,,Closed/Open 47 | 1001,1010,6010,11010,Switch State,SW52_STATE,RTU_GLOBAL_SW52_STATE,,,Closed/Open 48 | 1001,1011,6011,11011,Switch State,SW62_STATE,RTU_GLOBAL_SW62_STATE,,,Closed/Open 49 | 1001,1012,6012,11012,Switch State,SW72_STATE,RTU_GLOBAL_SW72_STATE,,,Closed/Open 50 | 1001,1013,6013,11013,Switch State,SW82_STATE,RTU_GLOBAL_SW82_STATE,,,Closed/Open 51 | 1001,1014,6014,11014,Switch State,SW92_STATE,RTU_GLOBAL_SW92_STATE,,,Closed/Open 52 | 1001,1015,6015,11015,Switch State,SW63_STATE,RTU_GLOBAL_SW63_STATE,,,Closed/Open 53 | 1001,1016,6016,11016,Switch State,SW73_STATE,RTU_GLOBAL_SW73_STATE,,,Closed/Open 54 | 1001,1017,6017,11017,Switch State,SW83_STATE,RTU_GLOBAL_SW83_STATE,,,Closed/Open 55 | 1001,1018,6018,11018,Switch State,SW93_STATE,RTU_GLOBAL_SW93_STATE,,,Closed/Open 56 | 1001,2001,7001,12001,Generated Power,G1_P,RTU_GLOBAL_G1_P,,,W 57 | 1001,2002,7002,12002,Generated Power,G2_P,RTU_GLOBAL_G2_P,,,W 58 | 1001,2501,7501,12501,Consumed Power,C1_P,RTU_GLOBAL_C1_P,,,W 59 | 1001,2502,7502,12502,Consumed Power,C2_P,RTU_GLOBAL_C2_P,,,W 60 | 1001,2503,7503,12503,Consumed Power,C3_P,RTU_GLOBAL_C3_P,,,W 61 | 1001,2504,7504,12504,Consumed Power,C4_P,RTU_GLOBAL_C4_P,,,W 62 | -------------------------------------------------------------------------------- /policy-generator/rtu-configs/Alpha_LocalKnowledge_Normalized_RTU_Configuration.csv: -------------------------------------------------------------------------------- 1 | RtuNo,IOA_M,IOA_Mtt,IOA_C,Description,Name,TagName,LowerBound,UpperBound,DimensionText 2 | 1001,1,5001,10001,Measured Current,M10_I,RTU_GLOBAL_M10_I,-10,10,A 3 | 1001,2,5002,10002,Measured Voltage,M10_V,RTU_GLOBAL_M10_V,-500,500,V 4 | 1001,3,5003,10003,Measured Current,M20_I,RTU_GLOBAL_M20_I,-10,10,A 5 | 1001,4,5004,10004,Measured Voltage,M20_V,RTU_GLOBAL_M20_V,-500,500,V 6 | 1001,5,5005,10005,Measured Current,M11_I,RTU_GLOBAL_M11_I,-10,10,A 7 | 1001,6,5006,10006,Measured Voltage,M11_V,RTU_GLOBAL_M11_V,-500,500,V 8 | 1001,7,5007,10007,Measured Current,M21_I,RTU_GLOBAL_M21_I,-10,10,A 9 | 1001,8,5008,10008,Measured Voltage,M21_V,RTU_GLOBAL_M21_V,-500,500,V 10 | 1001,9,5009,10009,Measured Current,M31_I,RTU_GLOBAL_M31_I,-10,10,A 11 | 1001,10,5010,10010,Measured Voltage,M31_V,RTU_GLOBAL_M31_V,-500,500,V 12 | 1001,11,5011,10011,Measured Current,M41_I,RTU_GLOBAL_M41_I,-10,10,A 13 | 1001,12,5012,10012,Measured Voltage,M41_V,RTU_GLOBAL_M41_V,-500,500,V 14 | 1001,13,5013,10013,Measured Current,M51_I,RTU_GLOBAL_M51_I,-10,10,A 15 | 1001,14,5014,10014,Measured Voltage,M51_V,RTU_GLOBAL_M51_V,-500,500,V 16 | 1001,15,5015,10015,Measured Current,M32_I,RTU_GLOBAL_M32_I,-10,10,A 17 | 1001,16,5016,10016,Measured Voltage,M32_V,RTU_GLOBAL_M32_V,-500,500,V 18 | 1001,17,5017,10017,Measured Current,M42_I,RTU_GLOBAL_M42_I,-10,10,A 19 | 1001,18,5018,10018,Measured Voltage,M42_V,RTU_GLOBAL_M42_V,-500,500,V 20 | 1001,19,5019,10019,Measured Current,M52_I,RTU_GLOBAL_M52_I,-10,10,A 21 | 1001,20,5020,10020,Measured Voltage,M52_V,RTU_GLOBAL_M52_V,-500,500,V 22 | 1001,21,5021,10021,Measured Current,M62_I,RTU_GLOBAL_M62_I,-10,10,A 23 | 1001,22,5022,10022,Measured Voltage,M62_V,RTU_GLOBAL_M62_V,-500,500,V 24 | 1001,23,5023,10023,Measured Current,M72_I,RTU_GLOBAL_M72_I,-10,10,A 25 | 1001,24,5024,10024,Measured Voltage,M72_V,RTU_GLOBAL_M72_V,-500,500,V 26 | 1001,25,5025,10025,Measured Current,M82_I,RTU_GLOBAL_M82_I,-10,10,A 27 | 1001,26,5026,10026,Measured Voltage,M82_V,RTU_GLOBAL_M82_V,-500,500,V 28 | 1001,27,5027,10027,Measured Current,M92_I,RTU_GLOBAL_M92_I,-10,10,A 29 | 1001,28,5028,10028,Measured Voltage,M92_V,RTU_GLOBAL_M92_V,-500,500,V 30 | 1001,29,5029,10029,Measured Current,M63_I,RTU_GLOBAL_M63_I,-10,10,A 31 | 1001,30,5030,10030,Measured Voltage,M63_V,RTU_GLOBAL_M63_V,-500,500,V 32 | 1001,31,5031,10031,Measured Current,M73_I,RTU_GLOBAL_M73_I,-10,10,A 33 | 1001,32,5032,10032,Measured Voltage,M73_V,RTU_GLOBAL_M73_V,-500,500,V 34 | 1001,33,5033,10033,Measured Current,M83_I,RTU_GLOBAL_M83_I,-10,10,A 35 | 1001,34,5034,10034,Measured Voltage,M83_V,RTU_GLOBAL_M83_V,-500,500,V 36 | 1001,35,5035,10035,Measured Current,M93_I,RTU_GLOBAL_M93_I,-10,10,A 37 | 1001,36,5036,10036,Measured Voltage,M93_V,RTU_GLOBAL_M93_V,-500,500,V 38 | 1001,1001,6001,11001,Switch State,SW10_STATE,RTU_GLOBAL_SW10_STATE,,,Closed/Open 39 | 1001,1002,6002,11002,Switch State,SW20_STATE,RTU_GLOBAL_SW20_STATE,,,Closed/Open 40 | 1001,1003,6003,11003,Switch State,SW11_STATE,RTU_GLOBAL_SW11_STATE,,,Closed/Open 41 | 1001,1004,6004,11004,Switch State,SW21_STATE,RTU_GLOBAL_SW21_STATE,,,Closed/Open 42 | 1001,1005,6005,11005,Switch State,SW31_STATE,RTU_GLOBAL_SW31_STATE,,,Closed/Open 43 | 1001,1006,6006,11006,Switch State,SW41_STATE,RTU_GLOBAL_SW41_STATE,,,Closed/Open 44 | 1001,1007,6007,11007,Switch State,SW51_STATE,RTU_GLOBAL_SW51_STATE,,,Closed/Open 45 | 1001,1008,6008,11008,Switch State,SW32_STATE,RTU_GLOBAL_SW32_STATE,,,Closed/Open 46 | 1001,1009,6009,11009,Switch State,SW42_STATE,RTU_GLOBAL_SW42_STATE,,,Closed/Open 47 | 1001,1010,6010,11010,Switch State,SW52_STATE,RTU_GLOBAL_SW52_STATE,,,Closed/Open 48 | 1001,1011,6011,11011,Switch State,SW62_STATE,RTU_GLOBAL_SW62_STATE,,,Closed/Open 49 | 1001,1012,6012,11012,Switch State,SW72_STATE,RTU_GLOBAL_SW72_STATE,,,Closed/Open 50 | 1001,1013,6013,11013,Switch State,SW82_STATE,RTU_GLOBAL_SW82_STATE,,,Closed/Open 51 | 1001,1014,6014,11014,Switch State,SW92_STATE,RTU_GLOBAL_SW92_STATE,,,Closed/Open 52 | 1001,1015,6015,11015,Switch State,SW63_STATE,RTU_GLOBAL_SW63_STATE,,,Closed/Open 53 | 1001,1016,6016,11016,Switch State,SW73_STATE,RTU_GLOBAL_SW73_STATE,,,Closed/Open 54 | 1001,1017,6017,11017,Switch State,SW83_STATE,RTU_GLOBAL_SW83_STATE,,,Closed/Open 55 | 1001,1018,6018,11018,Switch State,SW93_STATE,RTU_GLOBAL_SW93_STATE,,,Closed/Open 56 | 1001,2001,7001,12001,Generated Power,G1_P,RTU_GLOBAL_G1_P,-1000,1000,W 57 | 1001,2002,7002,12002,Generated Power,G2_P,RTU_GLOBAL_G2_P,-1000,1000,W 58 | 1001,2501,7501,12501,Consumed Power,C1_P,RTU_GLOBAL_C1_P,-1000,1000,W 59 | 1001,2502,7502,12502,Consumed Power,C2_P,RTU_GLOBAL_C2_P,-1000,1000,W 60 | 1001,2503,7503,12503,Consumed Power,C3_P,RTU_GLOBAL_C3_P,-1000,1000,W 61 | 1001,2504,7504,12504,Consumed Power,C4_P,RTU_GLOBAL_C4_P,-1000,1000,W 62 | -------------------------------------------------------------------------------- /policy-generator/rtu-configs/Alpha_LocalKnowledge_RTU_Configuration.csv: -------------------------------------------------------------------------------- 1 | RtuNo,IOA_M,IOA_Mtt,IOA_C,Description,Name,TagName,LowerBound,UpperBound,DimensionText 2 | 1001,1,5001,10001,Measured Current,M10_I,RTU_GLOBAL_M10_I,,,A 3 | 1001,2,5002,10002,Measured Voltage,M10_V,RTU_GLOBAL_M10_V,,,V 4 | 1001,3,5003,10003,Measured Current,M20_I,RTU_GLOBAL_M20_I,,,A 5 | 1001,4,5004,10004,Measured Voltage,M20_V,RTU_GLOBAL_M20_V,,,V 6 | 1001,5,5005,10005,Measured Current,M11_I,RTU_GLOBAL_M11_I,,,A 7 | 1001,6,5006,10006,Measured Voltage,M11_V,RTU_GLOBAL_M11_V,,,V 8 | 1001,7,5007,10007,Measured Current,M21_I,RTU_GLOBAL_M21_I,,,A 9 | 1001,8,5008,10008,Measured Voltage,M21_V,RTU_GLOBAL_M21_V,,,V 10 | 1001,9,5009,10009,Measured Current,M31_I,RTU_GLOBAL_M31_I,,,A 11 | 1001,10,5010,10010,Measured Voltage,M31_V,RTU_GLOBAL_M31_V,,,V 12 | 1001,11,5011,10011,Measured Current,M41_I,RTU_GLOBAL_M41_I,,,A 13 | 1001,12,5012,10012,Measured Voltage,M41_V,RTU_GLOBAL_M41_V,,,V 14 | 1001,13,5013,10013,Measured Current,M51_I,RTU_GLOBAL_M51_I,,,A 15 | 1001,14,5014,10014,Measured Voltage,M51_V,RTU_GLOBAL_M51_V,,,V 16 | 1001,15,5015,10015,Measured Current,M32_I,RTU_GLOBAL_M32_I,,,A 17 | 1001,16,5016,10016,Measured Voltage,M32_V,RTU_GLOBAL_M32_V,,,V 18 | 1001,17,5017,10017,Measured Current,M42_I,RTU_GLOBAL_M42_I,,,A 19 | 1001,18,5018,10018,Measured Voltage,M42_V,RTU_GLOBAL_M42_V,,,V 20 | 1001,19,5019,10019,Measured Current,M52_I,RTU_GLOBAL_M52_I,,,A 21 | 1001,20,5020,10020,Measured Voltage,M52_V,RTU_GLOBAL_M52_V,,,V 22 | 1001,21,5021,10021,Measured Current,M62_I,RTU_GLOBAL_M62_I,,,A 23 | 1001,22,5022,10022,Measured Voltage,M62_V,RTU_GLOBAL_M62_V,,,V 24 | 1001,23,5023,10023,Measured Current,M72_I,RTU_GLOBAL_M72_I,,,A 25 | 1001,24,5024,10024,Measured Voltage,M72_V,RTU_GLOBAL_M72_V,,,V 26 | 1001,25,5025,10025,Measured Current,M82_I,RTU_GLOBAL_M82_I,,,A 27 | 1001,26,5026,10026,Measured Voltage,M82_V,RTU_GLOBAL_M82_V,,,V 28 | 1001,27,5027,10027,Measured Current,M92_I,RTU_GLOBAL_M92_I,,,A 29 | 1001,28,5028,10028,Measured Voltage,M92_V,RTU_GLOBAL_M92_V,,,V 30 | 1001,29,5029,10029,Measured Current,M63_I,RTU_GLOBAL_M63_I,,,A 31 | 1001,30,5030,10030,Measured Voltage,M63_V,RTU_GLOBAL_M63_V,,,V 32 | 1001,31,5031,10031,Measured Current,M73_I,RTU_GLOBAL_M73_I,,,A 33 | 1001,32,5032,10032,Measured Voltage,M73_V,RTU_GLOBAL_M73_V,,,V 34 | 1001,33,5033,10033,Measured Current,M83_I,RTU_GLOBAL_M83_I,,,A 35 | 1001,34,5034,10034,Measured Voltage,M83_V,RTU_GLOBAL_M83_V,,,V 36 | 1001,35,5035,10035,Measured Current,M93_I,RTU_GLOBAL_M93_I,,,A 37 | 1001,36,5036,10036,Measured Voltage,M93_V,RTU_GLOBAL_M93_V,,,V 38 | 1001,1001,6001,11001,Switch State,SW10_STATE,RTU_GLOBAL_SW10_STATE,,,Closed/Open 39 | 1001,1002,6002,11002,Switch State,SW20_STATE,RTU_GLOBAL_SW20_STATE,,,Closed/Open 40 | 1001,1003,6003,11003,Switch State,SW11_STATE,RTU_GLOBAL_SW11_STATE,,,Closed/Open 41 | 1001,1004,6004,11004,Switch State,SW21_STATE,RTU_GLOBAL_SW21_STATE,,,Closed/Open 42 | 1001,1005,6005,11005,Switch State,SW31_STATE,RTU_GLOBAL_SW31_STATE,,,Closed/Open 43 | 1001,1006,6006,11006,Switch State,SW41_STATE,RTU_GLOBAL_SW41_STATE,,,Closed/Open 44 | 1001,1007,6007,11007,Switch State,SW51_STATE,RTU_GLOBAL_SW51_STATE,,,Closed/Open 45 | 1001,1008,6008,11008,Switch State,SW32_STATE,RTU_GLOBAL_SW32_STATE,,,Closed/Open 46 | 1001,1009,6009,11009,Switch State,SW42_STATE,RTU_GLOBAL_SW42_STATE,,,Closed/Open 47 | 1001,1010,6010,11010,Switch State,SW52_STATE,RTU_GLOBAL_SW52_STATE,,,Closed/Open 48 | 1001,1011,6011,11011,Switch State,SW62_STATE,RTU_GLOBAL_SW62_STATE,,,Closed/Open 49 | 1001,1012,6012,11012,Switch State,SW72_STATE,RTU_GLOBAL_SW72_STATE,,,Closed/Open 50 | 1001,1013,6013,11013,Switch State,SW82_STATE,RTU_GLOBAL_SW82_STATE,,,Closed/Open 51 | 1001,1014,6014,11014,Switch State,SW92_STATE,RTU_GLOBAL_SW92_STATE,,,Closed/Open 52 | 1001,1015,6015,11015,Switch State,SW63_STATE,RTU_GLOBAL_SW63_STATE,,,Closed/Open 53 | 1001,1016,6016,11016,Switch State,SW73_STATE,RTU_GLOBAL_SW73_STATE,,,Closed/Open 54 | 1001,1017,6017,11017,Switch State,SW83_STATE,RTU_GLOBAL_SW83_STATE,,,Closed/Open 55 | 1001,1018,6018,11018,Switch State,SW93_STATE,RTU_GLOBAL_SW93_STATE,,,Closed/Open 56 | 1001,2001,7001,12001,Generated Power,G1_P,RTU_GLOBAL_G1_P,,,W 57 | 1001,2002,7002,12002,Generated Power,G2_P,RTU_GLOBAL_G2_P,,,W 58 | 1001,2501,7501,12501,Consumed Power,C1_P,RTU_GLOBAL_C1_P,,,W 59 | 1001,2502,7502,12502,Consumed Power,C2_P,RTU_GLOBAL_C2_P,,,W 60 | 1001,2503,7503,12503,Consumed Power,C3_P,RTU_GLOBAL_C3_P,,,W 61 | 1001,2504,7504,12504,Consumed Power,C4_P,RTU_GLOBAL_C4_P,,,W 62 | -------------------------------------------------------------------------------- /state-manager/GridComponents/AbstractComponent.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | Every class representing a physical part of the electrical grid is subclass of this abstract component. 6 | This class ensures that every component has a name and offers a function to return all components there are. 7 | Furthermore it can be used for type checks and constraints. 8 | ''' 9 | 10 | 11 | class AbstractComponent(object): 12 | allComponents = [] 13 | allNames = [] 14 | 15 | def __init__(self, name): 16 | """ 17 | Initialize a component with a name. 18 | :param name: Name of the component 19 | """ 20 | assert name 21 | # assert name not in AbstractComponent.allNames 22 | self.name = name 23 | AbstractComponent.allComponents.append(self) 24 | AbstractComponent.allNames.append(name) 25 | 26 | 27 | def getAllComponentsOfType(type): 28 | """ 29 | Return all components of the given type. 30 | :param type: Class that represents the desired grid component 31 | :return: List of all components with the given type 32 | """ 33 | return [c for c in AbstractComponent.allComponents if isinstance(c, type)] 34 | 35 | 36 | def getComponentByName(name): 37 | """ 38 | Return the component of the given name. 39 | :param name: Searched name 40 | :return: Components with the given name 41 | """ 42 | for c in AbstractComponent.allComponents: 43 | if c.name == name: 44 | return c 45 | return None 46 | -------------------------------------------------------------------------------- /state-manager/GridComponents/AbstractDecorator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This abstract class is parent class of every decorator component of the electrical grid model (meter, switch, fuse, protective relay). 6 | ''' 7 | from GridComponents.AbstractComponent import AbstractComponent 8 | 9 | 10 | class AbstractDecorator(AbstractComponent): 11 | def __init__(self, name): 12 | """ 13 | Initialize a decorator type. 14 | :param name: Name of the decorator. 15 | """ 16 | super(AbstractDecorator, self).__init__(name) 17 | 18 | def setLine(self, connectedLine): 19 | """ 20 | Set the connected line for this decorator. 21 | :param connectedLine: Line this decorator is attached to 22 | """ 23 | self.connectedLine = connectedLine 24 | 25 | def setNode(self, connectedNode): 26 | """ 27 | Set the connected node for this decorator. 28 | :param connectedNode: Node with line this decorator is attached to 29 | """ 30 | self.connectedNode = connectedNode 31 | -------------------------------------------------------------------------------- /state-manager/GridComponents/AbstractInterlock.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents an abstract interlock. 6 | ''' 7 | from GridComponents.Switch import Switch 8 | 9 | 10 | class AbstractInterlock(object): 11 | allInterlocks = [] 12 | 13 | def __init__(self, name, interlockedSwitches): 14 | """ 15 | Initialize an interlock. 16 | :param name: Name of the interlock. 17 | :param interlockedSwitches: List of interlocked switches 18 | """ 19 | self.name = name 20 | assert type(interlockedSwitches) == list 21 | self.interlockedSwitches = interlockedSwitches 22 | for switch in self.interlockedSwitches: 23 | assert type(switch) == Switch 24 | switch.interlocks.append(self) 25 | AbstractInterlock.allInterlocks.append(self) 26 | 27 | 28 | def getAllInterlocksOfType(type): 29 | """ 30 | Return all interlocks of the given type. 31 | :param type: DynamicInterlock or StaticInterlock 32 | :return: List of all respective interlocks 33 | """ 34 | return [c for c in AbstractInterlock.allInterlocks if isinstance(c, type)] 35 | -------------------------------------------------------------------------------- /state-manager/GridComponents/AbstractProtectiveDevice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents an abstract protective device. 6 | ''' 7 | from GridComponents.AbstractDecorator import AbstractDecorator 8 | 9 | 10 | class AbstractProtectiveDevice(AbstractDecorator): 11 | def __init__(self, name, cuttingI, cuttingT=0, stateKey=None): 12 | """ 13 | Initialize a fuse. 14 | :param name: Name of the transformer. 15 | :param cuttingI: Cutting current 16 | :param cuttingT: Cutting time 17 | :param stateKey: Key for retrieving circuit state 18 | """ 19 | super(AbstractProtectiveDevice, self).__init__(name) 20 | assert cuttingI 21 | self.cuttingI = cuttingI 22 | self.cuttingT = cuttingT 23 | self.stateKey = stateKey if stateKey else "%s_STATE" % self.name.upper() 24 | -------------------------------------------------------------------------------- /state-manager/GridComponents/Consumer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents a consumer node. 6 | ''' 7 | from GridComponents.AbstractNode import AbstractNode 8 | from LoggerUtilities import logCheckPassed, logCheckDescription, logAllChecksPassed, logAllChecksDescription, \ 9 | logDebugCheckValues, logDebugUnknownValues, logError 10 | from StateManagerUtilities import isClose 11 | from ValueStore import ValueNotStoredException 12 | 13 | 14 | class Consumer(AbstractNode): 15 | def __init__(self, name, linesIn, linesOut, consumedPowerKey=None): 16 | """ 17 | Initialize a consumer. 18 | :param name: Name of the consumer. 19 | :param linesIn: List of ingoing lines 20 | :param linesOut: List of outgoing lines 21 | :param generatedPowerKey: Key for retrieving power state 22 | """ 23 | super(Consumer, self).__init__(name, linesIn, linesOut) 24 | assert len(linesIn) == 1 25 | assert len(linesOut) == 0 26 | self.consumedPowerKey = consumedPowerKey if consumedPowerKey else "%s_P" % self.name.upper() 27 | 28 | def consistencyCheckP5b(self, state): 29 | """ 30 | This consistency rule checks whether P = I * V holds for the consumer. 31 | :param state: State object (observed or calculated) 32 | :return: True if consistency rule holds, False otherwise (violation) 33 | """ 34 | logCheckDescription("P5b", indentation=3) 35 | passed = True 36 | try: 37 | l = self.linesIn[0] 38 | localVoltage = l.retrieveValue(state, self, "local", "voltage") 39 | localCurrent = l.retrieveValue(state, self, "local", "current") 40 | calculatedPower = localVoltage * localCurrent 41 | consumedPower = (-1) * state.retrieveValue(self.consumedPowerKey) 42 | passed = isClose(calculatedPower, consumedPower) 43 | logDebugCheckValues("Consumer %s. V=%f,A=%f. V*A=%f (==) P=%f." % (self.name, localVoltage, localCurrent, calculatedPower, consumedPower), passed, indentation=3) 44 | except ValueNotStoredException, e: 45 | logDebugUnknownValues(e.message, indentation=3) 46 | logCheckPassed("P5b", passed, indentation=3) 47 | return passed 48 | 49 | def executeConsistencyCheck(self, state): 50 | """ 51 | Execute consistency check over this compnent. 52 | :param state: State object which contains state information 53 | """ 54 | logAllChecksDescription("CONSISTENCY", "CONSUMER %s" % self.name, indentation=2) 55 | checkStatus = dict() 56 | try: 57 | checkStatus["P3"] = self.consistencyCheckP3(state) 58 | checkStatus["P4"] = self.consistencyCheckP4(state) 59 | checkStatus["P5b"] = self.consistencyCheckP5b(state) 60 | logAllChecksPassed("CONSISTENCY", "CONSUMER %s" % self.name, all(checkStatus.values()), indentation=2) 61 | except Exception, e: 62 | logError("Unknown exception or error: %s" % e.message, indentation=2) 63 | return checkStatus 64 | 65 | def executeSafetyCheck(self, state): 66 | """ 67 | Execute safety check over this compnent. 68 | :param state: State object which contains state information 69 | """ 70 | logAllChecksDescription("SAFETY", "CONSUMER %s" % self.name, indentation=2) 71 | checkStatus = dict() 72 | try: 73 | checkStatus["R1"] = self.safetyCheckR1(state) 74 | checkStatus["R2"] = self.safetyCheckR2(state) 75 | checkStatus["R3"] = self.safetyCheckR3(state) 76 | checkStatus["R4"] = self.safetyCheckR4(state) 77 | checkStatus["R8a"] = self.safetyCheckR8a(state) 78 | checkStatus["R8b"] = self.safetyCheckR8b(state) 79 | checkStatus["R9a"] = self.safetyCheckR9a(state) 80 | checkStatus["R9b"] = self.safetyCheckR9b(state) 81 | logAllChecksPassed("SAFETY", "CONSUMER %s" % self.name, all(checkStatus.values()), indentation=2) 82 | except Exception, e: 83 | logError("Unknown exception or error: %s" % e.message, indentation=2) 84 | return checkStatus 85 | 86 | def generateBroConsistencyCheck(self): 87 | """Generate consistency check bro rules for this compnent.""" 88 | pass 89 | 90 | def generateBroSafetyCheck(self): 91 | """Generate safety check bro rules for this compnent.""" 92 | pass 93 | -------------------------------------------------------------------------------- /state-manager/GridComponents/DynamicInterlock.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents an dynamic interlock. 6 | ''' 7 | from GridComponents.AbstractInterlock import AbstractInterlock 8 | 9 | 10 | class DynamicInterlock(AbstractInterlock): 11 | def __init__(self, name, interlockedSwitches, guaranteedCurrent): 12 | """ 13 | Initialize an interlock. 14 | :param name: Name of the interlock. 15 | :param interlockedSwitches: List of interlocked switches 16 | :param guaranteedCurrent: Minimum guaranteed current capacity of lines at closed switches 17 | """ 18 | super(DynamicInterlock, self).__init__(name, interlockedSwitches) 19 | self.guaranteedCurrent = guaranteedCurrent 20 | -------------------------------------------------------------------------------- /state-manager/GridComponents/Fuse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents a fuse. 6 | ''' 7 | from GridComponents.AbstractProtectiveDevice import AbstractProtectiveDevice 8 | 9 | 10 | class Fuse(AbstractProtectiveDevice): 11 | def __init__(self, name, cuttingI, cuttingT=0, stateKey=None): 12 | """ 13 | Initialize a fuse. 14 | :param name: Name of the transformer. 15 | :param cuttingI: Cutting current 16 | :param cuttingT: Cutting time 17 | :param stateKey: Key for retrieving circuit state 18 | """ 19 | super(Fuse, self).__init__(name, cuttingI, cuttingT, stateKey) 20 | -------------------------------------------------------------------------------- /state-manager/GridComponents/Generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents a generator node. 6 | ''' 7 | from GridComponents.AbstractNode import AbstractNode 8 | from LoggerUtilities import logCheckPassed, logCheckDescription, logAllChecksPassed, logAllChecksDescription, \ 9 | logDebugCheckValues, logDebugUnknownValues, logError 10 | from StateManagerUtilities import isClose 11 | from ValueStore import ValueNotStoredException 12 | 13 | 14 | class Generator(AbstractNode): 15 | def __init__(self, name, linesIn, linesOut, generatedPowerKey=None): 16 | """ 17 | Initialize a generator. 18 | :param name: Name of the generator. 19 | :param linesIn: List of ingoing lines 20 | :param linesOut: List of outgoing lines 21 | :param generatedPowerKey: Key for retrieving power state 22 | """ 23 | super(Generator, self).__init__(name, linesIn, linesOut) 24 | assert len(linesIn) == 0 25 | assert len(linesOut) == 1 26 | self.generatedPowerKey = generatedPowerKey if generatedPowerKey else "%s_P" % self.name.upper() 27 | 28 | def consistencyCheckP5a(self, state): 29 | """ 30 | This consistency rule checks whether P = I * V holds for the generator. 31 | :param state: State object (observed or calculated) 32 | :return: True if consistency rule holds, False otherwise (violation) 33 | """ 34 | logCheckDescription("P5a", indentation=3) 35 | passed = True 36 | try: 37 | l = self.linesOut[0] 38 | localVoltage = l.retrieveValue(state, self, "local", "voltage") 39 | localCurrent = l.retrieveValue(state, self, "local", "current") 40 | calculatedPower = localVoltage * localCurrent 41 | generatedPower = state.retrieveValue(self.generatedPowerKey) 42 | passed = isClose(localVoltage * localCurrent, generatedPower) 43 | logDebugCheckValues("Generator %s. V=%f,A=%f. V*A=%f (==) P=%f." % (self.name, localVoltage, localCurrent, calculatedPower, generatedPower), passed, indentation=3) 44 | except ValueNotStoredException, e: 45 | logDebugUnknownValues(e.message, indentation=3) 46 | logCheckPassed("P5a", passed, indentation=3) 47 | return passed 48 | 49 | def executeConsistencyCheck(self, state): 50 | """ 51 | Execute consistency check over this compnent. 52 | :param state: State object which contains state information 53 | """ 54 | logAllChecksDescription("CONSISTENCY", "GENERATOR %s" % self.name, indentation=2) 55 | checkStatus = dict() 56 | try: 57 | checkStatus["P3"] = self.consistencyCheckP3(state) 58 | checkStatus["P4"] = self.consistencyCheckP4(state) 59 | checkStatus["P5a"] = self.consistencyCheckP5a(state) 60 | logAllChecksPassed("CONSISTENCY", "GENERATOR %s" % self.name, all(checkStatus.values()), indentation=2) 61 | except Exception, e: 62 | logError("Unknown exception or error: %s" % e.message, indentation=2) 63 | return checkStatus 64 | 65 | def executeSafetyCheck(self, state): 66 | """ 67 | Execute safety check over this compnent. 68 | :param state: State object which contains state information 69 | """ 70 | logAllChecksDescription("SAFETY", "GENERATOR %s" % self.name, indentation=2) 71 | checkStatus = dict() 72 | try: 73 | checkStatus["R1"] = self.safetyCheckR1(state) 74 | checkStatus["R2"] = self.safetyCheckR2(state) 75 | checkStatus["R3"] = self.safetyCheckR3(state) 76 | checkStatus["R4"] = self.safetyCheckR4(state) 77 | checkStatus["R8a"] = self.safetyCheckR8a(state) 78 | checkStatus["R8b"] = self.safetyCheckR8b(state) 79 | checkStatus["R9a"] = self.safetyCheckR9a(state) 80 | checkStatus["R9b"] = self.safetyCheckR9b(state) 81 | logAllChecksPassed("SAFETY", "GENERATOR %s" % self.name, all(checkStatus.values()), indentation=2) 82 | except Exception, e: 83 | logError("Unknown exception or error: %s" % e.message, indentation=2) 84 | return checkStatus 85 | 86 | def generateBroConsistencyCheck(self): 87 | """Generate consistency check bro rules for this compnent.""" 88 | pass 89 | 90 | def generateBroSafetyCheck(self): 91 | """Generate safety check bro rules for this compnent.""" 92 | pass 93 | -------------------------------------------------------------------------------- /state-manager/GridComponents/Meter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents a meter. 6 | ''' 7 | from collections import defaultdict 8 | 9 | from GridComponents.AbstractDecorator import AbstractDecorator 10 | 11 | 12 | class Meter(AbstractDecorator): 13 | meterBySetPointTags = defaultdict(lambda: None) 14 | 15 | def __init__(self, name, currentKey=None, voltageKey=None, setPointIKey=None, setPointVKey=None): 16 | """ 17 | Initialize a meter. 18 | :param name: Name of the meter. 19 | :param currentKey: Key for retrieving measured current state 20 | :param voltageKey: Key for retrieving measured voltage state 21 | """ 22 | super(Meter, self).__init__(name) 23 | self.currentKey = currentKey if currentKey else "%s_I" % self.name.upper() 24 | self.voltageKey = voltageKey if voltageKey else "%s_V" % self.name.upper() 25 | self.setPointIKey = setPointIKey if setPointIKey else "%s_SP_I" % self.name.upper() 26 | self.setPointVKey = setPointVKey if setPointVKey else "%s_SP_V" % self.name.upper() 27 | Meter.meterBySetPointTags[self.setPointIKey] = self 28 | Meter.meterBySetPointTags[self.setPointVKey] = self 29 | 30 | 31 | def getMeterBySetPointTag(tagName): 32 | """ 33 | Return the meter component which contains the given tag. 34 | :param tagName: Tag name 35 | :return: Meter which has the tag 36 | """ 37 | return Meter.meterBySetPointTags[tagName] 38 | -------------------------------------------------------------------------------- /state-manager/GridComponents/ProtectiveRelay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents a protective relay. 6 | ''' 7 | from GridComponents.AbstractProtectiveDevice import AbstractProtectiveDevice 8 | 9 | 10 | class ProtectiveRelay(AbstractProtectiveDevice): 11 | def __init__(self, name, cuttingI, cuttingT=0, stateKey=None): 12 | """ 13 | Initialize a protective relay. 14 | :param name: Name of the protective relay. 15 | :param cuttingI: Cutting current 16 | :param cuttingT: Cutting time 17 | :param stateKey: Key for retrieving circuit state 18 | """ 19 | super(ProtectiveRelay, self).__init__(name, cuttingI, cuttingT, stateKey) 20 | -------------------------------------------------------------------------------- /state-manager/GridComponents/StaticInterlock.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This class represents an static interlock. 6 | ''' 7 | from GridComponents.AbstractInterlock import AbstractInterlock 8 | 9 | 10 | class StaticInterlock(AbstractInterlock): 11 | def __init__(self, name, interlockedSwitches, guaranteedClosedSwitches=1): 12 | """ 13 | Initialize a static interlock. 14 | :param name: Name of the interlock. 15 | :param interlockedSwitches: List of interlocked switches 16 | :param guaranteedClosedSwitches: Minimum guaranteed number of closed switches 17 | """ 18 | super(StaticInterlock, self).__init__(name, interlockedSwitches) 19 | self.guaranteedClosedSwitches = guaranteedClosedSwitches 20 | -------------------------------------------------------------------------------- /state-manager/GridComponents/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | if __name__ == '__main__': 9 | pass 10 | -------------------------------------------------------------------------------- /state-manager/README.md: -------------------------------------------------------------------------------- 1 | This folder contains the Python scripts for the prototype. It contains 1) the topology 2) the state manager and 3) the event engine. 2 | Futhermore, the interface to the prototype's second component, Bro, is built into the event engine. All scenario files are included in this folder. 3 | 4 | The analysis of the electrical grid's state is separated into safety and consistency rules. The model used for those rules is based on the following work: 5 | Chromik, J. J., Remke, A., & Haverkort, B. R. (2016). Improving SCADA security of a local process with a power grid model. Proceedings of the 4th International Symposium for ICS & SCADA Cyber Security Research 2016, 114–123. 6 | 7 | Analyze scenarios that are given in traffic capture files (3 different interactive bash shells in docker container): 8 | ```bash 9 | # First shell: 10 | cd /data/pythontests/ && bro -i eth0 -C T104_BroccoliStateManager_Masterthesis.bro t104.evt 11 | # Second shell: 12 | cd /data/pythontests/ && python TestScenariosBroMasterthesis.py 13 | # Replay traffic: 14 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario1.pcapng 15 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario2.pcapng 16 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario3.pcapng 17 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario4.pcapng 18 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario5.pcapng 19 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario6.pcapng 20 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario7.pcapng 21 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario8.pcapng 22 | tcpreplay --intf1=eth0 /data/pcap/scenarios/Masterthesis_GlobalKnowledge_Normalized_Scenario9.pcapng 23 | # Usage: (Asynchronous key polling): ebug, nfo, arnings, utomatic evaluation on/off, lose, alues print, valuate current state, save state, load state 24 | ``` -------------------------------------------------------------------------------- /state-manager/ScenarioTestScripts/TestScenariosAlpha.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This file starts the Alpha_GlobalKnowledge or Alpha_LocalKnowledge tests directly from the scenario files (without traffic). 6 | Only for testing and model verification purposes while developing. 7 | Important for regression tests. 8 | ''' 9 | import logging 10 | import time 11 | 12 | from LoggerUtilities import initializeLogging 13 | from TestTopologies import initiateTopologyAlpha 14 | from TestUtilities import playScenarios 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | def runAlphaGlobal(): 20 | """Start Alpha_GlobalKnowledge with all scenario cases.""" 21 | playScenarios(caseName="Alpha_GlobalKnowledge", topology=initiateTopologyAlpha()) 22 | 23 | 24 | def runAlphaLocal(): 25 | """Start Alpha_LocalKnowledge with all scenario cases on rtu1.""" 26 | playScenarios(caseName="Alpha_LocalKnowledge", topology=initiateTopologyAlpha(), rtusToTest={"rtu1"}) 27 | 28 | 29 | def runAlphaGlobalOnly1(): 30 | """Start Alpha_GlobalKnowledge with scenario case 1.""" 31 | playScenarios(caseName="Alpha_GlobalKnowledge", topology=initiateTopologyAlpha(), filterFunction=lambda filename: "Scenario1" in filename) 32 | 33 | 34 | if __name__ == '__main__': 35 | initializeLogging(level=logging.INFO, logLevel=False, logLocation=False, logTime=False) 36 | start = time.time() 37 | runAlphaGlobal() 38 | runAlphaLocal() 39 | runAlphaGlobalOnly1() 40 | end = time.time() 41 | logger.info("All tests completed in %2.3fs." % (end - start)) 42 | -------------------------------------------------------------------------------- /state-manager/ScenarioTestScripts/TestScenariosInterlock.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This file starts the Interlock tests directly from the scenario files (without traffic). 6 | Only for testing and model verification purposes while developing. 7 | Important for regression tests. 8 | ''' 9 | import logging 10 | import time 11 | 12 | from LoggerUtilities import initializeLogging 13 | from TestTopologies import initiateTopologyInterlock 14 | from TestUtilities import playScenarios 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | def runInterlock(): 20 | """Start Interlock with all scenario cases.""" 21 | playScenarios(caseName="Interlock", topology=initiateTopologyInterlock(), rtusToTest={"rtu1"}) 22 | 23 | 24 | if __name__ == '__main__': 25 | initializeLogging(level=logging.INFO, logLevel=False, logLocation=False, logTime=False) 26 | start = time.time() 27 | runInterlock() 28 | end = time.time() 29 | logger.info("All tests completed in %2.3fs." % (end - start)) 30 | -------------------------------------------------------------------------------- /state-manager/ScenarioTestScripts/TestScenariosMasterthesis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This file starts the Masterthesis tests directly from the scenario files (without traffic). 6 | Only for testing and model verification purposes while developing. 7 | Important for regression tests. 8 | ''' 9 | import logging 10 | import time 11 | 12 | from LoggerUtilities import initializeLogging 13 | from TestTopologies import initiateTopologyAlpha 14 | from TestUtilities import playScenarios 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | def runAlphaGlobalOnly1(): 20 | """Start Alpha_GlobalKnowledge with scenario case 1.""" 21 | playScenarios(caseName="Masterthesis_GlobalKnowledge", topology=initiateTopologyAlpha(), filterFunction=lambda filename: "Scenario1" in filename) 22 | 23 | 24 | if __name__ == '__main__': 25 | initializeLogging(level=logging.INFO, logLevel=False, logLocation=False, logTime=False) 26 | start = time.time() 27 | runAlphaGlobalOnly1() 28 | end = time.time() 29 | logger.info("All tests completed in %2.3fs." % (end - start)) 30 | -------------------------------------------------------------------------------- /state-manager/ScenarioTestScripts/TestScenariosTransfFuseRelay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This file starts the TransfFuseRelayGlobal tests directly from the scenario files (without traffic). 6 | Only for testing and model verification purposes while developing. 7 | Important for regression tests. 8 | ''' 9 | import logging 10 | import time 11 | 12 | from LoggerUtilities import initializeLogging 13 | from TestTopologies import initiateTopologyTransfFuseRelay 14 | from TestUtilities import playScenarios 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | def runTransfFuseRelayGlobal(): 20 | """Start TransfFuseRelayGlobal with all scenario cases.""" 21 | playScenarios(caseName="TransfFuseRelay", topology=initiateTopologyTransfFuseRelay()) 22 | 23 | 24 | def runTransfFuseRelayGlobalOnly3(): 25 | """Start TransfFuseRelayGlobal with scenario case 3.""" 26 | playScenarios(caseName="TransfFuseRelay", topology=initiateTopologyTransfFuseRelay(), rtusToTest={"rtu1"}, filterFunction=lambda filename: "Scenario3" in filename) 27 | 28 | 29 | if __name__ == '__main__': 30 | initializeLogging(level=logging.INFO, logLevel=False, logLocation=False, logTime=False) 31 | start = time.time() 32 | runTransfFuseRelayGlobal() 33 | runTransfFuseRelayGlobalOnly3() 34 | end = time.time() 35 | logger.info("All tests completed in %2.3fs." % (end - start)) 36 | -------------------------------------------------------------------------------- /state-manager/ScenarioTestScripts/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | if __name__ == '__main__': 9 | pass -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_BasicCase.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_BasicCase 2 | OPTION description Basic Case: Topology from Paper 'What's under the hood?'. 3 | 4 | # Set all switches to closed 5 | MEASUREMENT B RTU_GLOBAL_SW10_STATE True 6 | MEASUREMENT B RTU_GLOBAL_SW20_STATE True 7 | MEASUREMENT B RTU_GLOBAL_SW11_STATE True 8 | MEASUREMENT B RTU_GLOBAL_SW21_STATE True 9 | MEASUREMENT B RTU_GLOBAL_SW31_STATE True 10 | MEASUREMENT B RTU_GLOBAL_SW41_STATE True 11 | MEASUREMENT B RTU_GLOBAL_SW51_STATE True 12 | MEASUREMENT B RTU_GLOBAL_SW32_STATE True 13 | MEASUREMENT B RTU_GLOBAL_SW42_STATE True 14 | MEASUREMENT B RTU_GLOBAL_SW52_STATE True 15 | MEASUREMENT B RTU_GLOBAL_SW62_STATE True 16 | MEASUREMENT B RTU_GLOBAL_SW72_STATE True 17 | MEASUREMENT B RTU_GLOBAL_SW82_STATE True 18 | MEASUREMENT B RTU_GLOBAL_SW92_STATE True 19 | MEASUREMENT B RTU_GLOBAL_SW63_STATE True 20 | MEASUREMENT B RTU_GLOBAL_SW73_STATE True 21 | MEASUREMENT B RTU_GLOBAL_SW83_STATE True 22 | MEASUREMENT B RTU_GLOBAL_SW93_STATE True 23 | 24 | # Set generated and consumed power 25 | MEASUREMENT F RTU_GLOBAL_G1_P 98.9 26 | MEASUREMENT F RTU_GLOBAL_G2_P 52.9 27 | MEASUREMENT F RTU_GLOBAL_C1_P -20.7 28 | MEASUREMENT F RTU_GLOBAL_C2_P -80.5 29 | MEASUREMENT F RTU_GLOBAL_C3_P -29.9 30 | MEASUREMENT F RTU_GLOBAL_C4_P -20.7 31 | 32 | # Set current and voltage for all meters 33 | MEASUREMENT F RTU_GLOBAL_M10_I 0.43 34 | MEASUREMENT F RTU_GLOBAL_M10_V 230 35 | MEASUREMENT F RTU_GLOBAL_M20_I 0.23 36 | MEASUREMENT F RTU_GLOBAL_M20_V 230 37 | MEASUREMENT F RTU_GLOBAL_M11_I 0.43 38 | MEASUREMENT F RTU_GLOBAL_M11_V 230 39 | MEASUREMENT F RTU_GLOBAL_M21_I 0.23 40 | MEASUREMENT F RTU_GLOBAL_M21_V 230 41 | MEASUREMENT F RTU_GLOBAL_M31_I 0.22 42 | MEASUREMENT F RTU_GLOBAL_M31_V 230 43 | MEASUREMENT F RTU_GLOBAL_M41_I 0.22 44 | MEASUREMENT F RTU_GLOBAL_M41_V 230 45 | MEASUREMENT F RTU_GLOBAL_M51_I 0.22 46 | MEASUREMENT F RTU_GLOBAL_M51_V 230 47 | MEASUREMENT F RTU_GLOBAL_M32_I 0.22 48 | MEASUREMENT F RTU_GLOBAL_M32_V 230 49 | MEASUREMENT F RTU_GLOBAL_M42_I 0.22 50 | MEASUREMENT F RTU_GLOBAL_M42_V 230 51 | MEASUREMENT F RTU_GLOBAL_M52_I 0.22 52 | MEASUREMENT F RTU_GLOBAL_M52_V 230 53 | MEASUREMENT F RTU_GLOBAL_M62_I 0.09 54 | MEASUREMENT F RTU_GLOBAL_M62_V 230 55 | MEASUREMENT F RTU_GLOBAL_M72_I 0.35 56 | MEASUREMENT F RTU_GLOBAL_M72_V 230 57 | MEASUREMENT F RTU_GLOBAL_M82_I 0.13 58 | MEASUREMENT F RTU_GLOBAL_M82_V 230 59 | MEASUREMENT F RTU_GLOBAL_M92_I 0.09 60 | MEASUREMENT F RTU_GLOBAL_M92_V 230 61 | MEASUREMENT F RTU_GLOBAL_M63_I 0.09 62 | MEASUREMENT F RTU_GLOBAL_M63_V 230 63 | MEASUREMENT F RTU_GLOBAL_M73_I 0.35 64 | MEASUREMENT F RTU_GLOBAL_M73_V 230 65 | MEASUREMENT F RTU_GLOBAL_M83_I 0.13 66 | MEASUREMENT F RTU_GLOBAL_M83_V 230 67 | MEASUREMENT F RTU_GLOBAL_M93_I 0.09 68 | MEASUREMENT F RTU_GLOBAL_M93_V 230 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario1.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_1 2 | OPTION description Scenario 1: Topology from Paper 'What's under the hood?'. Normal operation. 3 | 4 | # No values set (Scenario 1 is BasicCase) -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario2.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_2 2 | OPTION description Scenario 2: Topology from Paper 'What's under the hood?'. Faulty current sensor. 3 | 4 | # Wrong sensor measurement 5 | MEASUREMENT F RTU_GLOBAL_M41_I 0.35 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario3.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_3 2 | OPTION description Scenario 3: Topology from Paper 'What's under the hood?'. Undesirable command causes too high current on one line. 3 | 4 | # Set one switch to opened 5 | MEASUREMENT B RTU_GLOBAL_SW51_STATE False 6 | 7 | # Set current and voltage for affected meters 8 | MEASUREMENT F RTU_GLOBAL_M51_I 0.0 9 | MEASUREMENT F RTU_GLOBAL_M51_V 0.0 10 | MEASUREMENT F RTU_GLOBAL_M52_I 0.0 11 | MEASUREMENT F RTU_GLOBAL_M52_V 0.0 12 | MEASUREMENT F RTU_GLOBAL_M31_I 0.33 13 | MEASUREMENT F RTU_GLOBAL_M41_I 0.33 14 | MEASUREMENT F RTU_GLOBAL_M32_I 0.33 15 | MEASUREMENT F RTU_GLOBAL_M42_I 0.33 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario4.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_4 2 | OPTION description Scenario 4: Topology from Paper 'What's under the hood?'. Faulty switch sensor. 3 | 4 | # Set one switch to opened, but do not set voltage and current 5 | MEASUREMENT B RTU_GLOBAL_SW51_STATE False -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario5.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_5 2 | OPTION description Scenario 5: Topology from Paper 'What's under the hood?'. Voltage too high. 3 | 4 | # Set voltage on one line too high 5 | MEASUREMENT F RTU_GLOBAL_M51_V 255 6 | MEASUREMENT F RTU_GLOBAL_M52_V 255 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario6.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_6 2 | OPTION description Scenario 6: Topology from Paper 'What's under the hood?'. Generator faulty power sensor. 3 | 4 | # Set a wrong power value for one generator 5 | MEASUREMENT F RTU_GLOBAL_G2_P 70 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario7.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_7 2 | OPTION description Scenario 7: Topology from Paper 'What's under the hood?'. Consumer faulty power sensor. 3 | 4 | # Set a wrong power value for one consumer 5 | MEASUREMENT F RTU_GLOBAL_C1_P 0.0 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_GlobalKnowledge_Scenario8.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_8 2 | OPTION description Scenario 8: Topology from Paper 'What's under the hood?'. Test file for WAIT, C102 and COMMAND commands 3 | 4 | # Set a wrong power value for one consumer 5 | WAIT 2.5 6 | C102 RTU_GLOBAL_C1_P 7 | WAIT 2 8 | COMMAND B RTU_GLOBAL_SW51_STATE False 9 | MEASUREMENT F RTU_GLOBAL_M41_I 0.35 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_LocalKnowledge_BasicCase.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_BasicCase 2 | OPTION description Basic Case: Topology from Paper 'What's under the hood?'. 3 | 4 | # Set all switches to closed 5 | MEASUREMENT B RTU_GLOBAL_SW11_STATE True 6 | MEASUREMENT B RTU_GLOBAL_SW21_STATE True 7 | MEASUREMENT B RTU_GLOBAL_SW31_STATE True 8 | MEASUREMENT B RTU_GLOBAL_SW41_STATE True 9 | MEASUREMENT B RTU_GLOBAL_SW51_STATE True 10 | 11 | # Set current and voltage for all meters 12 | MEASUREMENT F RTU_GLOBAL_M11_I 0.43 13 | MEASUREMENT F RTU_GLOBAL_M11_V 230 14 | MEASUREMENT F RTU_GLOBAL_M21_I 0.23 15 | MEASUREMENT F RTU_GLOBAL_M21_V 230 16 | MEASUREMENT F RTU_GLOBAL_M31_I 0.22 17 | MEASUREMENT F RTU_GLOBAL_M31_V 230 18 | MEASUREMENT F RTU_GLOBAL_M41_I 0.22 19 | MEASUREMENT F RTU_GLOBAL_M41_V 230 20 | MEASUREMENT F RTU_GLOBAL_M51_I 0.22 21 | MEASUREMENT F RTU_GLOBAL_M51_V 230 22 | -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_LocalKnowledge_Scenario1.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_LocalKnowledge_Scenario_1 2 | OPTION description Scenario 1: Topology from Paper 'What's under the hood?'. Normal operation. 3 | 4 | # No values set (Scenario 1 is BasicCase) -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_LocalKnowledge_Scenario2.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_GlobalKnowledge_Scenario_2 2 | OPTION description Scenario 2: Topology from Paper 'What's under the hood?'. Faulty current sensor. 3 | 4 | # Wrong sensor measurement 5 | MEASUREMENT F RTU_GLOBAL_M41_I 0.35 -------------------------------------------------------------------------------- /state-manager/Scenarios/Alpha_LocalKnowledge_Scenario3.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Alpha_LocalKnowledge_Scenario_3 2 | OPTION description Scenario 3: Topology from Paper 'What's under the hood?'. Undesirable command causes too high current on one line. 3 | 4 | # Set one switch to opened 5 | MEASUREMENT B RTU_GLOBAL_SW51_STATE False 6 | 7 | # Set current and voltage for affected meters 8 | MEASUREMENT F RTU_GLOBAL_M51_I 0.0 9 | MEASUREMENT F RTU_GLOBAL_M51_V 0.0 10 | MEASUREMENT F RTU_GLOBAL_M31_I 0.33 11 | MEASUREMENT F RTU_GLOBAL_M41_I 0.33 12 | -------------------------------------------------------------------------------- /state-manager/Scenarios/Interlock_BasicCase.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Interlock_BasicCase 2 | OPTION description Basic Case: Basic local interlock Example. 3 | 4 | # Set all switches to closed 5 | MEASUREMENT B RTU_GLOBAL_SW11_STATE True 6 | MEASUREMENT B RTU_GLOBAL_SW21_STATE True 7 | MEASUREMENT B RTU_GLOBAL_SW31_STATE True 8 | MEASUREMENT B RTU_GLOBAL_SW41_STATE True 9 | 10 | # Set generated and consumed power 11 | MEASUREMENT F RTU_GLOBAL_G1_P 345 12 | MEASUREMENT F RTU_GLOBAL_C1_P -345 13 | 14 | # Set current and voltage for all meters 15 | MEASUREMENT F RTU_GLOBAL_M11_I 1.5 16 | MEASUREMENT F RTU_GLOBAL_M11_V 230 17 | MEASUREMENT F RTU_GLOBAL_M21_I 0.5 18 | MEASUREMENT F RTU_GLOBAL_M21_V 230 19 | MEASUREMENT F RTU_GLOBAL_M31_I 0.5 20 | MEASUREMENT F RTU_GLOBAL_M31_V 230 21 | MEASUREMENT F RTU_GLOBAL_M41_I 0.5 22 | MEASUREMENT F RTU_GLOBAL_M41_V 230 -------------------------------------------------------------------------------- /state-manager/Scenarios/Interlock_Scenario1.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Interlock_Scenario1 2 | OPTION description Scenario 1: Basic local interlock Example. Normal operation. 3 | 4 | # No values set (Scenario 1 is BasicCase) -------------------------------------------------------------------------------- /state-manager/Scenarios/Interlock_Scenario2.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Interlock_Scenario2 2 | OPTION description Scenario 2: Basic local interlock Example. One switch open (okay). 3 | 4 | # No values set (Scenario 1 is BasicCase) 5 | MEASUREMENT B RTU_GLOBAL_SW21_STATE False 6 | 7 | # Set current and voltage for all meters 8 | MEASUREMENT F RTU_GLOBAL_M11_I 1.5 9 | MEASUREMENT F RTU_GLOBAL_M11_V 230 10 | MEASUREMENT F RTU_GLOBAL_M21_I 0 11 | MEASUREMENT F RTU_GLOBAL_M21_V 0 12 | MEASUREMENT F RTU_GLOBAL_M31_I 0.75 13 | MEASUREMENT F RTU_GLOBAL_M31_V 230 14 | MEASUREMENT F RTU_GLOBAL_M41_I 0.75 15 | MEASUREMENT F RTU_GLOBAL_M41_V 230 -------------------------------------------------------------------------------- /state-manager/Scenarios/Interlock_Scenario3.state: -------------------------------------------------------------------------------- 1 | OPTION name To_Interlock_Scenario3 2 | OPTION description Scenario 3: Basic local interlock Example. Two switches open (undesired command). 3 | 4 | # No values set (Scenario 1 is BasicCase) 5 | MEASUREMENT B RTU_GLOBAL_SW21_STATE False 6 | MEASUREMENT B RTU_GLOBAL_SW31_STATE False 7 | 8 | # Set current and voltage for all meters 9 | MEASUREMENT F RTU_GLOBAL_M11_I 1.5 10 | MEASUREMENT F RTU_GLOBAL_M11_V 230 11 | MEASUREMENT F RTU_GLOBAL_M21_I 0 12 | MEASUREMENT F RTU_GLOBAL_M21_V 0 13 | MEASUREMENT F RTU_GLOBAL_M31_I 0 14 | MEASUREMENT F RTU_GLOBAL_M31_V 0 15 | MEASUREMENT F RTU_GLOBAL_M41_I 1.5 16 | MEASUREMENT F RTU_GLOBAL_M41_V 230 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario1.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_1 2 | OPTION description Scenario 1: Topology for scenario evaluation in master thesis. Normal operation. 3 | 4 | # No values set or measured (Scenario 1 is BasicCase) -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario2.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_2 2 | OPTION description Scenario 2: Topology for scenario evaluation in master thesis. Consistency checks: Faulty current, voltage, switch, power and tap position measurement. 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Faulty current sensor at bus1 on line 3 8 | MEASUREMENT F RTU_BUS1_M31_I 0 9 | 10 | # Wait some time and reset... 11 | WAIT 3 12 | MEASUREMENT F RTU_BUS1_M31_I 90 13 | 14 | # Faulty voltage sensor at bus1 on line 3 15 | MEASUREMENT F RTU_BUS1_M31_V 10 16 | 17 | # Wait some time and reset... 18 | WAIT 3 19 | MEASUREMENT F RTU_BUS1_M31_V 10000 20 | 21 | # Faulty switch state reported at bus1 on line 3 22 | MEASUREMENT B RTU_BUS1_SW31_STATE False 23 | 24 | # Wait some time and reset... 25 | WAIT 3 26 | MEASUREMENT B RTU_BUS1_SW31_STATE True 27 | 28 | # Faulty power state reported at generator1 29 | MEASUREMENT F RTU_GENCON_G1_P 700000 30 | 31 | # Wait some time and reset... 32 | WAIT 3 33 | MEASUREMENT F RTU_GENCON_G1_P 1700000 34 | 35 | # Faulty tap position reported at transformer2 36 | MEASUREMENT F RTU_BUS3T_T2_TAP 2 37 | 38 | # Wait some time and reset... 39 | WAIT 3 40 | MEASUREMENT F RTU_BUS3T_T2_TAP 1 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario3.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_3 2 | OPTION description Scenario 3: Topology for scenario evaluation in master thesis. Allowed switch command 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Set switch at bus 1 on line 4 to open 8 | COMMAND B RTU_BUS1_SW41_STATE False 9 | 10 | # Wait some time until new measurements... 11 | WAIT 3 12 | 13 | # New measurements 14 | MEASUREMENT B RTU_BUS1_SW41_STATE False 15 | MEASUREMENT F RTU_BUS1_M31_I 135 16 | MEASUREMENT F RTU_BUS1_M41_I 0 17 | MEASUREMENT F RTU_BUS1_M51_I 135 18 | MEASUREMENT F RTU_BUS2_M32_I 135 19 | MEASUREMENT F RTU_BUS2_M42_I 0 20 | MEASUREMENT F RTU_BUS2_M52_I 135 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario4.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_4 2 | OPTION description Scenario 4: Topology for scenario evaluation in master thesis. Safe second switch command 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Set switch at bus 1 on line 4 to open 8 | COMMAND B RTU_BUS1_SW41_STATE False 9 | 10 | # Wait some time until new measurements... 11 | WAIT 3 12 | 13 | # New measurements 14 | MEASUREMENT B RTU_BUS1_SW41_STATE False 15 | MEASUREMENT F RTU_BUS1_M31_I 135 16 | MEASUREMENT F RTU_BUS1_M41_I 0 17 | MEASUREMENT F RTU_BUS1_M51_I 135 18 | MEASUREMENT F RTU_BUS2_M32_I 135 19 | MEASUREMENT F RTU_BUS2_M42_I 0 20 | MEASUREMENT F RTU_BUS2_M52_I 135 21 | 22 | # Wait some time... 23 | WAIT 3 24 | 25 | # Set switch at bus 1 on line 5 to open 26 | COMMAND B RTU_BUS1_SW51_STATE False 27 | 28 | # Wait some time until new measurements... 29 | WAIT 3 30 | 31 | # New measurements 32 | MEASUREMENT B RTU_BUS1_SW51_STATE False 33 | MEASUREMENT F RTU_BUS1_M31_I 270 34 | MEASUREMENT F RTU_BUS1_M41_I 0 35 | MEASUREMENT F RTU_BUS1_M51_I 0 36 | MEASUREMENT F RTU_BUS2_M32_I 270 37 | MEASUREMENT F RTU_BUS2_M42_I 0 38 | MEASUREMENT F RTU_BUS2_M52_I 0 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario5.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_5 2 | OPTION description Scenario 5: Topology for scenario evaluation in master thesis. Unsafe second switch command 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Set switch at bus 1 on line 4 to open 8 | COMMAND B RTU_BUS1_SW41_STATE False 9 | 10 | # Wait some time until new measurements... 11 | WAIT 3 12 | 13 | # New measurements 14 | MEASUREMENT B RTU_BUS1_SW41_STATE False 15 | MEASUREMENT F RTU_BUS1_M31_I 135 16 | MEASUREMENT F RTU_BUS1_M41_I 0 17 | MEASUREMENT F RTU_BUS1_M51_I 135 18 | MEASUREMENT F RTU_BUS2_M32_I 135 19 | MEASUREMENT F RTU_BUS2_M42_I 0 20 | MEASUREMENT F RTU_BUS2_M52_I 135 21 | 22 | # Wait some time... 23 | WAIT 3 24 | 25 | # Set switch at bus 1 on line 3 to open 26 | COMMAND B RTU_BUS1_SW31_STATE False 27 | 28 | # Wait some time until new measurements... 29 | WAIT 3 30 | 31 | # New measurements 32 | MEASUREMENT B RTU_BUS1_SW31_STATE False 33 | MEASUREMENT F RTU_BUS1_M31_I 0 34 | MEASUREMENT F RTU_BUS1_M41_I 0 35 | MEASUREMENT F RTU_BUS1_M51_I 270 36 | MEASUREMENT F RTU_BUS2_M32_I 0 37 | MEASUREMENT F RTU_BUS2_M42_I 0 38 | MEASUREMENT F RTU_BUS2_M52_I 270 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario6.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_6 2 | OPTION description Scenario 6: Topology for scenario evaluation in master thesis. Safe switch command sequence 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Set switch at bus 2 on line 6 to open 8 | COMMAND B RTU_BUS2_SW62_STATE False 9 | 10 | # Wait some time until new measurements... 11 | WAIT 3 12 | 13 | # New measurements 14 | MEASUREMENT B RTU_BUS2_SW62_STATE False 15 | MEASUREMENT F RTU_BUS2_M62_I 0 16 | MEASUREMENT F RTU_BUS2_M72_I 270 17 | MEASUREMENT F RTU_BUS3_M63_I 0 18 | MEASUREMENT F RTU_BUS3_M73_I 270 19 | 20 | # Wait some time... 21 | WAIT 3 22 | 23 | # Set switch at bus 2 on line 6 to closed 24 | COMMAND B RTU_BUS2_SW62_STATE True 25 | 26 | # Wait some time until new measurements... 27 | WAIT 3 28 | 29 | # New measurements 30 | MEASUREMENT B RTU_BUS2_SW62_STATE True 31 | MEASUREMENT F RTU_BUS2_M62_I 135 32 | MEASUREMENT F RTU_BUS2_M72_I 135 33 | MEASUREMENT F RTU_BUS3_M63_I 135 34 | MEASUREMENT F RTU_BUS3_M73_I 135 35 | 36 | # Wait some time... 37 | WAIT 3 38 | 39 | # Set switch at bus 2 on line 7 to open 40 | COMMAND B RTU_BUS2_SW72_STATE False 41 | 42 | # Wait some time until new measurements... 43 | WAIT 3 44 | 45 | # New measurements 46 | MEASUREMENT B RTU_BUS2_SW72_STATE True 47 | MEASUREMENT F RTU_BUS2_M62_I 270 48 | MEASUREMENT F RTU_BUS2_M72_I 0 49 | MEASUREMENT F RTU_BUS3_M63_I 270 50 | MEASUREMENT F RTU_BUS3_M73_I 0 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario7.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_7 2 | OPTION description Scenario 7: Topology for scenario evaluation in master thesis. Safe and unsafe set point changes. 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Set set points at bus 2 on line 6 and 7 to safe values 8 | COMMAND F RTU_BUS2_M62_SP_I 295 9 | COMMAND F RTU_BUS2_M62_SP_V 9900 10 | COMMAND F RTU_BUS2_M72_SP_I 295 11 | COMMAND F RTU_BUS2_M72_SP_V 9900 12 | 13 | # Wait some time... 14 | WAIT 3 15 | 16 | # New measurements 17 | MEASUREMENT F RTU_BUS2_M62_SP_I 295 18 | MEASUREMENT F RTU_BUS2_M62_SP_V 9900 19 | MEASUREMENT F RTU_BUS2_M72_SP_I 295 20 | MEASUREMENT F RTU_BUS2_M72_SP_V 9900 21 | 22 | # Wait some time... 23 | WAIT 3 24 | 25 | # Set set points at bus 2 on line 6 and 7 to unsafe values 26 | COMMAND F RTU_BUS2_M62_SP_I 200 27 | COMMAND F RTU_BUS2_M62_SP_V 8000 28 | COMMAND F RTU_BUS2_M72_SP_I 400 29 | COMMAND F RTU_BUS2_M72_SP_V 12000 30 | 31 | # Wait some time... 32 | WAIT 3 33 | 34 | # New measurements 35 | MEASUREMENT F RTU_BUS2_M62_SP_I 200 36 | MEASUREMENT F RTU_BUS2_M62_SP_V 8000 37 | MEASUREMENT F RTU_BUS2_M72_SP_I 400 38 | MEASUREMENT F RTU_BUS2_M72_SP_V 12000 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario8.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_8 2 | OPTION description Scenario 8: Topology for scenario evaluation in master thesis. Safe and unsafe transformer tap position changes 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Set transformer tap position of transformer 2 (safe) 8 | COMMAND F RTU_BUS3T_T2_TAP 2 9 | 10 | # Wait some time until new measurements... 11 | WAIT 3 12 | 13 | # New measurements 14 | MEASUREMENT F RTU_BUS3T_T2_TAP 2 15 | MEASUREMENT F RTU_BUS3T_M114_V 5882.353 16 | MEASUREMENT F RTU_BUS3T_M114_I 442 17 | MEASUREMENT F RTU_GENCON_M115_V 5882.353 18 | MEASUREMENT F RTU_GENCON_M115_I 442 19 | 20 | # Wait some time... 21 | WAIT 3 22 | 23 | # Set transformer tap positions of transformer 1 (unsafe) 24 | COMMAND F RTU_BUS3T_T1_TAP 2 25 | 26 | # Wait some time until new measurements... 27 | WAIT 3 28 | 29 | # New measurements 30 | MEASUREMENT F RTU_BUS3T_T1_TAP 2 31 | MEASUREMENT F RTU_BUS3T_M104_V 200 32 | MEASUREMENT F RTU_BUS3T_M104_I 499.99 33 | MEASUREMENT F RTU_GENCON_M105_V 200 34 | MEASUREMENT F RTU_GENCON_M105_I 499.99 -------------------------------------------------------------------------------- /state-manager/Scenarios/Masterthesis_GlobalKnowledge_Scenario9.state: -------------------------------------------------------------------------------- 1 | OPTION name Masterthesis_GlobalKnowledge_Scenario_9 2 | OPTION description Scenario 9: Topology for scenario evaluation in master thesis. Unsafe transformer tap position triggers fuse 3 | 4 | # Wait some time... 5 | WAIT 3 6 | 7 | # Set transformer tap positions of transformer 1 (unsafe) 8 | COMMAND F RTU_BUS3T_T1_TAP 3 9 | 10 | # Wait some time until new measurements... 11 | WAIT 3 12 | 13 | # New measurements 14 | MEASUREMENT F RTU_BUS3T_T1_TAP 3 15 | MEASUREMENT F RTU_BUS3T_M104_V 181.82 16 | MEASUREMENT F RTU_BUS3T_M104_I 550 17 | MEASUREMENT F RTU_GENCON_M105_V 181.82 18 | MEASUREMENT F RTU_GENCON_M105_I 550 19 | 20 | # Wait some time until fuse melts... 21 | WAIT 6 22 | 23 | # New measurements 24 | MEASUREMENT B RTU_BUS3T_FU104_STATE False 25 | MEASUREMENT F RTU_BUS3T_M104_V 0 26 | MEASUREMENT F RTU_BUS3T_M104_I 0 27 | MEASUREMENT F RTU_GENCON_M105_V 0 28 | MEASUREMENT F RTU_GENCON_M105_I 0 29 | 30 | MEASUREMENT F RTU_BUS3_M83_I 0 31 | MEASUREMENT F RTU_BUS3_M83_V 10000 32 | MEASUREMENT F RTU_BUS3T_M84_I 0 33 | MEASUREMENT F RTU_BUS3T_M84_V 10000 34 | 35 | MEASUREMENT F RTU_BUS3_M93_I 270 36 | MEASUREMENT F RTU_BUS3_M93_V 10000 37 | MEASUREMENT F RTU_BUS3T_M94_I 270 38 | MEASUREMENT F RTU_BUS3T_M94_V 10000 39 | 40 | MEASUREMENT F RTU_BUS3T_M114_I 432 41 | MEASUREMENT F RTU_BUS3T_M114_V 6250 42 | MEASUREMENT F RTU_GENCON_M115_I 432 43 | MEASUREMENT F RTU_GENCON_M115_V 6250 44 | 45 | MEASUREMENT F RTU_GENCON_C1_P -0 46 | MEASUREMENT F RTU_GENCON_C2_P -2700000 -------------------------------------------------------------------------------- /state-manager/Scenarios/TransfFuseRelay_BasicCase.state: -------------------------------------------------------------------------------- 1 | OPTION name To_TransfFuseRelay_BasicCase 2 | OPTION description Basic Case: Basic Transformer / Fuse / Protective Relay Example. 3 | 4 | # Set all switches to closed 5 | MEASUREMENT B RTU_GLOBAL_SW10_STATE True 6 | MEASUREMENT B RTU_GLOBAL_SW11_STATE True 7 | MEASUREMENT B RTU_GLOBAL_SW21_STATE True 8 | MEASUREMENT B RTU_GLOBAL_SW22_STATE True 9 | MEASUREMENT B RTU_GLOBAL_SW31_STATE True 10 | MEASUREMENT B RTU_GLOBAL_SW32_STATE True 11 | MEASUREMENT B RTU_GLOBAL_SW42_STATE True 12 | MEASUREMENT B RTU_GLOBAL_SW43_STATE True 13 | MEASUREMENT B RTU_GLOBAL_SW52_STATE True 14 | MEASUREMENT B RTU_GLOBAL_SW53_STATE True 15 | 16 | # Set generated and consumed power 17 | MEASUREMENT F RTU_GLOBAL_G1_P 96 18 | MEASUREMENT F RTU_GLOBAL_C1_P -48 19 | MEASUREMENT F RTU_GLOBAL_C2_P -48 20 | 21 | # Set transformer tap positions 22 | MEASUREMENT F RTU_GLOBAL_T1_TAP 0 23 | MEASUREMENT F RTU_GLOBAL_T2_TAP 0 24 | 25 | # Set fuse and protective relay state 26 | MEASUREMENT B RTU_GLOBAL_F42_STATE True 27 | MEASUREMENT B RTU_GLOBAL_PR52_STATE True 28 | 29 | # Set current and voltage for all meters 30 | MEASUREMENT F RTU_GLOBAL_M10_I 0.01 31 | MEASUREMENT F RTU_GLOBAL_M10_V 9600 32 | MEASUREMENT F RTU_GLOBAL_M11_I 0.01 33 | MEASUREMENT F RTU_GLOBAL_M11_V 9600 34 | MEASUREMENT F RTU_GLOBAL_M21_I 0.005 35 | MEASUREMENT F RTU_GLOBAL_M21_V 9600 36 | MEASUREMENT F RTU_GLOBAL_M22_I 0.005 37 | MEASUREMENT F RTU_GLOBAL_M22_V 9600 38 | MEASUREMENT F RTU_GLOBAL_M31_I 0.005 39 | MEASUREMENT F RTU_GLOBAL_M31_V 9600 40 | MEASUREMENT F RTU_GLOBAL_M32_I 0.005 41 | MEASUREMENT F RTU_GLOBAL_M32_V 9600 42 | MEASUREMENT F RTU_GLOBAL_M42_I 0.2 43 | MEASUREMENT F RTU_GLOBAL_M42_V 240 44 | MEASUREMENT F RTU_GLOBAL_M43_I 0.2 45 | MEASUREMENT F RTU_GLOBAL_M43_V 240 46 | MEASUREMENT F RTU_GLOBAL_M52_I 0.2 47 | MEASUREMENT F RTU_GLOBAL_M52_V 240 48 | MEASUREMENT F RTU_GLOBAL_M53_I 0.2 49 | MEASUREMENT F RTU_GLOBAL_M53_V 240 50 | -------------------------------------------------------------------------------- /state-manager/Scenarios/TransfFuseRelay_Scenario1.state: -------------------------------------------------------------------------------- 1 | OPTION name To_TransfFuseRelay_Scenario1 2 | OPTION description Scenario 1: Basic Transformer / Fuse / Protective Relay Example. Normal operation. 3 | 4 | # No values set (Scenario 1 is BasicCase) -------------------------------------------------------------------------------- /state-manager/Scenarios/TransfFuseRelay_Scenario2.state: -------------------------------------------------------------------------------- 1 | OPTION name To_TransfFuseRelay_Scenario2 2 | OPTION description Scenario 2: Basic Transformer / Fuse / Protective Relay Example. Faulty protective relay state sensor (current flowing, but protective relay says it is open). 3 | 4 | # Set protective relay state 5 | MEASUREMENT B RTU_GLOBAL_PR52_STATE False -------------------------------------------------------------------------------- /state-manager/Scenarios/TransfFuseRelay_Scenario3.state: -------------------------------------------------------------------------------- 1 | OPTION name To_TransfFuseRelay_Scenario3 2 | OPTION description Scenario 3: Basic Transformer / Fuse / Protective Relay Example. Molten fuse. 3 | 4 | # Set fuse state 5 | MEASUREMENT B RTU_GLOBAL_F42_STATE False 6 | 7 | # Set current to 0 on line 2 8 | MEASUREMENT F RTU_GLOBAL_M21_I 0.0 9 | MEASUREMENT F RTU_GLOBAL_M21_V 9600 10 | MEASUREMENT F RTU_GLOBAL_M22_I 0.0 11 | MEASUREMENT F RTU_GLOBAL_M22_V 9600 12 | 13 | # Set voltage and current to 0 on line 4 14 | MEASUREMENT F RTU_GLOBAL_M42_I 0.0 15 | MEASUREMENT F RTU_GLOBAL_M42_V 0 16 | MEASUREMENT F RTU_GLOBAL_M43_I 0.0 17 | MEASUREMENT F RTU_GLOBAL_M43_V 0 18 | 19 | # Adjust generated and consumed power 20 | MEASUREMENT F RTU_GLOBAL_G1_P 48 21 | MEASUREMENT F RTU_GLOBAL_C1_P 0 22 | MEASUREMENT F RTU_GLOBAL_C2_P -48 23 | 24 | # Adjust other measured values 25 | MEASUREMENT F RTU_GLOBAL_M10_I 0.005 26 | MEASUREMENT F RTU_GLOBAL_M10_V 9600 27 | MEASUREMENT F RTU_GLOBAL_M11_I 0.005 28 | MEASUREMENT F RTU_GLOBAL_M11_V 9600 29 | MEASUREMENT F RTU_GLOBAL_M21_I 0.0 30 | MEASUREMENT F RTU_GLOBAL_M21_V 0 31 | MEASUREMENT F RTU_GLOBAL_M22_I 0.0 32 | MEASUREMENT F RTU_GLOBAL_M22_V 0 33 | MEASUREMENT F RTU_GLOBAL_M31_I 0.005 34 | MEASUREMENT F RTU_GLOBAL_M31_V 9600 35 | MEASUREMENT F RTU_GLOBAL_M32_I 0.005 36 | MEASUREMENT F RTU_GLOBAL_M32_V 9600 37 | MEASUREMENT F RTU_GLOBAL_M52_I 0.2 38 | MEASUREMENT F RTU_GLOBAL_M52_V 240 39 | MEASUREMENT F RTU_GLOBAL_M53_I 0.2 40 | MEASUREMENT F RTU_GLOBAL_M53_V 240 41 | -------------------------------------------------------------------------------- /state-manager/Scenarios/TransfFuseRelay_Scenario4.state: -------------------------------------------------------------------------------- 1 | OPTION name To_TransfFuseRelay_Scenario4 2 | OPTION description Scenario 4: Basic Transformer / Fuse / Protective Relay Example. Transformer tap position change (new Voltage okay). 3 | 4 | # Set transformer tap positions 5 | MEASUREMENT F RTU_GLOBAL_T1_TAP 1 6 | 7 | # Set current and voltage for all meters 8 | MEASUREMENT F RTU_GLOBAL_M42_I 0.225 9 | MEASUREMENT F RTU_GLOBAL_M42_V 213.33333 10 | MEASUREMENT F RTU_GLOBAL_M43_I 0.225 11 | MEASUREMENT F RTU_GLOBAL_M43_V 213.33333 12 | -------------------------------------------------------------------------------- /state-manager/Scenarios/TransfFuseRelay_Scenario5.state: -------------------------------------------------------------------------------- 1 | OPTION name To_TransfFuseRelay_Scenario4 2 | OPTION description Scenario 4: Basic Transformer / Fuse / Protective Relay Example. Transformer tap position change (new Voltage too low). 3 | 4 | # Set transformer tap positions 5 | MEASUREMENT F RTU_GLOBAL_T2_TAP 2 6 | 7 | # Set current and voltage for all meters 8 | MEASUREMENT F RTU_GLOBAL_M52_I 0.25 9 | MEASUREMENT F RTU_GLOBAL_M52_V 192 10 | MEASUREMENT F RTU_GLOBAL_M53_I 0.25 11 | MEASUREMENT F RTU_GLOBAL_M53_V 192 12 | -------------------------------------------------------------------------------- /state-manager/StateManagerUtilities.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This file contains utility functions for testing, typechecks and formatting and conversions. 6 | ''' 7 | import datetime 8 | import select 9 | import struct 10 | import sys 11 | import termios 12 | 13 | FLOAT_TOLERANCE_REL = 5 * 1e-02 14 | FLOAT_TOLERANCE_ABS = 1 * 1e-04 15 | ZERO_TOLERANCE = 1 * 1e-04 16 | 17 | 18 | def isClose(a, b, rel_tol=FLOAT_TOLERANCE_REL, abs_tol=FLOAT_TOLERANCE_ABS): 19 | """ 20 | Test whether the float values a and b are approximately equal. 21 | :param a: First float value 22 | :param b: Second float value 23 | :param rel_tol: allowed relative tolerance (relative to bigger float value) 24 | :param abs_tol: allowed absolute tolerance 25 | :return: True if approximately equal under relative or absolute tolerance 26 | """ 27 | return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) 28 | 29 | 30 | def isZero(a): 31 | """ 32 | Test whether float value a is zero. 33 | :param a: Float value 34 | :return: True if a is zero under tolerance of 1e-04 (ZERO_TOLERANCE) 35 | """ 36 | return isClose(a, 0.0, 0.0, ZERO_TOLERANCE) 37 | 38 | 39 | def typecheck(types, ranges=None): 40 | """ 41 | Typecheck annotation utility for function 42 | :param types: Allowed types for values 43 | :param ranges: Allowed value ranges 44 | :return: True if types are allowed 45 | """ 46 | 47 | def __f(f): 48 | def _f(*args, **kwargs): 49 | for a, t in zip(args, types): 50 | if not isinstance(a, t): 51 | raise ValueError("Expected %s got %r" % (t, a)) 52 | for a, r in zip(args, ranges or []): 53 | if r and not r[0] <= a <= r[1]: 54 | raise ValueError("Should be in range %r: %r" % (r, a)) 55 | return f(*args, **kwargs) 56 | 57 | return _f 58 | 59 | return __f 60 | 61 | 62 | def formatTimestamp(timestamp, fileFormat=False): 63 | """ 64 | Format the given timestamp to an human readable format 65 | 66 | :param timestamp: timestamp 67 | :param fileFormat: if True, different delimiter are used (usable for all filesystems) 68 | :return: Timestamp formatted 69 | """ 70 | if timestamp is None: 71 | return None 72 | today = datetime.datetime.fromtimestamp(int(timestamp)) 73 | if fileFormat: 74 | return today.strftime("%Y-%m-%d_%H-%M-%S") 75 | else: 76 | return today.strftime("%d.%m.%Y %H:%M:%S") 77 | 78 | 79 | def normalize_value(value): 80 | """ 81 | Normalize a Bro (as bitarray converted to int) value to an iec-104 normalized value 82 | :param value: Value to normalize 83 | :return: Normalized value 84 | """ 85 | return value / 32768.0 if value / 32768.0 < 1 else (value / 32768.0) - 2 86 | 87 | 88 | def doublefy_value(value): 89 | """ 90 | Convert a Bro (as bitarray converted to int) value to a double value 91 | :param value: Value to convert 92 | :return: Converted value 93 | """ 94 | return struct.unpack('f', struct.pack('I', value))[0] 95 | 96 | 97 | class KeyPoller(): 98 | # From https://stackoverflow.com/questions/13207678/whats-the-simplest-way-of-detecting-keyboard-input-in-python-from-the-terminal 99 | def __enter__(self): 100 | # Save the terminal settings 101 | self.fd = sys.stdin.fileno() 102 | self.new_term = termios.tcgetattr(self.fd) 103 | self.old_term = termios.tcgetattr(self.fd) 104 | # New terminal setting unbuffered 105 | self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO) 106 | termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term) 107 | return self 108 | 109 | def __exit__(self, type, value, traceback): 110 | termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term) 111 | 112 | def poll(self): 113 | dr, dw, de = select.select([sys.stdin], [], [], 0) 114 | if not dr == []: 115 | return sys.stdin.read(1) 116 | return None 117 | -------------------------------------------------------------------------------- /state-manager/TestScenariosBroAlpha.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This script starts the python broccoli interface and state manager to work with Bro on Alpha_GlobalKnowledge and Alpha_LocalKnowledge. 6 | ''' 7 | 8 | import StateManager 9 | from TestTopologies import initiateTopologyAlpha 10 | 11 | if __name__ == '__main__': 12 | StateManager.initializeStateManager(initiateTopologyAlpha, "Alpha") 13 | StateManager.runStateManagerMainLoop() 14 | -------------------------------------------------------------------------------- /state-manager/TestScenariosBroMasterthesis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This script starts the python broccoli interface and state manager to work with Bro on Alpha_GlobalKnowledge and Alpha_LocalKnowledge. 6 | ''' 7 | 8 | import StateManager 9 | from TestTopologies import initiateTopologyMasterthesis 10 | 11 | if __name__ == '__main__': 12 | StateManager.initializeStateManager(initiateTopologyMasterthesis, "Masterthesis") 13 | StateManager.runStateManagerMainLoop() 14 | -------------------------------------------------------------------------------- /state-manager/TestUtilities.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This file offeres generalized test functions for scenario testing 6 | ''' 7 | import logging 8 | import os 9 | 10 | from LoggerUtilities import logAllChecksDescription, logAllChecksPassed, logError 11 | from ValueStore import ValueStore 12 | 13 | logger = logging.getLogger(__name__) 14 | 15 | SCENARIO_PATH = "../../state-manager/Scenarios/" 16 | 17 | 18 | def playScenarios(caseName, topology, rtusToTest=None, filterFunction=lambda filename: True): 19 | """ 20 | Load and test all or some cases of a scenario. 21 | :param caseName: Name of case 22 | :param topology: Topology list of RTUs 23 | :param rtusToTest: RTUs which should be tested 24 | :param filterFunction: Lambda filter function for filenames (e.g. for testing specific cases only) 25 | """ 26 | scenarioFiles = [] 27 | basicCaseFilename = "%s_%s.state" % (caseName, "BasicCase") 28 | basicCaseFound = False 29 | for filename in os.listdir(SCENARIO_PATH): 30 | if filename == basicCaseFilename: 31 | basicCaseFound = True 32 | else: 33 | if filename.startswith(caseName) and filename.endswith(".state"): 34 | scenarioFiles.append(filename) 35 | assert basicCaseFound 36 | logger.warn("Testing %d scenarios of case %s." % (len(filter(filterFunction, sorted(scenarioFiles))), caseName)) 37 | for scenarioFilename in filter(filterFunction, sorted(scenarioFiles)): 38 | stateScenario = ValueStore("T_{o}") 39 | stateScenario.loadFromFile("%s%s" % (SCENARIO_PATH, basicCaseFilename)) 40 | stateScenario.loadFromFile("%s%s" % (SCENARIO_PATH, scenarioFilename)) 41 | logger.warn("") 42 | logger.warn(stateScenario.description) 43 | checkTopology(topology, stateScenario, rtusToTest) 44 | 45 | 46 | def checkTopology(topology, state, rtusToTest=None): 47 | """ 48 | Evaluate all consistency and safety rules on the topology with the given state information 49 | :param topology: Topology list of RTUs 50 | :param state: State object with stateful information 51 | :param rtusToTest: RTUs which should be tested 52 | :return: (T,T) If all tests are successful, (F,T) if consistency violation, (T,F) if safety violation, (F,F) if violation in both 53 | """ 54 | logAllChecksDescription("ALL CHECKS", "TOPOLOGY", indentation=0) 55 | checkStatusConsistency = dict() 56 | checkStatusSafety = dict() 57 | try: 58 | if type(rtusToTest) == set or type(rtusToTest) == list: 59 | relevantRTUs = [rtu for rtu in topology if rtu.name in rtusToTest] 60 | else: 61 | relevantRTUs = topology 62 | for rtu in relevantRTUs: 63 | checkStatusConsistency[rtu.name] = all(rtu.executeFullConsistencyCheck(state).values()) 64 | checkStatusSafety[rtu.name] = all(rtu.executeFullSafetyCheck(state).values()) 65 | logAllChecksPassed("ALL CHECKS", "TOPOLOGY", all(checkStatusConsistency.values()) and all(checkStatusSafety.values()), indentation=0) 66 | except Exception, e: 67 | logError("Unknown exception or error: %s" % e.message, indentation=0) 68 | return (all(checkStatusConsistency.values()), all(checkStatusSafety.values())) 69 | 70 | 71 | def generateRules(topology): 72 | """ 73 | Start the Bro rule generation process. 74 | :param topology: Topology list of RTUs 75 | """ 76 | for rtu in topology: 77 | rtu.generateFullBroConsistencyCheck() 78 | rtu.generateFullBroSafetyCheck() 79 | -------------------------------------------------------------------------------- /traffic-generator/APDUType01.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType01(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, boolValue, dateTime=None): 13 | super(APDUType01, self).__init__(1, commonAddress, infoObjectAddress, dateTime) 14 | assert isinstance(boolValue, bool) 15 | self.value = boolValue 16 | 17 | def toHex(self): 18 | return self.toHexSinglePointValue(qds=False, timetag=False) 19 | -------------------------------------------------------------------------------- /traffic-generator/APDUType03.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType03(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, doublePointValue, dateTime=None): 13 | super(APDUType03, self).__init__(3, commonAddress, infoObjectAddress, dateTime) 14 | assert isinstance(doublePointValue, int) 15 | assert 0 <= doublePointValue and doublePointValue <= 3 16 | self.value = doublePointValue 17 | 18 | def toHex(self): 19 | return self.toHexDoublePointValue(qds=False, timetag=False) 20 | -------------------------------------------------------------------------------- /traffic-generator/APDUType09.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType09(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=None): 13 | super(APDUType09, self).__init__(9, commonAddress, infoObjectAddress, dateTime) 14 | if isinstance(floatValue, int): 15 | floatValue = float(floatValue) 16 | assert isinstance(floatValue, float) 17 | self.value = floatValue 18 | 19 | def toHex(self): 20 | return self.toHexNormalizedValue(qds=True, timetag=False) 21 | -------------------------------------------------------------------------------- /traffic-generator/APDUType102.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType102(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress): 13 | super(APDUType102, self).__init__(102, commonAddress, infoObjectAddress) 14 | 15 | def toHex(self): 16 | return self.toHexC102() 17 | -------------------------------------------------------------------------------- /traffic-generator/APDUType13.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType13(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=None): 13 | super(APDUType13, self).__init__(13, commonAddress, infoObjectAddress, dateTime) 14 | if isinstance(floatValue, int): 15 | floatValue = float(floatValue) 16 | assert isinstance(floatValue, float) 17 | self.value = floatValue 18 | 19 | def toHex(self): 20 | return self.toHexFloatValue(qds=True, timetag=False) 21 | -------------------------------------------------------------------------------- /traffic-generator/APDUType21.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType21(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=None): 13 | super(APDUType21, self).__init__(21, commonAddress, infoObjectAddress, dateTime) 14 | if isinstance(floatValue, int): 15 | floatValue = float(floatValue) 16 | assert isinstance(floatValue, float) 17 | self.value = floatValue 18 | 19 | def toHex(self): 20 | return self.toHexNormalizedValue(qds=False, timetag=False) 21 | -------------------------------------------------------------------------------- /traffic-generator/APDUType30.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | import datetime 9 | 10 | from AbstractAPDU import AbstractAPDU 11 | 12 | 13 | class APDUType30(AbstractAPDU): 14 | def __init__(self, commonAddress, infoObjectAddress, boolValue, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | super(APDUType30, self).__init__(30, commonAddress, infoObjectAddress, dateTime) 16 | assert isinstance(boolValue, bool) 17 | self.value = boolValue 18 | 19 | def toHex(self): 20 | return self.toHexSinglePointValue(qds=False, timetag=True) 21 | -------------------------------------------------------------------------------- /traffic-generator/APDUType31.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | import datetime 9 | 10 | from AbstractAPDU import AbstractAPDU 11 | 12 | 13 | class APDUType31(AbstractAPDU): 14 | def __init__(self, commonAddress, infoObjectAddress, doublePointValue, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | super(APDUType31, self).__init__(31, commonAddress, infoObjectAddress, dateTime) 16 | assert isinstance(doublePointValue, int) 17 | assert 0 <= doublePointValue and doublePointValue <= 3 18 | self.value = doublePointValue 19 | 20 | def toHex(self): 21 | return self.toHexDoublePointValue(qds=False, timetag=True) 22 | -------------------------------------------------------------------------------- /traffic-generator/APDUType34.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | import datetime 9 | 10 | from AbstractAPDU import AbstractAPDU 11 | 12 | 13 | class APDUType34(AbstractAPDU): 14 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | super(APDUType34, self).__init__(34, commonAddress, infoObjectAddress, dateTime) 16 | if isinstance(floatValue, int): 17 | floatValue = float(floatValue) 18 | assert isinstance(floatValue, float) 19 | self.value = floatValue 20 | 21 | def toHex(self): 22 | return self.toHexNormalizedValue(qds=True, timetag=True) 23 | -------------------------------------------------------------------------------- /traffic-generator/APDUType36.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | import datetime 9 | 10 | from AbstractAPDU import AbstractAPDU 11 | 12 | 13 | class APDUType36(AbstractAPDU): 14 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | super(APDUType36, self).__init__(36, commonAddress, infoObjectAddress, dateTime) 16 | if isinstance(floatValue, int): 17 | floatValue = float(floatValue) 18 | assert isinstance(floatValue, float) 19 | self.value = floatValue 20 | 21 | def toHex(self): 22 | return self.toHexFloatValue(qds=True, timetag=True) 23 | -------------------------------------------------------------------------------- /traffic-generator/APDUType45.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType45(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, boolValue, dateTime=None): 13 | super(APDUType45, self).__init__(45, commonAddress, infoObjectAddress, dateTime) 14 | assert isinstance(boolValue, bool) 15 | self.value = boolValue 16 | 17 | def toHex(self): 18 | return self.toHexSinglePointValue(qds=False, timetag=False) 19 | -------------------------------------------------------------------------------- /traffic-generator/APDUType46.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType46(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, doublePointValue, dateTime=None): 13 | super(APDUType46, self).__init__(46, commonAddress, infoObjectAddress, dateTime) 14 | assert isinstance(doublePointValue, int) 15 | assert 0 <= doublePointValue and doublePointValue <= 3 16 | self.value = doublePointValue 17 | 18 | def toHex(self): 19 | return self.toHexDoublePointValue(qds=False, timetag=False) 20 | -------------------------------------------------------------------------------- /traffic-generator/APDUType48.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType48(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=None): 13 | super(APDUType48, self).__init__(48, commonAddress, infoObjectAddress, dateTime) 14 | if isinstance(floatValue, int): 15 | floatValue = float(floatValue) 16 | assert isinstance(floatValue, float) 17 | self.value = floatValue 18 | 19 | def toHex(self): 20 | return self.toHexNormalizedValue(qds=True, timetag=False) 21 | -------------------------------------------------------------------------------- /traffic-generator/APDUType50.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType50(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=None): 13 | super(APDUType50, self).__init__(50, commonAddress, infoObjectAddress, dateTime) 14 | if isinstance(floatValue, int): 15 | floatValue = float(floatValue) 16 | assert isinstance(floatValue, float) 17 | self.value = floatValue 18 | 19 | def toHex(self): 20 | return self.toHexFloatValue(qds=True, timetag=False) 21 | -------------------------------------------------------------------------------- /traffic-generator/APDUType58.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | import datetime 9 | 10 | from AbstractAPDU import AbstractAPDU 11 | 12 | 13 | class APDUType58(AbstractAPDU): 14 | def __init__(self, commonAddress, infoObjectAddress, boolValue, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | super(APDUType58, self).__init__(58, commonAddress, infoObjectAddress, dateTime) 16 | assert isinstance(boolValue, bool) 17 | self.value = boolValue 18 | 19 | def toHex(self): 20 | return self.toHexSinglePointValue(qds=False, timetag=True) 21 | -------------------------------------------------------------------------------- /traffic-generator/APDUType59.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | from AbstractAPDU import AbstractAPDU 9 | 10 | 11 | class APDUType59(AbstractAPDU): 12 | def __init__(self, commonAddress, infoObjectAddress, doublePointValue, dateTime=None): 13 | super(APDUType59, self).__init__(59, commonAddress, infoObjectAddress, dateTime) 14 | assert isinstance(doublePointValue, int) 15 | assert 0 <= doublePointValue and doublePointValue <= 3 16 | self.value = doublePointValue 17 | 18 | def toHex(self): 19 | return self.toHexDoublePointValue(qds=False, timetag=True) 20 | -------------------------------------------------------------------------------- /traffic-generator/APDUType61.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | import datetime 9 | 10 | from AbstractAPDU import AbstractAPDU 11 | 12 | 13 | class APDUType61(AbstractAPDU): 14 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | super(APDUType61, self).__init__(61, commonAddress, infoObjectAddress, dateTime) 16 | if isinstance(floatValue, int): 17 | floatValue = float(floatValue) 18 | assert isinstance(floatValue, float) 19 | self.value = floatValue 20 | 21 | def toHex(self): 22 | return self.toHexNormalizedValue(qds=True, timetag=True) 23 | -------------------------------------------------------------------------------- /traffic-generator/APDUType63.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | 6 | ''' 7 | 8 | import datetime 9 | 10 | from AbstractAPDU import AbstractAPDU 11 | 12 | 13 | class APDUType63(AbstractAPDU): 14 | def __init__(self, commonAddress, infoObjectAddress, floatValue, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | super(APDUType63, self).__init__(63, commonAddress, infoObjectAddress, dateTime) 16 | if isinstance(floatValue, int): 17 | floatValue = float(floatValue) 18 | assert isinstance(floatValue, float) 19 | self.value = floatValue 20 | 21 | def toHex(self): 22 | return self.toHexFloatValue(qds=True, timetag=True) 23 | -------------------------------------------------------------------------------- /traffic-generator/AbstractAPDU.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | ''' 5 | 6 | import binascii 7 | import datetime 8 | import struct 9 | 10 | from TrafficUtilities import datetime_to_cp56time2a, structToHex 11 | 12 | 13 | class AbstractAPDU(object): 14 | def __init__(self, type, commonAddress, infoObjectAddress, dateTime=datetime.datetime(2000, 01, 01, 0, 0, 0)): 15 | assert isinstance(type, int) and type < 256 16 | assert isinstance(commonAddress, int) and commonAddress < 65536 17 | assert isinstance(infoObjectAddress, int) and infoObjectAddress < 16777216 18 | assert isinstance(dateTime, datetime.datetime) 19 | self.type = type 20 | self.ca = commonAddress 21 | self.ioa = infoObjectAddress 22 | self.dateTime = dateTime 23 | 24 | def hexType(self): 25 | return binascii.hexlify(struct.pack("B", self.type)) 26 | 27 | def hexCa(self): 28 | return structToHex(struct.pack('BB', self.ca & 0xFF, self.ca >> 8 & 0xFF)) 29 | 30 | def hexIoa(self): 31 | return structToHex(struct.pack('BBB', self.ioa & 0xFF, self.ioa >> 8 & 0xFF, self.ioa >> 16 & 0xFF)) 32 | 33 | def hexDateTime(self): 34 | return datetime_to_cp56time2a(self.dateTime) 35 | 36 | def hexAPCI(self, asduLength, tx=3, rx=1): 37 | iec104MagicByte = "68" 38 | length = "%02x" % (asduLength / 2 + 4) 39 | tx = binascii.hexlify(struct.pack("H", tx * 2)) 40 | rx = binascii.hexlify(struct.pack("H", rx * 2)) 41 | return iec104MagicByte + length + tx + rx 42 | 43 | def hexTrailer(self): 44 | return self.hexType() + "013100" + self.hexCa() + self.hexIoa() 45 | 46 | def hexFooter(self, qds, timetag): 47 | tmp = "" 48 | if qds: 49 | tmp = tmp + "00" 50 | if timetag: 51 | tmp = tmp + self.hexDateTime() 52 | return tmp 53 | 54 | def toBytes(self): 55 | return self.toHex().decode('hex') 56 | 57 | def toHexSinglePointValue(self, qds, timetag): 58 | # Create ASDU 59 | hexValue = structToHex(struct.pack('B', self.value)) 60 | asdu = self.hexTrailer() + hexValue + self.hexFooter(qds, timetag) 61 | # Create APCI + ASDU hex representation 62 | return self.hexAPCI(len(asdu)) + asdu 63 | 64 | def toHexDoublePointValue(self, qds, timetag): 65 | # Create ASDU 66 | hexValue = structToHex(struct.pack('B', self.value)) 67 | asdu = self.hexTrailer() + hexValue + self.hexFooter(qds, timetag) 68 | # Create APCI + ASDU hex representation 69 | return self.hexAPCI(len(asdu)) + asdu 70 | 71 | def toHexNormalizedValue(self, qds, timetag): 72 | # Create ASDU 73 | hexValue = structToHex(struct.pack(' physical tag 67 | type physical_tag_map_t : table[rtu_number_t,address_t] of physical_tag_t; 68 | 69 | # log format of a physical tag 70 | type log_physical_tag_entry_t: record{ 71 | ts: time &log; 72 | id: conn_id &log; 73 | note: string &log; 74 | physicalTag : physical_tag_t &log; 75 | }; 76 | 77 | # cause of transmission information 78 | type cause_of_transmission:record { 79 | negative : bool; 80 | test : bool; 81 | common_addr : int; 82 | }; 83 | } -------------------------------------------------------------------------------- /workspace/pythontests/T104_STATS_Events.bro: -------------------------------------------------------------------------------- 1 | # This Bro script is printing used iec-104 function codes in realtime and a summary afterwards. 2 | 3 | @load base/protocols/conn 4 | @load T104_DataTypes 5 | @load T104_UtilityFunctions 6 | @load T104_PhysicalTags 7 | 8 | module T104_STATS_Events; 9 | 10 | export{ 11 | redef enum Log::ID += {LOG_stats}; 12 | redef T104_UtilityFunctions::DEBUG_LEVEL=2; 13 | const RTU_NUMBER = 1001; 14 | 15 | type function_id_counter_t : table[count] of count; 16 | global functionIdCounter : function_id_counter_t; 17 | 18 | type info_obj_code_lookup_t : table[count] of string; 19 | global infoObjCodeLookup : info_obj_code_lookup_t; 20 | } 21 | 22 | # Initialization code 23 | event bro_init(){ 24 | #Log::create_stream(T104_STATS_Events::LOG_stats, [$columns=T104_DataTypes::log_physical_tag_entry_t, $path="stats"]); 25 | } 26 | 27 | # Prints information about ASDU 28 | function print_asdu_details(c: connection, rtuNumber:int, infoObjectType: T104::Info_obj_code){ 29 | local note = fmt("[%s] ASDU! RTU %d (%s:%s -> %s:%s)",T104_UtilityFunctions::format_timestamp(network_time()),rtuNumber,c$id$orig_h,c$id$orig_p,c$id$resp_h,c$id$resp_p); 30 | note = note + fmt(": Function ID: %s (%d)",infoObjectType,infoObjectType); 31 | T104_UtilityFunctions::print_debug(note,2); 32 | } 33 | 34 | # Adds information of current ASDU to statistics 35 | function add_statistics(c: connection, rtuNumber:int, infoObjectType: T104::Info_obj_code){ 36 | local functionId = int_to_count(enum_to_int(infoObjectType)); 37 | infoObjCodeLookup[functionId] = fmt("%s",infoObjectType); 38 | if(functionId in functionIdCounter){ 39 | functionIdCounter[functionId]=functionIdCounter[functionId]+1; 40 | } 41 | else { 42 | functionIdCounter[functionId]=1; 43 | } 44 | } 45 | 46 | # Prints statistics of seen ASDUs 47 | function print_statistics(){ 48 | local note = ""; 49 | local usedIds : vector of count; 50 | for (id in functionIdCounter){ 51 | usedIds[|usedIds|]=id; 52 | } 53 | usedIds = sort(usedIds); 54 | T104_UtilityFunctions::print_debug("",2); 55 | T104_UtilityFunctions::print_debug(fmt("Summary:"),2); 56 | T104_UtilityFunctions::print_debug(fmt(":"),2); 57 | for (id in usedIds){ 58 | note= fmt("%4d: %4d",usedIds[id], functionIdCounter[usedIds[id]]); 59 | T104_UtilityFunctions::print_debug(note,2); 60 | } 61 | } 62 | 63 | # ASDU Event 64 | event t104::asdu(c: connection, cause:T104_DataTypes::cause_of_transmission, infoObjectType: T104::Info_obj_code) { 65 | local rtuNumber=RTU_NUMBER; 66 | print_asdu_details(c,rtuNumber,infoObjectType); 67 | add_statistics(c,rtuNumber,infoObjectType); 68 | } 69 | 70 | # Execute after parsing whole traffic dump 71 | event bro_done(){ 72 | print_statistics(); 73 | } 74 | -------------------------------------------------------------------------------- /workspace/pythontests/T104_UtilityFunctions.bro: -------------------------------------------------------------------------------- 1 | # This Bro script provides several utility functions like logging 2 | 3 | @load T104_DataTypes 4 | module T104_UtilityFunctions; 5 | 6 | export{ 7 | const DEBUG_ENABLED = T &redef; 8 | const DEBUG_LEVEL = 1 &redef; # 1... verbose, 2... less verbose 9 | const DEBUG_PRINT_LEVEL = F &redef; 10 | 11 | # Debug output 12 | function print_debug(message : string, level : int &default=1){ 13 | if (DEBUG_ENABLED && level>=DEBUG_LEVEL){ 14 | if (DEBUG_PRINT_LEVEL){ 15 | print fmt("[DEBUG L%d] %s",level,message); 16 | } 17 | else { 18 | print fmt("%s",message); 19 | } 20 | } 21 | } 22 | 23 | # Format a physical tag 24 | function get_physical_tag_note(rtuNumber : T104_DataTypes::rtu_number_t, address : T104_DataTypes::address_t, current_physical_tag : T104_DataTypes::physical_tag_t, printNormalizationInterval : bool &default=F) : string { 25 | if(printNormalizationInterval){ 26 | return fmt("",rtuNumber,address,current_physical_tag$tagName,current_physical_tag$name,current_physical_tag$description,current_physical_tag$dimension,current_physical_tag$normalizationInterval$min,current_physical_tag$normalizationInterval$max); 27 | } else { 28 | return fmt("",rtuNumber,address,current_physical_tag$tagName,current_physical_tag$name,current_physical_tag$description,current_physical_tag$dimension); 29 | } 30 | } 31 | 32 | # Format a timestamp 33 | function format_timestamp(timestamp : time):string { 34 | return strftime("%Y-%m-%d %H:%M:%S", timestamp); 35 | } 36 | 37 | # Convert raw value from bro event into normalized value between -1.0 and 1.0 38 | function normalize_value(raw : T104_DataTypes::raw_value_t):T104_DataTypes::normalized_value_t { 39 | local tmpValue : T104_DataTypes::normalized_value_t = raw/32768.0; 40 | if(tmpValue<=1.0) { 41 | # positive value 42 | return tmpValue; 43 | } else { 44 | # negative value 45 | return tmpValue-2; 46 | } 47 | } 48 | 49 | # Denormalize value from a normalized value between -1.0 and 1.0 into real float value 50 | function denormalize_value(normalized_value : T104_DataTypes::normalized_value_t,normalization_interval : T104_DataTypes::interval_t):T104_DataTypes::normalized_value_t { 51 | local interval_position = (normalized_value + 1)/2.0; 52 | local difference = normalization_interval$max - normalization_interval$min; 53 | return normalization_interval$min + difference * interval_position; 54 | } 55 | 56 | # This function is from T104_DoubleTests.bro 57 | function doublefy_value_test(raw : T104_DataTypes::raw_value_t):T104_DataTypes::normalized_value_t { 58 | local command = fmt("/data/scripts/DoubleConverterC++ %d",raw); 59 | local cmd = Exec::Command($cmd=command); 60 | when (local res = Exec::run(cmd)) 61 | { 62 | return to_double(res$stdout[0]); 63 | } 64 | return -1.0; 65 | } 66 | 67 | # Convert raw double value from bro event into a double value (this is damn slow, no bro builtin for that available) 68 | function doublefy_value(raw : T104_DataTypes::raw_value_t):T104_DataTypes::normalized_value_t { 69 | return doublefy_value_test(raw); 70 | } 71 | 72 | # Interval check (inclusive edges) for normalized (or generally double) values 73 | function interval_check(value: T104_DataTypes::normalized_value_t,allowed_interval: T104_DataTypes::interval_t) : bool{ 74 | return (allowed_interval$min <= value && value <= allowed_interval$max); 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /workspace/pythontests/TestScenariosBroAlpha.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This script starts the python broccoli interface and state manager to work with Bro on Alpha_GlobalKnowledge and Alpha_LocalKnowledge. 6 | ''' 7 | 8 | import StateManager 9 | from TestTopologies import initiateTopologyAlpha 10 | 11 | if __name__ == '__main__': 12 | StateManager.initializeStateManager(initiateTopologyAlpha, "Alpha") 13 | StateManager.runStateManagerMainLoop() 14 | -------------------------------------------------------------------------------- /workspace/pythontests/TestScenariosBroMasterthesis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This script starts the python broccoli interface and state manager to work with Bro on Alpha_GlobalKnowledge and Alpha_LocalKnowledge. 6 | ''' 7 | 8 | import StateManager 9 | from TestTopologies import initiateTopologyMasterthesis 10 | 11 | if __name__ == '__main__': 12 | StateManager.initializeStateManager(initiateTopologyMasterthesis, "Masterthesis") 13 | StateManager.runStateManagerMainLoop() 14 | -------------------------------------------------------------------------------- /workspace/pythontests/TestUtilities.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | ''' 4 | 5 | This file offeres generalized test functions for scenario testing 6 | ''' 7 | import logging 8 | import os 9 | 10 | from LoggerUtilities import logAllChecksDescription, logAllChecksPassed, logError 11 | from ValueStore import ValueStore 12 | 13 | logger = logging.getLogger(__name__) 14 | 15 | SCENARIO_PATH = "../../state-manager/Scenarios/" 16 | 17 | 18 | def playScenarios(caseName, topology, rtusToTest=None, filterFunction=lambda filename: True): 19 | """ 20 | Load and test all or some cases of a scenario. 21 | :param caseName: Name of case 22 | :param topology: Topology list of RTUs 23 | :param rtusToTest: RTUs which should be tested 24 | :param filterFunction: Lambda filter function for filenames (e.g. for testing specific cases only) 25 | """ 26 | scenarioFiles = [] 27 | basicCaseFilename = "%s_%s.state" % (caseName, "BasicCase") 28 | basicCaseFound = False 29 | for filename in os.listdir(SCENARIO_PATH): 30 | if filename == basicCaseFilename: 31 | basicCaseFound = True 32 | else: 33 | if filename.startswith(caseName) and filename.endswith(".state"): 34 | scenarioFiles.append(filename) 35 | assert basicCaseFound 36 | logger.warn("Testing %d scenarios of case %s." % (len(filter(filterFunction, sorted(scenarioFiles))), caseName)) 37 | for scenarioFilename in filter(filterFunction, sorted(scenarioFiles)): 38 | stateScenario = ValueStore("T_{o}") 39 | stateScenario.loadFromFile("%s%s" % (SCENARIO_PATH, basicCaseFilename)) 40 | stateScenario.loadFromFile("%s%s" % (SCENARIO_PATH, scenarioFilename)) 41 | logger.warn("") 42 | logger.warn(stateScenario.description) 43 | checkTopology(topology, stateScenario, rtusToTest) 44 | 45 | 46 | def checkTopology(topology, state, rtusToTest=None): 47 | """ 48 | Evaluate all consistency and safety rules on the topology with the given state information 49 | :param topology: Topology list of RTUs 50 | :param state: State object with stateful information 51 | :param rtusToTest: RTUs which should be tested 52 | :return: (T,T) If all tests are successful, (F,T) if consistency violation, (T,F) if safety violation, (F,F) if violation in both 53 | """ 54 | logAllChecksDescription("ALL CHECKS", "TOPOLOGY", indentation=0) 55 | checkStatusConsistency = dict() 56 | checkStatusSafety = dict() 57 | try: 58 | if type(rtusToTest) == set or type(rtusToTest) == list: 59 | relevantRTUs = [rtu for rtu in topology if rtu.name in rtusToTest] 60 | else: 61 | relevantRTUs = topology 62 | for rtu in relevantRTUs: 63 | checkStatusConsistency[rtu.name] = all(rtu.executeFullConsistencyCheck(state).values()) 64 | checkStatusSafety[rtu.name] = all(rtu.executeFullSafetyCheck(state).values()) 65 | logAllChecksPassed("ALL CHECKS", "TOPOLOGY", all(checkStatusConsistency.values()) and all(checkStatusSafety.values()), indentation=0) 66 | except Exception, e: 67 | logError("Unknown exception or error: %s" % e.message, indentation=0) 68 | return (all(checkStatusConsistency.values()), all(checkStatusSafety.values())) 69 | 70 | 71 | def generateRules(topology): 72 | """ 73 | Start the Bro rule generation process. 74 | :param topology: Topology list of RTUs 75 | """ 76 | for rtu in topology: 77 | rtu.generateFullBroConsistencyCheck() 78 | rtu.generateFullBroSafetyCheck() 79 | -------------------------------------------------------------------------------- /workspace/valueDumps/valueStoreAutosaveDump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjchromik/RuleGeneratorSCADA/ef289d8ba49cdb9e1f716599568c701bec5837d4/workspace/valueDumps/valueStoreAutosaveDump --------------------------------------------------------------------------------