SWaTSimulationManagerNetwork.java 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065
  1. package de.tu_darmstadt.tk.SmartHomeNetworkSim.core;
  2. import java.beans.PropertyChangeListener;
  3. import java.beans.PropertyChangeSupport;
  4. import java.io.File;
  5. import java.io.FileWriter;
  6. import java.io.IOException;
  7. import java.text.SimpleDateFormat;
  8. import java.util.Calendar;
  9. import java.util.Comparator;
  10. import java.util.Date;
  11. import java.util.LinkedHashMap;
  12. import java.util.LinkedList;
  13. import java.util.List;
  14. import java.util.Locale;
  15. import java.util.Random;
  16. import java.util.concurrent.ThreadLocalRandom;
  17. import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.Controller;
  18. import de.tu_darmstadt.tk.SmartHomeNetworkSim.control.NetworkController;
  19. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.devices.SWaTDevice;
  20. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.distributionHandler.SWaTDatasetDistributionHandler;
  21. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.util.Pair;
  22. import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.util.SWaTUtilities;
  23. /**
  24. * The functions of the SWaT network traffic simulation.
  25. * @author Fabian Kaiser
  26. */
  27. public class SWaTSimulationManagerNetwork {
  28. /**
  29. * Network controller for the simulation.
  30. */
  31. private NetworkController networkController;
  32. /**
  33. * The duration of the simulation in seconds.
  34. */
  35. private int durationInSeconds;
  36. /**
  37. * The path of the directory where the output files are saved.
  38. */
  39. private String outputDirectoryPath;
  40. /**
  41. * The start time of the simulation.
  42. */
  43. private Calendar outputFileNameTime;
  44. /**
  45. * The start time of the simulation.
  46. */
  47. private Calendar startTime;
  48. /**
  49. * A list of pairs with all response times as keys and the corresponding CSV strings as values.
  50. */
  51. private LinkedList<Pair<Double, String>> allTimes = new LinkedList<Pair<Double, String>>();
  52. /**
  53. * The transaction IDs that are currently in use and the date they can be reused.
  54. */
  55. private LinkedHashMap<Integer, LinkedList<Pair<Date, Date>>> transactionIDsCurrentlyInUseUntil = new LinkedHashMap<Integer, LinkedList<Pair<Date, Date>>>();
  56. /**
  57. * The distribution handlers for dataset A1 or A6 with the connection names as keys.
  58. */
  59. private LinkedHashMap<String, SWaTDatasetDistributionHandler> distributionHandlers = new LinkedHashMap<String, SWaTDatasetDistributionHandler>();
  60. /**
  61. * Pass-by-reference value to stop the simulation.
  62. */
  63. private boolean[] passByReferenceValue_KeepSimulationRunning;
  64. /**
  65. * Variable needed as Observable.
  66. */
  67. private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
  68. /**
  69. * The results of the simulation from {@link SWaTSimulationManagerPhysical#getSimulationResults()}.
  70. */
  71. private LinkedHashMap<Integer, String[]> physicalSimulationResults;
  72. /**
  73. * A counter for the number of processed connections.
  74. */
  75. private int connectionCounter = 0;
  76. /**
  77. * Counts the number of output files.
  78. */
  79. private int outputFileCounter = 0;
  80. /**
  81. * The number of rows per output file.
  82. */
  83. private int rowsPerOutputFile = 500000;
  84. /**
  85. * The number of the current row of the output file.
  86. */
  87. private int outputFileRowCounter = 0;
  88. /**
  89. * Whether the optimized output is activated.<br>
  90. * The optimization writes the output files already during the simulation instead of waiting
  91. * until the simulation is finished.<br>
  92. * The optimization should be activated if more than 10000 seconds are simulated to prevent
  93. * Out of Memory exceptions.
  94. */
  95. private boolean optimizeOutput = false;
  96. /**
  97. * The constructor.
  98. * @param controller The controller of the scenario.
  99. */
  100. public SWaTSimulationManagerNetwork(Controller controller) {
  101. this.networkController = controller.getNetworkController();
  102. }
  103. /**
  104. * Starts the simulation.
  105. * @param duration The duration of the simulation in seconds.
  106. * @param startTime The start time of the simulation.
  107. * @param outputDirectoryPath The path of the directory for the output files.
  108. * @param propertyChangeListener The PropertyChangeListener to observe this class.
  109. * @param physicalSimulationResults The results of the simulation from {@link SWaTSimulationManagerPhysical#getSimulationResults()}
  110. * @param passByReferenceValue_KeepSimulationRunning The PropertyChangeListener to observe this class.
  111. * @param timeOffset The time offset between the occurrence of a sensor reading in the physical data and the time it is used in network packets.
  112. */
  113. public void startSimulation(String duration, Calendar startTime, String outputDirectoryPath, boolean[] passByReferenceValue_KeepSimulationRunning,
  114. PropertyChangeListener propertyChangeListener, LinkedHashMap<Integer, String[]> physicalSimulationResults, int timeOffset) {
  115. // Reset variables
  116. distributionHandlers = new LinkedHashMap<String, SWaTDatasetDistributionHandler>();
  117. setDatasetDistributionHandlers();
  118. durationInSeconds = Integer.parseInt(duration);
  119. this.outputDirectoryPath = outputDirectoryPath;
  120. this.passByReferenceValue_KeepSimulationRunning = passByReferenceValue_KeepSimulationRunning;
  121. this.propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
  122. outputFileNameTime = (Calendar) startTime.clone();
  123. this.startTime = (Calendar) startTime.clone();
  124. this.startTime.set(Calendar.SECOND, startTime.get(Calendar.SECOND)+timeOffset);
  125. allTimes = new LinkedList<Pair<Double, String>>();
  126. transactionIDsCurrentlyInUseUntil = new LinkedHashMap<Integer, LinkedList<Pair<Date, Date>>>();
  127. this.physicalSimulationResults = physicalSimulationResults;
  128. connectionCounter = 0;
  129. if(durationInSeconds > 10000) optimizeOutput = true;
  130. LinkedList<Thread> threadList = new LinkedList<Thread>();
  131. List<SmartDevice> deviceList = SWaTUtilities.getSortedDeviceList(networkController);
  132. for(SmartDevice device:deviceList) {
  133. connectionCounter = 0;
  134. if(passByReferenceValue_KeepSimulationRunning[0] == false) break;
  135. if(device.getClass().getSimpleName().equals("SWaTDevice") && device.getName().startsWith("Switch") == false) {
  136. Thread deviceThread = new Thread(() -> {
  137. simulateDevice((SWaTDevice)device);
  138. });
  139. deviceThread.start();
  140. threadList.add(deviceThread);
  141. }
  142. }
  143. // Wait for threads to finish
  144. for(Thread thread:threadList)
  145. try {
  146. thread.join();
  147. } catch (InterruptedException e) {
  148. System.out.println("One of the threads of the network simulation was interrupted.");
  149. }
  150. if(passByReferenceValue_KeepSimulationRunning[0] == true) {
  151. if(optimizeOutput == false)
  152. saveCSVOutput();
  153. else
  154. saveCSVOutputRest();
  155. }
  156. }
  157. /**
  158. * Simulates the request packets of a device and the corresponding response packets.
  159. * @param device The device to be simulated.
  160. */
  161. private void simulateDevice(SWaTDevice device){
  162. LinkedList<Thread> threadList = new LinkedList<Thread>();
  163. String deviceName = device.getName();
  164. propertyChangeSupport.firePropertyChange("statusPercentageNetworkSimulation", 0, deviceName);
  165. List<String> elementConnections = SWaTUtilities.getElementConnections(device, networkController);
  166. for(String connection:elementConnections) {
  167. Thread connectionThread = new Thread(() -> {
  168. connectionCounter++;
  169. String sendingDistribution = device.getDeviceData().get(connection+"_"+"sendingCurrent");
  170. String destinationDeviceName = SWaTUtilities.getDestinationDeviceName(connection);
  171. // Construct distribution handler names for dataset A1 and A6 distributions
  172. String distributionHandlerNameRequest = sendingDistribution+"_Request_"+(deviceName+"-"+destinationDeviceName);
  173. String distributionHandlerNameResponse = sendingDistribution+"_Response_"+(destinationDeviceName+"-"+deviceName);
  174. distributionHandlerNameRequest = distributionHandlerNameRequest.replaceAll(" ", "");
  175. distributionHandlerNameResponse = distributionHandlerNameResponse.replaceAll(" ", "");
  176. // Device that send only read tag service requests
  177. LinkedList<String> readTagServiceDevices = new LinkedList<String>();
  178. readTagServiceDevices.add("PLC 1");
  179. readTagServiceDevices.add("PLC 2");
  180. readTagServiceDevices.add("PLC 3");
  181. readTagServiceDevices.add("PLC 4");
  182. readTagServiceDevices.add("PLC 5");
  183. readTagServiceDevices.add("PLC 6");
  184. // CSV columns
  185. String dateRequest;
  186. String dateResponse;
  187. String timeRequest;
  188. String timeResponse;
  189. String orig = "192.168.1.48";
  190. String type = "log";
  191. String ifname = "eth1";
  192. String ifdir = "outbound";
  193. String src = device.getDeviceData().get(connection+"_"+"sourceIPAddress");
  194. String dst;
  195. String proto = "tcp";
  196. String appi_nameRequest;
  197. String appi_nameResponse;
  198. String proxy_src_ip;
  199. String Modbus_Function_Code;
  200. String Modbus_Function_DescriptionRequest;
  201. String Modbus_Function_DescriptionResponse;
  202. String Modbus_Transaction_ID;
  203. String SCADA_Tag = "";
  204. String Modbus_ValueRequest = "Number of Elements: 1";
  205. String Modbus_ValueResponse = "";
  206. String service; // destination port
  207. String s_port = device.getDeviceData().get(connection+"_"+"outboundPort"); // source port
  208. String Tag = "0";
  209. // Simulate request times
  210. List<String> destinationInformation = SWaTUtilities.getConnectionInformation(destinationDeviceName, deviceName, networkController);
  211. LinkedList<Double> requestTimesList = new LinkedList<Double>();
  212. LinkedList<Double> responseTimesList = new LinkedList<Double>();
  213. switch(sendingDistribution) {
  214. case "Constant Value":
  215. double constantValueSending = Double.parseDouble(device.getDeviceData().get(connection+"_sendingConstantValue"));
  216. requestTimesList = simulateConstantValueTimes(null, constantValueSending);
  217. break;
  218. case "Normal Distribution":
  219. double meanSending = Double.parseDouble(device.getDeviceData().get(connection+"_sendingNormalDistributionMean"));
  220. double standardDeviationSending = Double.parseDouble(device.getDeviceData().get(connection+"_sendingNormalDistributionSD"));
  221. requestTimesList = simulateNormalDistributionTimes(null, meanSending, standardDeviationSending);
  222. break;
  223. default: // "Dataset A1 Distribution" || "Dataset A6 Distribution"
  224. if(distributionHandlers.containsKey(distributionHandlerNameRequest) == false)
  225. break;
  226. double requestStandardDeviation = Double.parseDouble(device.getDeviceData().get(connection+"_"+sendingDistribution.replaceAll(" ", "")+"SendingSD"));
  227. requestTimesList = simulateTimesForRequestConnection(distributionHandlerNameRequest, requestStandardDeviation);
  228. }
  229. // Simulate response times
  230. String responseDistribution = destinationInformation.get(2);
  231. switch(responseDistribution) {
  232. case "Constant Value":
  233. double constantValueResponse = Double.parseDouble(device.getDeviceData().get(connection+"_responseConstantValue"));
  234. responseTimesList = simulateConstantValueTimes(requestTimesList, constantValueResponse);
  235. break;
  236. case "Normal Distribution":
  237. double meanResponse = Double.parseDouble(device.getDeviceData().get(connection+"_responseNormalDistributionMean"));
  238. double standardDeviationResponse = Double.parseDouble(device.getDeviceData().get(connection+"_responseNormalDistributionSD"));
  239. responseTimesList = simulateNormalDistributionTimes(requestTimesList, meanResponse, standardDeviationResponse);
  240. break;
  241. default: // "Dataset A1 Distribution" || "Dataset A6 Distribution"
  242. if(distributionHandlers.containsKey(distributionHandlerNameResponse) == false)
  243. break;
  244. double responseStandardDeviation = Double.parseDouble(device.getDeviceData().get(connection+"_"+destinationInformation.get(2).replaceAll(" ", "")+"ResponseSD"));
  245. responseTimesList = simulateTimesForResponseConnection(requestTimesList, distributionHandlerNameResponse, responseStandardDeviation);
  246. }
  247. int responseTimes = responseTimesList.size();
  248. int responseTimesPercent = responseTimes/100;
  249. if(responseTimesPercent == 0) {
  250. responseTimesPercent = 1;
  251. connectionCounter = 0;
  252. }
  253. for(int i = 0; i < responseTimes; i++) {
  254. if(passByReferenceValue_KeepSimulationRunning[0] == false) break;
  255. if(i % responseTimesPercent == 0) propertyChangeSupport.firePropertyChange("statusPercentageNetworkSimulation", 0, deviceName+", Connection "+connectionCounter+" "+(int)(i/responseTimesPercent)+"%");
  256. // Process dates and times
  257. double requestTime = requestTimesList.get(i);
  258. int timeInFullSeconds = (int) requestTime;
  259. Calendar startTimeClone = (Calendar) startTime.clone();
  260. startTimeClone.set(Calendar.SECOND, startTimeClone.get(Calendar.SECOND)+timeInFullSeconds);
  261. Date date = startTimeClone.getTime();
  262. dateRequest = new SimpleDateFormat("ddMMMyyyy",Locale.ROOT).format(date);
  263. timeRequest = new SimpleDateFormat("H:mm:ss").format(date);
  264. double responseTime = responseTimesList.get(i);
  265. timeInFullSeconds = (int) (requestTime + responseTime);
  266. Calendar endTime = (Calendar) startTime.clone();
  267. endTime.set(Calendar.SECOND, endTime.get(Calendar.SECOND)+timeInFullSeconds);
  268. date = endTime.getTime();
  269. dateResponse = new SimpleDateFormat("ddMMMyyyy",Locale.ROOT).format(date);
  270. timeResponse = new SimpleDateFormat("H:mm:ss").format(date);
  271. int currentSimulationTimeInSeconds = (int) (startTimeClone.getTimeInMillis()-startTime.getTimeInMillis())/1000;
  272. // IP addresses
  273. proxy_src_ip = src;
  274. dst = destinationInformation.get(0);
  275. // SCADA
  276. String connectionEnding = connection.substring(connection.length()-5);
  277. if(connectionEnding.startsWith("PLC"))
  278. SCADA_Tag = getSCADATagDatasetA1ForDestinationPLC(Integer.parseInt(connectionEnding.substring(connectionEnding.length()-1)));
  279. // Modbus information
  280. if(readTagServiceDevices.contains(deviceName)) {
  281. appi_nameRequest = "CIP_read_tag_service";
  282. appi_nameResponse = appi_nameRequest;
  283. Modbus_Function_Code = "76";
  284. Modbus_Function_DescriptionRequest= "Read Tag Service";
  285. Modbus_Function_DescriptionResponse = "Read Tag Service - Response";
  286. }
  287. else {
  288. switch(deviceName) {
  289. case "Historian":
  290. String[] appi_nameHistorian =
  291. {"Common Industrial Protocol - get attribute all",
  292. "Common Industrial Protocol - get attribute single",
  293. "Common Industrial Protocol - multiple service packet"};
  294. appi_nameRequest = appi_nameHistorian[new Random().nextInt(appi_nameHistorian.length)];
  295. Modbus_Function_Code = "";
  296. Modbus_Function_DescriptionRequest = "";
  297. Modbus_Function_DescriptionResponse = "";
  298. appi_nameResponse = "Common Industrial Protocol - success";
  299. break;
  300. case "Engineering Workstation":
  301. appi_nameRequest = "CIP_func79";
  302. appi_nameResponse = appi_nameRequest;
  303. Modbus_Function_Code = "79";
  304. Modbus_Function_DescriptionRequest= "Unknown Function Code";
  305. Modbus_Function_DescriptionResponse = "Unknown Function Code - Response";
  306. break;
  307. default: // SCADA
  308. appi_nameRequest = "CIP_func75";
  309. appi_nameResponse = appi_nameRequest;
  310. Modbus_Function_Code = "75";
  311. Modbus_Function_DescriptionRequest= "Unknown Function Code";
  312. Modbus_Function_DescriptionResponse = "Unknown Function Code - Response";
  313. }
  314. Modbus_ValueRequest = "";
  315. Modbus_ValueResponse = "";
  316. SCADA_Tag = "";
  317. }
  318. switch(SCADA_Tag) {
  319. case "":
  320. Modbus_ValueResponse = "";
  321. break;
  322. case "HMI_LIT101":
  323. String sensorReadingLIT101 = SWaTUtilities.convertDoubleStringToSWaTNetworkHexString(physicalSimulationResults.get(currentSimulationTimeInSeconds)[0]);
  324. int[] randomValuesLIT101 = new int[] {32, 34, 36, 44, 96, 98, 100};
  325. Modbus_ValueResponse = sensorReadingLIT101 + "; 0x00 0x00 0x00 0x00; 0x00 0x00 0x00 0x00; 0x00 0x00 0x96 0x44; 0x00 0x00 0x48 0x44; 0x00 0x00 0xfa 0x43; 0x00 0x00 0x7a 0x43; ";
  326. Modbus_ValueResponse += randomValuesLIT101[ThreadLocalRandom.current().nextInt(0, randomValuesLIT101.length)];
  327. Modbus_ValueResponse += "; " + sensorReadingLIT101;
  328. break;
  329. case "HMI_AIT202":
  330. String sensorReadingAIT202 = SWaTUtilities.convertDoubleStringToSWaTNetworkHexString(physicalSimulationResults.get(currentSimulationTimeInSeconds)[1]);
  331. Modbus_ValueResponse = sensorReadingAIT202 + "; 0x00 0x00 0x00 0x00; 0x00 0x00 0x00 0x00; 0x00 0x00 0x20 0x41; 0x9a 0x99 0xe1 0x40; 0x66 0x66 0xde 0x40; 0x00 0x00 0x40 0x40; 34; ";
  332. Modbus_ValueResponse += sensorReadingAIT202;
  333. break;
  334. case "HMI_FIT201":
  335. String sensorReadingFIT201 = SWaTUtilities.convertDoubleStringToSWaTNetworkHexString(physicalSimulationResults.get(currentSimulationTimeInSeconds)[2]);
  336. int[] randomValuesFIT201 = new int[] {0, 2, 12};
  337. Modbus_ValueResponse = sensorReadingFIT201 + "; 0x00 0x00 0x00 0x00; 0x00 0x00 0x00 0x00; 0x00 0x00 0x60 0x40; 0x00 0x00 0x00 0x40; 0x00 0x00 0x00 0x00; 0x00 0x00 0x00 0x00; 0x05 0xab 0x59 0x43; ";
  338. Modbus_ValueResponse += randomValuesFIT201[ThreadLocalRandom.current().nextInt(0, randomValuesFIT201.length)];
  339. Modbus_ValueResponse += "; " + sensorReadingFIT201 +"; 4";
  340. break;
  341. case "HMI_LIT301":
  342. String sensorReadingLIT301 = SWaTUtilities.convertDoubleStringToSWaTNetworkHexString(physicalSimulationResults.get(currentSimulationTimeInSeconds)[3]);
  343. int[] randomValuesLIT301 = new int[] {32, 34, 36, 44, 98, 100};
  344. Modbus_ValueResponse = sensorReadingLIT301 + "; 0x00 0x00 0x00 0x00; 0x00 0x00 0x00 0x00; 0x00 0x00 0x96 0x44; 0x00 0x00 0x7a 0x44; 0x00 0x00 0x2f 0x44; 0x00 0x00 0x7a 0x43; ";
  345. Modbus_ValueResponse += randomValuesLIT301[ThreadLocalRandom.current().nextInt(0, randomValuesLIT301.length)];
  346. Modbus_ValueResponse += "; " + sensorReadingLIT301;
  347. break;
  348. case "HMI_LIT401":
  349. String sensorReadingLIT401 = SWaTUtilities.convertDoubleStringToSWaTNetworkHexString(physicalSimulationResults.get(currentSimulationTimeInSeconds)[4]);
  350. int[] randomValuesLIT401 = new int[] {32, 34, 36, 44, 98, 99, 100};
  351. Modbus_ValueResponse = sensorReadingLIT401 + "; 0x00 0x00 0x00 0x00; 0x00 0x00 0x00 0x00; 0x00 0x80 0x89 0x44; 0x00 0x00 0x7a 0x44; 0x00 0x00 0x2f 0x44; 0x00 0x00 0x7a 0x43; ";
  352. Modbus_ValueResponse += randomValuesLIT401[ThreadLocalRandom.current().nextInt(0, randomValuesLIT401.length)];
  353. Modbus_ValueResponse += "; " + sensorReadingLIT401;
  354. break;
  355. }
  356. // Modbus transaction ID
  357. Calendar endTimeClone = (Calendar) endTime.clone();
  358. endTimeClone.set(Calendar.SECOND, endTimeClone.get(Calendar.SECOND)+1);
  359. Modbus_Transaction_ID = String.valueOf(getValidTransactionID(startTimeClone.getTime(), endTimeClone.getTime()));
  360. // Ports
  361. service = destinationInformation.get(1);
  362. // Tag
  363. Tag = physicalSimulationResults.get(currentSimulationTimeInSeconds)[5];
  364. // Parts of the output
  365. String outputSubString1 = orig+","+type+","+ifname+","+ifdir+","+src+","+dst+","+proto+",";
  366. String outputSubString2 = ","+ proxy_src_ip+","+Modbus_Function_Code+",";
  367. String outputSubString3 = ","+Modbus_Transaction_ID+","+SCADA_Tag+",";
  368. String outputSubString4 = ","+service+","+s_port+","+Tag;
  369. // Complete output
  370. String outputRequest = dateRequest+","+timeRequest+","+outputSubString1+appi_nameRequest+outputSubString2+Modbus_Function_DescriptionRequest+outputSubString3+Modbus_ValueRequest+outputSubString4;
  371. String outputResponse = dateResponse+","+timeResponse+","+outputSubString1+appi_nameResponse+outputSubString2+Modbus_Function_DescriptionResponse+outputSubString3+Modbus_ValueResponse+outputSubString4;
  372. // Special cases
  373. if(sendingDistribution.equals("Dataset A1 Distribution")) {
  374. if(connection.equals("Historian via Switch 2 to Engineering Workstation") || connection.equals("Engineering Workstation via Switch 2 to Historian")){
  375. int timeDistance = 0;
  376. String appi_name1 = "";
  377. String appi_name2 = "Unknown Traffic";
  378. if(connection.equals("Historian via Switch 2 to Engineering Workstation")) {
  379. // Combination of two packets with 50 seconds time between them and no responses
  380. timeDistance = 50;
  381. appi_name1 = "DCE-RPC Protocol";
  382. }
  383. else { // Engineering Workstation via Switch 2 to Historian
  384. // Combination of two packets with 42 or 22 seconds time between them with probability 2/5 and 3/5 and no responses
  385. int[] probabilities = {42, 42, 22, 22, 22};
  386. timeDistance = probabilities[new Random().nextInt(probabilities.length)];
  387. appi_name1 = "OSIsoft PI";
  388. }
  389. Calendar specialTime = (Calendar) startTimeClone.clone();
  390. specialTime.set(Calendar.SECOND, specialTime.get(Calendar.SECOND)+timeDistance);
  391. Date specialDate = specialTime.getTime();
  392. String dateSpecialOutput2 = new SimpleDateFormat("ddMMMyyyy").format(specialDate);
  393. String timeSpecialOutput2 = new SimpleDateFormat("H:mm:ss").format(specialDate);
  394. String output1 = dateRequest+","+timeRequest+","+outputSubString1+","+appi_name1+","+ proxy_src_ip+",,,,,,"+outputSubString4;
  395. String output2 = dateSpecialOutput2+","+timeSpecialOutput2+","+outputSubString1+","+appi_name2+","+ proxy_src_ip+",,,,,,"+outputSubString4;
  396. appendToAllTimesSynchronized(new Pair<Double, String>(requestTime, output1));
  397. appendToAllTimesSynchronized(new Pair<Double, String>(requestTime+timeDistance, output2));
  398. break;
  399. }
  400. else if(connection.equals("Engineering Workstation via Switch 2 to SCADA")) {
  401. // Packet without response
  402. String output1 = dateRequest+","+timeRequest+","+outputSubString1+",VNC,"+ proxy_src_ip+",,,,,,"+outputSubString4;
  403. appendToAllTimesSynchronized(new Pair<Double, String>(requestTime, output1));
  404. break;
  405. }
  406. else if(connection.startsWith("SCADA via Switch 2 and Switch 1 to PLC")) {
  407. // Very unpredictable packets that only occur at the very beginning and the very end of dataset A1
  408. break;
  409. }
  410. }
  411. // Add to list
  412. appendToAllTimesSynchronized(new Pair<Double, String>(requestTime, outputRequest));
  413. appendToAllTimesSynchronized(new Pair<Double, String>(requestTime+responseTime, outputResponse));
  414. }
  415. });
  416. connectionThread.start();
  417. threadList.add(connectionThread);
  418. }
  419. // Wait for threads to finish
  420. for(Thread thread:threadList)
  421. try {
  422. thread.join();
  423. } catch (InterruptedException e) {
  424. System.out.println("One of the threads of the network simulation was interrupted.");
  425. }
  426. }
  427. /**
  428. * Simulates the times of requests for a connection.
  429. * @param connectionHandlerName The name of the connection handler.
  430. * @param requestStandardDeviation The standard deviation of requests for this connection.
  431. * @return The times of requests for a connection.
  432. */
  433. private LinkedList<Double> simulateTimesForRequestConnection(String connectionHandlerName, double requestStandardDeviation){
  434. LinkedList<Double> times = new LinkedList<Double>();
  435. SWaTDatasetDistributionHandler distributionHandler = distributionHandlers.get(connectionHandlerName);
  436. distributionHandler.setBandwidth(requestStandardDeviation);
  437. double simulatedTime = 0.0;
  438. while(true) {
  439. double nextValue = distributionHandler.sampleNextValueExactly();
  440. if(simulatedTime+nextValue > durationInSeconds)
  441. break; // Stop simulation if it would exceed the duration
  442. else {
  443. // Add new request times to the variables
  444. simulatedTime += nextValue;
  445. times.add(simulatedTime);
  446. }
  447. }
  448. return times;
  449. }
  450. /**
  451. * Simulates the times of responses to requests.
  452. * @param requestTimes The list of request times.
  453. * @param connectionHandlerName The name of the connection handler.
  454. * @param responseStandardDeviation The standard deviation of responses for this connection.
  455. * @return
  456. */
  457. private LinkedList<Double> simulateTimesForResponseConnection(LinkedList<Double> requestTimes, String connectionHandlerName, double responseStandardDeviation){
  458. LinkedList<Double> times = new LinkedList<Double>();
  459. SWaTDatasetDistributionHandler distributionHandler = distributionHandlers.get(connectionHandlerName);
  460. distributionHandler.setBandwidth(responseStandardDeviation);
  461. for(Double requestTime:requestTimes) {
  462. double nextValue = distributionHandler.sampleNextValueExactly();
  463. if(requestTime+nextValue > durationInSeconds)
  464. break; // Stop simulation if it would exceed the duration
  465. else {
  466. times.add(nextValue);
  467. }
  468. }
  469. return times;
  470. }
  471. /**
  472. * Simulates the times for constant value distributions.
  473. * @param requestTimes The list of request times or null if the request times are to be sampled.
  474. * @param constantValue The constant value of the distribution.
  475. * @return The times for constant value distributions.
  476. */
  477. private LinkedList<Double> simulateConstantValueTimes(LinkedList<Double> requestTimes, double constantValue){
  478. LinkedList<Double> times = new LinkedList<Double>();
  479. if(requestTimes != null) { // Responses
  480. for(Double requestTime:requestTimes) {
  481. if(requestTime+constantValue > durationInSeconds)
  482. break; // Stop simulation if it would exceed the duration
  483. else {
  484. times.add(constantValue);
  485. }
  486. }
  487. }
  488. else { // Requests
  489. double simulatedTime = 0.0;
  490. while(true) {
  491. if(simulatedTime+constantValue > durationInSeconds)
  492. break; // Stop simulation if it would exceed the duration
  493. else {
  494. // Add new request times to the variables
  495. simulatedTime += constantValue;
  496. times.add(simulatedTime);
  497. }
  498. }
  499. }
  500. return times;
  501. }
  502. /**
  503. * Simulates the times for normal distributions.
  504. * @param requestTimes The list of request times or null if the request times are to be sampled.
  505. * @param mean The mean value of the distribution.
  506. * @param standardDeviation The standard deviation value of the distribution.
  507. * @return The times for constant value distributions.
  508. */
  509. private LinkedList<Double> simulateNormalDistributionTimes(LinkedList<Double> requestTimes, double mean, double standardDeviation){
  510. LinkedList<Double> times = new LinkedList<Double>();
  511. Random rng = new Random();
  512. if(requestTimes != null) { // Responses
  513. for(Double requestTime:requestTimes) {
  514. double nextValue;
  515. while(true) {
  516. nextValue = rng.nextGaussian()*standardDeviation+mean;
  517. if(nextValue >= 0.0)
  518. break;
  519. }
  520. if(requestTime+nextValue > durationInSeconds)
  521. break; // Stop simulation if it would exceed the duration
  522. else {
  523. times.add(nextValue);
  524. }
  525. }
  526. return times;
  527. }
  528. else { // Requests
  529. double simulatedTime = 0.0;
  530. while(true) {
  531. double nextValue;
  532. while(true) {
  533. nextValue = rng.nextGaussian()*standardDeviation+mean;
  534. if(nextValue >= 0.0)
  535. break;
  536. }
  537. if(simulatedTime+nextValue > durationInSeconds)
  538. break; // Stop simulation if it would exceed the duration
  539. else {
  540. // Add new request times to the variables
  541. simulatedTime += nextValue;
  542. times.add(simulatedTime);
  543. }
  544. }
  545. }
  546. return times;
  547. }
  548. /**
  549. * Sorts the times list according to the time
  550. */
  551. private void sortTimesList() {
  552. allTimes.sort(new Comparator<Pair<Double,String>>(){
  553. public int compare(Pair<Double, String> pair1, Pair<Double, String> pair2) {
  554. Double key1 = pair1.getLeft();
  555. Double key2 = pair2.getLeft();
  556. if(key1 > key2) return 1;
  557. else if(key1 < key2) return -1;
  558. return 0;
  559. }
  560. });
  561. }
  562. /**
  563. * Saves the CSV output.
  564. */
  565. private void saveCSVOutput() {
  566. String filePathName = outputDirectoryPath+"/IoTDGF_SWaT Simulation";
  567. String fileNameAddition = "_"+new SimpleDateFormat("yyyy-MM-dd HH.mm.ss").format(outputFileNameTime.getTime())+"_Network";
  568. // Write file
  569. FileWriter outputWriter;
  570. try {
  571. sortTimesList();
  572. double rowsPerFile = 500000;
  573. double rowsPerFileCurrent = rowsPerFile;
  574. double numberOfTimes = allTimes.size();
  575. int numberOfFiles = (int) Math.ceil(numberOfTimes/rowsPerFile);
  576. int rowsInLastFile = (int) (numberOfTimes-rowsPerFile*(numberOfFiles-1));
  577. int num = 0;
  578. int numberOfTimesTenth = (int) numberOfTimes/100;
  579. if(numberOfTimesTenth == 0) numberOfTimesTenth = 1;
  580. for(int j = 0; j < numberOfFiles; j++) {
  581. if(passByReferenceValue_KeepSimulationRunning[0] == false) break;
  582. if(j == numberOfFiles-1) // Last File
  583. rowsPerFileCurrent = rowsInLastFile;
  584. StringBuilder outputStringBuilder = new StringBuilder(300*((int) rowsPerFileCurrent));
  585. outputStringBuilder.append("num,date,time,orig,type,i/f_name,i/f_dir,src,dst,proto,appi_name,"+
  586. "proxy_src_ip,Modbus_Function_Code,Modbus_Function_Description"+
  587. ",Modbus_Transaction_ID,SCADA_Tag,Modbus_Value,service,s_port,Tag\n");
  588. for(int i = 0; i < rowsPerFileCurrent; i++) {
  589. if(passByReferenceValue_KeepSimulationRunning[0] == false) break;
  590. num++;
  591. String CSVRow = allTimes.pop().getRight();//allTimes.get((int)(j*rowsPerFile+i)).getRight();
  592. outputStringBuilder.append(num+","+CSVRow);
  593. if(i < rowsPerFileCurrent-1)
  594. outputStringBuilder.append("\n");
  595. if(num % numberOfTimesTenth == 0) {
  596. propertyChangeSupport.firePropertyChange("statusPercentageNetworkOutput", 0, (int)(num/numberOfTimesTenth));
  597. }
  598. }
  599. if(passByReferenceValue_KeepSimulationRunning[0] == true) {
  600. String outputFileName = filePathName+fileNameAddition;
  601. if(numberOfFiles > 1)
  602. outputFileName += "_"+String.valueOf(j+1)+" of "+numberOfFiles;
  603. // Try file name combinations until one does not yet exist
  604. File outputFile;
  605. int fileNameAdditionExtra = 0;
  606. while(true) {
  607. String fileNameCandidate = outputFileName;
  608. if(fileNameAdditionExtra > 0) fileNameCandidate += "_"+fileNameAdditionExtra;
  609. outputFile = new File(fileNameCandidate+".csv");
  610. if(outputFile.exists())
  611. fileNameAdditionExtra++;
  612. else
  613. break;
  614. }
  615. if(fileNameAdditionExtra > 0) outputFileName += "_"+fileNameAdditionExtra;
  616. outputFileName += ".csv";
  617. outputWriter = new FileWriter(outputFileName);
  618. outputWriter.write(outputStringBuilder.toString());
  619. outputWriter.close();
  620. }
  621. }
  622. } catch (IOException e) {
  623. e.printStackTrace();
  624. System.out.println("Could not write network simulation output to file");
  625. }
  626. }
  627. /**
  628. * Returns the SCADA Tag for a given destination PLC in the style of dataset A1.
  629. * @param PLCNumber 1-6
  630. * @return The SCADA Tag for a given destination PLC in the style of dataset A1.
  631. */
  632. private String getSCADATagDatasetA1ForDestinationPLC(int PLCNumber){
  633. String SCADATag = "";
  634. switch(PLCNumber) {
  635. case 1:
  636. SCADATag = "HMI_LIT101";
  637. break;
  638. case 2:
  639. String[] tags = {"HMI_FIT201", "HMI_AIT202"};
  640. int randomNumber = ThreadLocalRandom.current().nextInt(0, 2);
  641. SCADATag = tags[randomNumber];
  642. break;
  643. case 3:
  644. SCADATag = "HMI_LIT301";
  645. break;
  646. case 4:
  647. SCADATag = "HMI_LIT401";
  648. break;
  649. case 5:
  650. SCADATag = "HMI_AIT501";
  651. break;
  652. case 6:
  653. SCADATag = "HMI_FIT601";
  654. break;
  655. }
  656. return SCADATag;
  657. }
  658. /**
  659. * Generates a valid transaction ID that is currently not in use.</br>
  660. * Synchronized to enable parallelization.
  661. * @param startDate The start date of the read tag service.
  662. * @param endDate The end date of the read tag service.
  663. * @return The transaction ID.
  664. */
  665. private synchronized int getValidTransactionID(Date startDate, Date endDate) {
  666. int randomID = 0;
  667. Pair<Date, Date> currentDatePair = new Pair<Date, Date>(startDate, endDate);
  668. while(true) {
  669. randomID = ThreadLocalRandom.current().nextInt(0, 65536);
  670. if(transactionIDsCurrentlyInUseUntil.containsKey(randomID)) {
  671. boolean iDIsGood = false;
  672. LinkedList<Pair<Date, Date>> transactionIDDates = transactionIDsCurrentlyInUseUntil.get(randomID);
  673. int numberOfDates = transactionIDDates.size();
  674. Date pairBeforeEndDate;
  675. Date pairAfterStartDate;
  676. if(numberOfDates == 1) { // Add before or after the only other pair
  677. Pair<Date, Date> firstDatePair = transactionIDDates.getFirst();
  678. if(firstDatePair.getLeft().after(endDate))
  679. transactionIDDates.add(0, currentDatePair);
  680. else
  681. transactionIDDates.add(currentDatePair);
  682. iDIsGood = true;
  683. }
  684. else
  685. for(int i = 1; i <= numberOfDates; i++) {
  686. if(i == 1 && transactionIDDates.get(0).getLeft().after(endDate)) { // Add before all existing pairs
  687. transactionIDDates.add(0, currentDatePair);
  688. iDIsGood = true;
  689. break;
  690. }
  691. else if(i == numberOfDates) { // Add after all existing pairs
  692. transactionIDDates.add(currentDatePair);
  693. iDIsGood = true;
  694. break;
  695. }
  696. else { // Add between pairs
  697. pairBeforeEndDate = transactionIDDates.get(i-1).getRight();
  698. pairAfterStartDate = transactionIDDates.get(i).getLeft();
  699. if(pairBeforeEndDate.before(startDate) && pairAfterStartDate.after(endDate)) {
  700. // Add new time window
  701. transactionIDDates.add(i, currentDatePair);
  702. iDIsGood = true;
  703. break;
  704. }
  705. }
  706. }
  707. if(iDIsGood) {
  708. transactionIDsCurrentlyInUseUntil.put(randomID, transactionIDDates);
  709. break;
  710. }
  711. }
  712. else {
  713. // ID was never in use until now
  714. LinkedList<Pair<Date, Date>> transactionIDDates = new LinkedList<Pair<Date, Date>>();
  715. transactionIDDates.add(currentDatePair);
  716. transactionIDsCurrentlyInUseUntil.put(randomID, transactionIDDates);
  717. break;
  718. }
  719. }
  720. return randomID;
  721. }
  722. /**
  723. * Sets the distribution handlers.
  724. */
  725. private void setDatasetDistributionHandlers() {
  726. // Some lines are commented out because they are for very rare special cases
  727. distributionHandlers.put("DatasetA1Distribution_Request_EngineeringWorkstation-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_EngineeringWorkstation-Historian_corrected.txt"));
  728. // distributionHandlers.put("DatasetA1Distribution_Request_EngineeringWorkstation-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_EngineeringWorkstation-PLC5.txt"));
  729. distributionHandlers.put("DatasetA1Distribution_Request_EngineeringWorkstation-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_EngineeringWorkstation-SCADA_corrected.txt"));
  730. distributionHandlers.put("DatasetA1Distribution_Request_Historian-EngineeringWorkstation", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_Historian-EngineeringWorkstation_corrected.txt"));
  731. distributionHandlers.put("DatasetA1Distribution_Request_Historian-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_Historian-PLC1_corrected.txt"));
  732. distributionHandlers.put("DatasetA1Distribution_Request_Historian-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_Historian-PLC2.txt"));
  733. // distributionHandlers.put("DatasetA1Distribution_Request_Historian-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_Historian-PLC3.txt"));
  734. distributionHandlers.put("DatasetA1Distribution_Request_PLC1-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_PLC1-PLC2.txt"));
  735. distributionHandlers.put("DatasetA1Distribution_Request_PLC2-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_PLC2-PLC3.txt"));
  736. distributionHandlers.put("DatasetA1Distribution_Request_PLC3-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_PLC3-PLC4.txt"));
  737. distributionHandlers.put("DatasetA1Distribution_Request_PLC6-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_PLC6-PLC1.txt"));
  738. distributionHandlers.put("DatasetA1Distribution_Request_PLC6-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_PLC6-PLC2.txt"));
  739. // distributionHandlers.put("DatasetA1Distribution_Request_SCADA-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_SCADA-PLC1.txt"));
  740. // distributionHandlers.put("DatasetA1Distribution_Request_SCADA-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_SCADA-PLC2.txt"));
  741. // distributionHandlers.put("DatasetA1Distribution_Request_SCADA-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_SCADA-PLC3.txt"));
  742. // distributionHandlers.put("DatasetA1Distribution_Request_SCADA-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_SCADA-PLC4.txt"));
  743. // distributionHandlers.put("DatasetA1Distribution_Request_SCADA-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_SCADA-PLC5.txt"));
  744. // distributionHandlers.put("DatasetA1Distribution_Request_SCADA-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Request_SCADA-PLC6.txt"));
  745. distributionHandlers.put("DatasetA1Distribution_Response_EngineeringWorkstation-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_EngineeringWorkstation-Historian.txt"));
  746. distributionHandlers.put("DatasetA1Distribution_Response_Historian-EngineeringWorkstation", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_Historian-EngineeringWorkstation.txt"));
  747. distributionHandlers.put("DatasetA1Distribution_Response_PLC1-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC1-PLC6.txt"));
  748. distributionHandlers.put("DatasetA1Distribution_Response_PLC1-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC1-SCADA.txt"));
  749. distributionHandlers.put("DatasetA1Distribution_Response_PLC2-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC2-PLC1.txt"));
  750. distributionHandlers.put("DatasetA1Distribution_Response_PLC2-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC2-PLC6.txt"));
  751. distributionHandlers.put("DatasetA1Distribution_Response_PLC2-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC2-SCADA.txt"));
  752. distributionHandlers.put("DatasetA1Distribution_Response_PLC3-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC3-Historian.txt"));
  753. distributionHandlers.put("DatasetA1Distribution_Response_PLC3-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC3-PLC2.txt"));
  754. distributionHandlers.put("DatasetA1Distribution_Response_PLC3-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC3-SCADA.txt"));
  755. distributionHandlers.put("DatasetA1Distribution_Response_PLC4-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC4-PLC3.txt"));
  756. distributionHandlers.put("DatasetA1Distribution_Response_PLC4-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC4-SCADA.txt"));
  757. distributionHandlers.put("DatasetA1Distribution_Response_PLC5-EngineeringWorkstation", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC5-EngineeringWorkstation.txt"));
  758. distributionHandlers.put("DatasetA1Distribution_Response_PLC5-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC5-SCADA.txt"));
  759. distributionHandlers.put("DatasetA1Distribution_Response_PLC6-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_PLC6-SCADA.txt"));
  760. distributionHandlers.put("DatasetA1Distribution_Response_SCADA-EngineeringWorkstation", SWaTUtilities.readDatasetDistributionData("DatasetA1Distribution_Response_SCADA-EngineeringWorkstation.txt"));
  761. distributionHandlers.put("DatasetA6Distribution_Request_Historian-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_Historian-PLC1.txt"));
  762. distributionHandlers.put("DatasetA6Distribution_Request_Historian-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_Historian-PLC2.txt"));
  763. distributionHandlers.put("DatasetA6Distribution_Request_Historian-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_Historian-PLC3.txt"));
  764. distributionHandlers.put("DatasetA6Distribution_Request_Historian-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_Historian-PLC4.txt"));
  765. distributionHandlers.put("DatasetA6Distribution_Request_Historian-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_Historian-PLC5.txt"));
  766. distributionHandlers.put("DatasetA6Distribution_Request_Historian-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_Historian-PLC6.txt"));
  767. distributionHandlers.put("DatasetA6Distribution_Request_PLC1-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC1-PLC2.txt"));
  768. distributionHandlers.put("DatasetA6Distribution_Request_PLC1-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC1-PLC3.txt"));
  769. distributionHandlers.put("DatasetA6Distribution_Request_PLC1-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC1-PLC4.txt"));
  770. distributionHandlers.put("DatasetA6Distribution_Request_PLC1-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC1-PLC5.txt"));
  771. distributionHandlers.put("DatasetA6Distribution_Request_PLC1-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC1-PLC6.txt"));
  772. distributionHandlers.put("DatasetA6Distribution_Request_PLC2-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC2-PLC3.txt"));
  773. distributionHandlers.put("DatasetA6Distribution_Request_PLC2-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC2-PLC4.txt"));
  774. distributionHandlers.put("DatasetA6Distribution_Request_PLC2-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC2-PLC5.txt"));
  775. distributionHandlers.put("DatasetA6Distribution_Request_PLC3-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC3-PLC2.txt"));
  776. distributionHandlers.put("DatasetA6Distribution_Request_PLC3-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC3-PLC4.txt"));
  777. distributionHandlers.put("DatasetA6Distribution_Request_PLC3-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC3-PLC6.txt"));
  778. distributionHandlers.put("DatasetA6Distribution_Request_PLC4-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC4-PLC3.txt"));
  779. distributionHandlers.put("DatasetA6Distribution_Request_PLC4-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC4-PLC5.txt"));
  780. distributionHandlers.put("DatasetA6Distribution_Request_PLC5-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC5-PLC4.txt"));
  781. distributionHandlers.put("DatasetA6Distribution_Request_PLC6-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC6-PLC1.txt"));
  782. distributionHandlers.put("DatasetA6Distribution_Request_PLC6-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_PLC6-PLC2.txt"));
  783. distributionHandlers.put("DatasetA6Distribution_Request_SCADA-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_SCADA-PLC1.txt"));
  784. distributionHandlers.put("DatasetA6Distribution_Request_SCADA-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_SCADA-PLC2.txt"));
  785. distributionHandlers.put("DatasetA6Distribution_Request_SCADA-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_SCADA-PLC3.txt"));
  786. distributionHandlers.put("DatasetA6Distribution_Request_SCADA-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_SCADA-PLC4.txt"));
  787. distributionHandlers.put("DatasetA6Distribution_Request_SCADA-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_SCADA-PLC5.txt"));
  788. distributionHandlers.put("DatasetA6Distribution_Request_SCADA-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Request_SCADA-PLC6.txt"));
  789. distributionHandlers.put("DatasetA6Distribution_Response_PLC1-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC1-Historian.txt"));
  790. distributionHandlers.put("DatasetA6Distribution_Response_PLC1-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC1-PLC6.txt"));
  791. distributionHandlers.put("DatasetA6Distribution_Response_PLC1-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC1-SCADA.txt"));
  792. distributionHandlers.put("DatasetA6Distribution_Response_PLC2-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC2-Historian.txt"));
  793. distributionHandlers.put("DatasetA6Distribution_Response_PLC2-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC2-PLC1.txt"));
  794. distributionHandlers.put("DatasetA6Distribution_Response_PLC2-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC2-PLC3.txt"));
  795. distributionHandlers.put("DatasetA6Distribution_Response_PLC2-PLC6", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC2-PLC6.txt"));
  796. distributionHandlers.put("DatasetA6Distribution_Response_PLC2-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC2-SCADA.txt"));
  797. distributionHandlers.put("DatasetA6Distribution_Response_PLC3-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC3-Historian.txt"));
  798. distributionHandlers.put("DatasetA6Distribution_Response_PLC3-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC3-PLC1.txt"));
  799. distributionHandlers.put("DatasetA6Distribution_Response_PLC3-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC3-PLC2.txt"));
  800. distributionHandlers.put("DatasetA6Distribution_Response_PLC3-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC3-PLC4.txt"));
  801. distributionHandlers.put("DatasetA6Distribution_Response_PLC3-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC3-SCADA.txt"));
  802. distributionHandlers.put("DatasetA6Distribution_Response_PLC4-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC4-Historian.txt"));
  803. distributionHandlers.put("DatasetA6Distribution_Response_PLC4-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC4-PLC1.txt"));
  804. distributionHandlers.put("DatasetA6Distribution_Response_PLC4-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC4-PLC2.txt"));
  805. distributionHandlers.put("DatasetA6Distribution_Response_PLC4-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC4-PLC3.txt"));
  806. distributionHandlers.put("DatasetA6Distribution_Response_PLC4-PLC5", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC4-PLC5.txt"));
  807. distributionHandlers.put("DatasetA6Distribution_Response_PLC4-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC4-SCADA.txt"));
  808. distributionHandlers.put("DatasetA6Distribution_Response_PLC5-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC5-Historian.txt"));
  809. distributionHandlers.put("DatasetA6Distribution_Response_PLC5-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC5-PLC1.txt"));
  810. distributionHandlers.put("DatasetA6Distribution_Response_PLC5-PLC2", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC5-PLC2.txt"));
  811. distributionHandlers.put("DatasetA6Distribution_Response_PLC5-PLC4", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC5-PLC4.txt"));
  812. distributionHandlers.put("DatasetA6Distribution_Response_PLC5-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC5-SCADA.txt"));
  813. distributionHandlers.put("DatasetA6Distribution_Response_PLC6-Historian", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC6-Historian.txt"));
  814. distributionHandlers.put("DatasetA6Distribution_Response_PLC6-PLC1", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC6-PLC1.txt"));
  815. distributionHandlers.put("DatasetA6Distribution_Response_PLC6-PLC3", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC6-PLC3.txt"));
  816. distributionHandlers.put("DatasetA6Distribution_Response_PLC6-SCADA", SWaTUtilities.readDatasetDistributionData("DatasetA6Distribution_Response_PLC6-SCADA.txt"));
  817. }
  818. /**
  819. * Append elements to allTimes in a thread-safe way.
  820. * @param pairToAdd The pair of times to add to allTimes.
  821. */
  822. private synchronized void appendToAllTimesSynchronized(Pair<Double, String> pairToAdd) {
  823. allTimes.add(pairToAdd);
  824. if(optimizeOutput)
  825. if(allTimes.size() == rowsPerOutputFile*5) // The multiplicator can be changed for further optimization wrt. the available RAM
  826. saveCSVOutputOneFile();
  827. }
  828. /**
  829. * Saves one file worth of CSV output.
  830. */
  831. private void saveCSVOutputOneFile() {
  832. String filePathName = outputDirectoryPath+"/IoTDGF_SWaT Simulation";
  833. String fileNameAddition = "_"+new SimpleDateFormat("yyyy-MM-dd HH.mm.ss").format(outputFileNameTime.getTime())+"_Network";
  834. // Write file
  835. FileWriter outputWriter;
  836. try {
  837. outputFileCounter++;
  838. sortTimesList();
  839. Calendar currentTime = (Calendar) startTime.clone();
  840. double currentRunTimeDouble = allTimes.getLast().getLeft();
  841. int currentRunTime = (int) currentRunTimeDouble;
  842. currentTime.set(Calendar.SECOND, currentTime.get(Calendar.SECOND)+currentRunTime-60);
  843. removeOldTransactionIDs(currentTime.getTime());
  844. int rowsPerFileCurrent = rowsPerOutputFile;
  845. int numberOfTimesTenth = rowsPerOutputFile/100;
  846. int num = 0;
  847. if(numberOfTimesTenth == 0) numberOfTimesTenth = 1;
  848. if(passByReferenceValue_KeepSimulationRunning[0] == false) return;
  849. StringBuilder outputStringBuilder = new StringBuilder(300*rowsPerOutputFile);
  850. outputStringBuilder.append("num,date,time,orig,type,i/f_name,i/f_dir,src,dst,proto,appi_name,"+
  851. "proxy_src_ip,Modbus_Function_Code,Modbus_Function_Description"+
  852. ",Modbus_Transaction_ID,SCADA_Tag,Modbus_Value,service,s_port,Tag\n");
  853. for(int i = 0; i < rowsPerFileCurrent; i++) {
  854. if(passByReferenceValue_KeepSimulationRunning[0] == false) return;
  855. num++;
  856. outputFileRowCounter++;
  857. String CSVRow = allTimes.pop().getRight();
  858. outputStringBuilder.append(outputFileRowCounter+","+CSVRow);
  859. if(i < rowsPerFileCurrent-1)
  860. outputStringBuilder.append("\n");
  861. if(num % numberOfTimesTenth == 0) {
  862. propertyChangeSupport.firePropertyChange("statusPercentageNetworkOutput", 0, (int)(num/numberOfTimesTenth));
  863. }
  864. }
  865. if(passByReferenceValue_KeepSimulationRunning[0] == true) {
  866. String outputFileName = filePathName+fileNameAddition;
  867. outputFileName += "_"+outputFileCounter;
  868. // Try file name combinations until one does not yet exist
  869. File outputFile;
  870. int fileNameAdditionExtra = 0;
  871. while(true) {
  872. String fileNameCandidate = outputFileName;
  873. if(fileNameAdditionExtra > 0) fileNameCandidate += "_"+fileNameAdditionExtra;
  874. outputFile = new File(fileNameCandidate+".csv");
  875. if(outputFile.exists())
  876. fileNameAdditionExtra++;
  877. else
  878. break;
  879. }
  880. if(fileNameAdditionExtra > 0) outputFileName += "_"+fileNameAdditionExtra;
  881. outputFileName += ".csv";
  882. outputWriter = new FileWriter(outputFileName);
  883. outputWriter.write(outputStringBuilder.toString());
  884. outputWriter.close();
  885. }
  886. } catch (IOException e) {
  887. e.printStackTrace();
  888. System.out.println("Could not write network simulation output to file");
  889. }
  890. }
  891. /**
  892. * Saves the rest of the CSV output.
  893. */
  894. private void saveCSVOutputRest() {
  895. String filePathName = outputDirectoryPath+"/IoTDGF_SWaT Simulation";
  896. String fileNameAddition = "_"+new SimpleDateFormat("yyyy-MM-dd HH.mm.ss").format(outputFileNameTime.getTime())+"_Network";
  897. // Write file
  898. FileWriter outputWriter;
  899. try {
  900. sortTimesList();
  901. double rowsPerFileCurrent = rowsPerOutputFile;
  902. double numberOfTimes = allTimes.size();
  903. int numberOfFiles = (int) Math.ceil(numberOfTimes/rowsPerOutputFile);
  904. int rowsInLastFile = (int) (numberOfTimes-rowsPerOutputFile*(numberOfFiles-1));
  905. int num = 0;
  906. int numberOfTimesTenth = (int) numberOfTimes/100;
  907. if(numberOfTimesTenth == 0) numberOfTimesTenth = 1;
  908. for(int j = 0; j < numberOfFiles; j++) {
  909. if(passByReferenceValue_KeepSimulationRunning[0] == false) return;
  910. outputFileCounter++;
  911. if(j == numberOfFiles-1) // Last File
  912. rowsPerFileCurrent = rowsInLastFile;
  913. StringBuilder outputStringBuilder = new StringBuilder(300*rowsPerOutputFile);
  914. outputStringBuilder.append("num,date,time,orig,type,i/f_name,i/f_dir,src,dst,proto,appi_name,"+
  915. "proxy_src_ip,Modbus_Function_Code,Modbus_Function_Description"+
  916. ",Modbus_Transaction_ID,SCADA_Tag,Modbus_Value,service,s_port,Tag\n");
  917. for(int i = 0; i < rowsPerFileCurrent; i++) {
  918. if(passByReferenceValue_KeepSimulationRunning[0] == false) return;
  919. num++;
  920. outputFileRowCounter++;
  921. String CSVRow = allTimes.pop().getRight();//allTimes.get((int)(j*rowsPerFile+i)).getRight();
  922. outputStringBuilder.append(outputFileRowCounter+","+CSVRow);
  923. if(i < rowsPerFileCurrent-1)
  924. outputStringBuilder.append("\n");
  925. if(num % numberOfTimesTenth == 0) {
  926. propertyChangeSupport.firePropertyChange("statusPercentageNetworkOutput", 0, (int)(num/numberOfTimesTenth));
  927. }
  928. }
  929. if(passByReferenceValue_KeepSimulationRunning[0] == true) {
  930. String outputFileName = filePathName+fileNameAddition;
  931. outputFileName += "_"+outputFileCounter;
  932. // Try file name combinations until one does not yet exist
  933. File outputFile;
  934. int fileNameAdditionExtra = 0;
  935. while(true) {
  936. String fileNameCandidate = outputFileName;
  937. if(fileNameAdditionExtra > 0) fileNameCandidate += "_"+fileNameAdditionExtra;
  938. outputFile = new File(fileNameCandidate+".csv");
  939. if(outputFile.exists())
  940. fileNameAdditionExtra++;
  941. else
  942. break;
  943. }
  944. if(fileNameAdditionExtra > 0) outputFileName += "_"+fileNameAdditionExtra;
  945. outputFileName += ".csv";
  946. outputWriter = new FileWriter(outputFileName);
  947. outputWriter.write(outputStringBuilder.toString());
  948. outputWriter.close();
  949. }
  950. }
  951. } catch (IOException e) {
  952. e.printStackTrace();
  953. System.out.println("Could not write network simulation output to file");
  954. }
  955. }
  956. /**
  957. * Removes the transaction IDs with a end time in the before the date variable.
  958. * @param currentDateTime The data variable.
  959. */
  960. private void removeOldTransactionIDs(Date currentDateTime) {
  961. for(int i = 0; i < 65536; i++) {
  962. if(transactionIDsCurrentlyInUseUntil.containsKey(i)) {
  963. LinkedList<Pair<Date, Date>> datePairList = transactionIDsCurrentlyInUseUntil.get(i);
  964. while(true) {
  965. if(datePairList.size() == 0) break;
  966. Pair<Date, Date> datePair = datePairList.getFirst();
  967. if(datePair.getRight().before(currentDateTime))
  968. datePairList.removeFirst();
  969. else
  970. break;
  971. }
  972. }
  973. }
  974. }
  975. }