zhao006 vor 4 Monaten
Ursprung
Commit
28830f8b8f
100 geänderte Dateien mit 46343 neuen und 11 gelöschten Zeilen
  1. 314 0
      app/CJT188/CJT188.c
  2. 46 0
      app/CJT188/CJT188.h
  3. 10 0
      app/HARDWARE/include/iwdg.h
  4. 1 1
      app/HARDWARE/include/malloc.h
  5. 11 0
      app/HARDWARE/include/reset.h
  6. 24 0
      app/HARDWARE/source/iwdg.c
  7. 9 2
      app/HARDWARE/source/malloc.c
  8. 43 0
      app/HARDWARE/source/reset.c
  9. 11 8
      app/HARDWARE/source/usart.c
  10. 918 0
      app/IEC_SERVER/IEC10X/IEC101.c
  11. 217 0
      app/IEC_SERVER/IEC10X/IEC101.h
  12. 0 0
      app/IEC_SERVER/IEC10X/test.c
  13. 129 0
      app/IEC_SERVER/lib60870-C/CMakeLists.txt
  14. 2441 0
      app/IEC_SERVER/lib60870-C/Doxyfile
  15. 155 0
      app/IEC_SERVER/lib60870-C/Makefile
  16. 83 0
      app/IEC_SERVER/lib60870-C/config/lib60870_config.h
  17. 8 0
      app/IEC_SERVER/lib60870-C/dependencies/README
  18. BIN
      app/IEC_SERVER/lib60870-C/doxydoc/mz-automation.ico
  19. 7 0
      app/IEC_SERVER/lib60870-C/make/common_targets.mk
  20. 3 0
      app/IEC_SERVER/lib60870-C/make/stack_includes.mk
  21. 185 0
      app/IEC_SERVER/lib60870-C/make/target_system.mk
  22. 173 0
      app/IEC_SERVER/lib60870-C/src/CMakeLists.txt
  23. 175 0
      app/IEC_SERVER/lib60870-C/src/common/inc/linked_list.h
  24. 186 0
      app/IEC_SERVER/lib60870-C/src/common/linked_list.c
  25. 143 0
      app/IEC_SERVER/lib60870-C/src/file-service/cs101_file_service.h
  26. 907 0
      app/IEC_SERVER/lib60870-C/src/file-service/file_server.c
  27. 201 0
      app/IEC_SERVER/lib60870-C/src/hal/CMakeLists.txt
  28. 51 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/hal_base.h
  29. 140 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/hal_serial.h
  30. 358 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/hal_socket.h
  31. 106 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/hal_thread.h
  32. 80 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/hal_time.h
  33. 53 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/lib_memory.h
  34. 37 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/platform_endian.h
  35. 322 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/tls_config.h
  36. 122 0
      app/IEC_SERVER/lib60870-C/src/hal/inc/tls_socket.h
  37. 70 0
      app/IEC_SERVER/lib60870-C/src/hal/memory/lib_memory.c
  38. 57 0
      app/IEC_SERVER/lib60870-C/src/hal/serial/linux/serial_port_linux.c
  39. 273 0
      app/IEC_SERVER/lib60870-C/src/hal/serial/win32/serial_port_win32.c
  40. 647 0
      app/IEC_SERVER/lib60870-C/src/hal/socket/bsd/socket_bsd.c
  41. 862 0
      app/IEC_SERVER/lib60870-C/src/hal/socket/linux/socket_linux.c
  42. 796 0
      app/IEC_SERVER/lib60870-C/src/hal/socket/win32/socket_win32.c
  43. 109 0
      app/IEC_SERVER/lib60870-C/src/hal/thread/bsd/thread_bsd.c
  44. 108 0
      app/IEC_SERVER/lib60870-C/src/hal/thread/linux/thread_linux.c
  45. 149 0
      app/IEC_SERVER/lib60870-C/src/hal/thread/macos/thread_macos.c
  46. 114 0
      app/IEC_SERVER/lib60870-C/src/hal/thread/win32/thread_win32.c
  47. 67 0
      app/IEC_SERVER/lib60870-C/src/hal/time/unix/time.c
  48. 61 0
      app/IEC_SERVER/lib60870-C/src/hal/time/win32/time.c
  49. 65 0
      app/IEC_SERVER/lib60870-C/src/hal/tls/mbedtls/mbedtls_config.h
  50. 1060 0
      app/IEC_SERVER/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c
  51. 691 0
      app/IEC_SERVER/lib60870-C/src/iec60870/apl/cpXXtime2a.c
  52. 1545 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_asdu.c
  53. 163 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_bcr.c
  54. 7707 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_information_objects.c
  55. 531 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_master.c
  56. 70 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_master_connection.c
  57. 203 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_queue.c
  58. 874 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_slave.c
  59. 1365 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs104/cs104_connection.c
  60. 210 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs104/cs104_frame.c
  61. 4485 0
      app/IEC_SERVER/lib60870-C/src/iec60870/cs104/cs104_slave.c
  62. 71 0
      app/IEC_SERVER/lib60870-C/src/iec60870/frame.c
  63. 69 0
      app/IEC_SERVER/lib60870-C/src/iec60870/lib60870_common.c
  64. 124 0
      app/IEC_SERVER/lib60870-C/src/iec60870/link_layer/buffer_frame.c
  65. 2162 0
      app/IEC_SERVER/lib60870-C/src/iec60870/link_layer/link_layer.c
  66. 196 0
      app/IEC_SERVER/lib60870-C/src/iec60870/link_layer/serial_transceiver_ft_1_2.c
  67. 2109 0
      app/IEC_SERVER/lib60870-C/src/inc/api/cs101_information_objects.h
  68. 342 0
      app/IEC_SERVER/lib60870-C/src/inc/api/cs101_master.h
  69. 351 0
      app/IEC_SERVER/lib60870-C/src/inc/api/cs101_slave.h
  70. 372 0
      app/IEC_SERVER/lib60870-C/src/inc/api/cs104_connection.h
  71. 395 0
      app/IEC_SERVER/lib60870-C/src/inc/api/cs104_slave.h
  72. 888 0
      app/IEC_SERVER/lib60870-C/src/inc/api/iec60870_common.h
  73. 59 0
      app/IEC_SERVER/lib60870-C/src/inc/api/iec60870_master.h
  74. 268 0
      app/IEC_SERVER/lib60870-C/src/inc/api/iec60870_slave.h
  75. 54 0
      app/IEC_SERVER/lib60870-C/src/inc/api/link_layer_parameters.h
  76. 65 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/apl_types_internal.h
  77. 70 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/buffer_frame.h
  78. 59 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/cs101_asdu_internal.h
  79. 106 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/cs101_queue.h
  80. 59 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/cs104_frame.h
  81. 41 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/frame.h
  82. 1219 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/information_objects_internal.h
  83. 41 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/lib60870_internal.h
  84. 182 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/link_layer.h
  85. 46 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/link_layer_private.h
  86. 57 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/platform_endian.h
  87. 59 0
      app/IEC_SERVER/lib60870-C/src/inc/internal/serial_transceiver_ft_1_2.h
  88. 13 0
      app/IEC_SERVER/lib60870-C/src/lib60870.pc.in
  89. 20 0
      app/IEC_SERVER/lib60870-C/src/version.rc.in
  90. 48 0
      app/IEC_SERVER/lib60870-C/tests/CMakeLists.txt
  91. 6644 0
      app/IEC_SERVER/lib60870-C/tests/all_tests.c
  92. 35 0
      app/IEC_SERVER/lib60870-C/tests/certs/README.md
  93. 27 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_1.key
  94. 20 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_1.pem
  95. 27 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_2.key
  96. 20 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_2.pem
  97. 27 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_3.key
  98. 20 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_3.pem
  99. 27 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_4.key
  100. 0 0
      app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_4.pem

+ 314 - 0
app/CJT188/CJT188.c

@@ -0,0 +1,314 @@
+#include "CJT188.h"
+#include "string.h"
+#include "timer.h"
+#include "gateway_message.h"
+#include "tcp_server.h"
+#include "data_task.h"
+
+CTJBus_t ctjData;
+
+void cjt_callback(UART_HandleTypeDef *husart)
+{ 
+  if(__HAL_UART_GET_FLAG(husart, UART_FLAG_RXNE) && __HAL_UART_GET_IT_SOURCE(husart, UART_IT_RXNE))
+	{
+			if (ctjData.rxIndex < _CJT_RXSIZE - 1)
+			{
+				ctjData.rxBuf[ctjData.rxIndex] = husart->Instance->DR;
+				ctjData.rxIndex++;
+			}
+			else
+			{
+				uint8_t data = husart->Instance->DR;
+			}
+	}
+	if ((ctjData.rxIndex > 0) && RESET != __HAL_UART_GET_FLAG(husart, USART_FLAG_IDLE) && __HAL_UART_GET_IT_SOURCE(husart, UART_IT_IDLE))
+	{	
+		uint8_t i;	
+		ctjData.done = 1;	
+		i = husart->Instance->SR;
+		i = husart->Instance->DR;		
+		return;
+	}
+  ctjData.rxTime = gettick();
+}
+// ##################################################################################################
+bool cjt_sendRaw(uint8_t *data, uint16_t size, uint32_t timeout)
+{
+	GATEWAY_PARAMS *get;
+	get= get_gateway_config_params();	
+	while(ctjData.txBusy == 1)
+	delay_ms(1);
+	ctjData.txBusy = 1;
+	memset(ctjData.rxBuf, 0, _CJT_RXSIZE);
+	ctjData.rxIndex = 0;
+	ctjData.done = 0;
+	uint32_t startTime = gettick();
+	portENTER_CRITICAL();
+	if(get->comProtocol){ // get->protocol  1:232 0:485
+		USART_232_Send(data,size);
+	}else{
+		USART_485_Send(data,size);
+	}
+	
+	portEXIT_CRITICAL();
+	ctjData.done=0;
+	ctjData.txBusy = 0;
+
+	return true;
+}
+// ##################################################################################################
+uint16_t cjt_receiveRaw(uint32_t timeout)
+{
+  uint32_t startTime = gettick();
+  while (1)
+  {
+    if (gettick() - startTime> timeout * 1000)
+      return 0;
+    if (ctjData.done == 1)
+    {
+      return ctjData.rxIndex;
+    }
+    delay_ms(5);
+  }
+}
+
+bool cjt_init(uint32_t timeout)
+{
+  // HAL_GPIO_WritePin(_MMODBUS_CTRL_GPIO, _MMODBUS_CTRL_PIN, GPIO_PIN_RESET); 此处初始化在485 init过
+	HAL_GPIO_WritePin(_CTJ_CTRL_GPIO, _CTJ_CTRL_PIN,GPIO_PIN_RESET);
+  memset(&ctjData, 0, sizeof(ctjData));
+  ctjData.timeout = timeout;
+  return true;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: bool cjt_read_meter_data(uint8_t* slaveAddress, uint8_t *data, uint8_t protocol)
+*	功能说明: 读取仪表上的数据
+*	形   参1: slaveAddress:读取的地址  
+*	形   参2:	data:接收的内存地址  
+*	形   参3:	protocol:读取的仪表类型 0x10(水表) 0x30(燃气表) 0x40(电表)
+*	返 回 值: true: 成功 false:失败
+*********************************************************************************************************
+*/
+bool cjt_read_meter_data(uint8_t* slaveAddress, double* data, uint8_t protocol)
+{
+  uint8_t txData[19];
+	txData[0] = 0xFE;
+	txData[1] = 0xFE;
+	txData[2] = 0xFE;							// 引导符
+  txData[3] = 0x68;							// 起始帧
+	if(protocol == 0x10)					// 水表
+		txData[4] = 0x10;							
+	else if(protocol == 0x30)			// 燃气表
+		txData[4] = 0x30;
+	else if(protocol == 0x40)  		// 电表
+		txData[4] = 0x40;
+  txData[5] = slaveAddress[0];	// 地址	生产流水号最低字节
+  txData[6] = slaveAddress[1];	// 地址 生产流水号次高字节
+  txData[7] = slaveAddress[2];	// 地址 生产流水号最高字节
+	txData[8] = slaveAddress[3];	// 地址 表计生产月份
+  txData[9] = slaveAddress[4];	// 地址 表计生产年份
+  txData[10] = slaveAddress[5];	// 地址	生产厂商代码低字节
+	txData[11] = slaveAddress[6];	// 地址 生产厂商代码高字节
+	txData[12] = 0x01;							// 控制码 CTR_0
+	txData[13] = 0x03;						// 数据域长度
+	txData[14] = 0x90;						// 数据标识DI0
+	txData[15] = 0x1F;						// 数据标识DI1
+	txData[16] = 0x01;						// 序列号
+	uint8_t crc = 0;
+	for(uint8_t i = 0; i < 14; i++)
+	{
+		crc += txData[i + 3];	
+	}
+	txData[17] = crc;
+	txData[18] = 0x16;						// 结束帧
+	
+  cjt_sendRaw(txData, 19, 100);
+  uint16_t recLen = cjt_receiveRaw(ctjData.timeout);
+  if (recLen == 0)
+    return false;
+	for(uint8_t i = 0; i < 12; i++)
+	{
+		if(ctjData.rxBuf[i] != txData[i])
+			return false;
+	}
+  
+	if (ctjData.rxBuf[12] != txData[12] + 0x80)
+    return false;
+	if (ctjData.rxBuf[14] != txData[14])
+    return false;
+	if (ctjData.rxBuf[15] != txData[15])
+    return false;
+	crc = 0;
+	for(uint8_t i = 0; i < 20; i++)
+	{
+		crc += ctjData.rxBuf[i + 3];	
+	}
+	if (ctjData.rxBuf[23] != crc)
+    return false;
+	if (ctjData.rxBuf[24] != txData[18])
+    return false;
+
+	if(TransparentModeFlag)	return true;
+
+	*data = data_translate_to_float(ctjData.rxBuf);
+
+  return true;
+}
+
+
+/*
+*********************************************************************************************************
+*	函 数 名: bool cjt_set_addr(uint8_t *addr)
+*	功能说明: 设置仪表地址
+*	形    参: addr:需要设置的新地址
+*	返 回 值: true: 成功 false:失败
+*********************************************************************************************************
+*/
+bool cjt_set_addr(uint8_t *addr)
+{ 
+	uint8_t txData[26];
+	txData[0] = 0xFE;
+	txData[1] = 0xFE;
+	txData[2] = 0xFE;							// 引导符
+  txData[3] = 0x68;							// 起始帧
+  txData[4] = 0xAA;							// 水表、燃气表、电表
+  txData[5] = 0xAA;							
+  txData[6] = 0xAA;							
+  txData[7] = 0xAA;							
+	txData[8] = 0xAA;							
+  txData[9] = 0xAA;							
+  txData[10] = 0xAA;							
+	txData[11] = 0xAA;							
+	txData[12] = 0x15;							// 控制码 CTR_3
+	txData[13] = 0x0A;						// 数据域长度
+	txData[14] = 0x18;						// 数据标识DI0
+	txData[15] = 0xA0;						// 数据标识DI1
+	txData[16] = 0x00;						// 序列号
+	txData[17] = addr[6];					// 新地址
+	txData[18] = addr[5];					// 新地址
+	txData[19] = addr[4];					// 新地址
+	txData[20] = addr[3];					// 新地址
+	txData[21] = addr[2];					// 新地址
+	txData[22] = addr[1];					// 新地址
+	txData[23] = addr[0];					// 新地址
+	
+	uint8_t crc = 0;
+	for(uint8_t i = 0; i < 21; i++)
+	{
+		crc += txData[i + 3];	
+	}
+	txData[24] = crc;	
+	txData[25] = 0x16;	
+	
+	cjt_sendRaw(txData, 26, 100);
+  uint16_t recLen = cjt_receiveRaw(ctjData.timeout);
+	if (recLen == 0)
+    return false;
+  if (ctjData.rxBuf[3] != txData[3])
+    return false;
+	for(uint8_t i = 0; i < 7; i++)
+	{
+		if(ctjData.rxBuf[i + 5] != txData[23 - i])
+			return false;
+	}
+	if(ctjData.rxBuf[12] != txData[12] + 0x80)
+		return false;
+	if(ctjData.rxBuf[14] !=	txData[14] && ctjData.rxBuf[15] != txData[15] )
+		return false;
+	crc = 0;
+	for(uint8_t i = 0; i < 14; i++)
+	{
+		crc += ctjData.rxBuf[i  + 3];	
+	}
+	if(ctjData.rxBuf[17] != crc)
+		return false;
+	if(ctjData.rxBuf[18] != txData[25])
+		return false;
+	
+	return true;
+}
+
+/*
+*********************************************************************************************************
+*	函 数 名: bool cjt_read_addr(uint8_t *add)
+*	功能说明: 读取仪表地址
+*	形    参: data:读取地址的存放地址
+*	返 回 值: true: 成功 false:失败
+*********************************************************************************************************
+*/
+bool cjt_read_addr(uint8_t *add)
+{
+	uint8_t txData[19];
+	txData[0] = 0xFE;
+	txData[1] = 0xFE;
+	txData[2] = 0xFE;							// 引导符
+  txData[3] = 0x68;							// 起始帧
+  txData[4] = 0xAA;							// 水表、燃气表、电表
+  txData[5] = 0xAA;							
+  txData[6] = 0xAA;							
+  txData[7] = 0xAA;							
+	txData[8] = 0xAA;							
+  txData[9] = 0xAA;							
+  txData[10] = 0xAA;							
+	txData[11] = 0xAA;							
+	txData[12] = 0x03;							// 控制码
+	txData[13] = 0x03;						// 数据域长度
+	txData[14] = 0x81;						// 数据标识DI0
+	txData[15] = 0x0A;						// 数据标识DI1
+	txData[16] = 0x00;						// 序列号
+	uint8_t	crc = 0;
+	for(uint8_t i = 0; i < 14; i++)
+	{
+		crc += txData[i + 3];	
+	}
+	txData[17] = crc;						// crc
+	txData[18] = 0x16;					// 结束帧
+	 
+	cjt_sendRaw(txData, 19, 100);
+  uint16_t recLen = cjt_receiveRaw(ctjData.timeout);
+  if (recLen == 0)
+    return false;
+  
+	if (ctjData.rxBuf[3] != txData[3])
+    return false;
+	if (ctjData.rxBuf[12] != txData[12] + 0x80)
+    return false;
+	for(uint8_t i = 0; i < 4; i++)
+	{
+		if(ctjData.rxBuf[i + 13] != txData[i + 13])
+			return false;
+	}
+	crc = 0;
+	for(uint8_t i = 0; i < 14; i++)
+	{
+		crc += ctjData.rxBuf[i + 3];	
+	}
+	if(ctjData.rxBuf[17] != crc)
+		return false;
+	if(ctjData.rxBuf[18] != txData[18])
+		return false;
+	
+	if(add != NULL)
+	{
+		for(uint8_t i = 0; i < 7; i++)
+		{
+			add[6 - i] =	ctjData.rxBuf[i + 5];
+		}
+	}
+	return true;
+}
+
+// 将读取的数据转换成double型
+double data_translate_to_float(uint8_t* msg)
+{
+	int val;
+	
+	val += ((0xff & (msg[17]>>4))*10 +(0xf & msg[17]));
+	val += ((0xff & (msg[18]>>4))*10 +(0xf & msg[18])) * my_pow(10,2);
+	val += ((0xff & (msg[19]>>4))*10 +(0xf & msg[19])) * my_pow(10,4);
+	val += ((0xff & (msg[20]>>4))*10 +(0xf & msg[20])) * my_pow(10,6);
+
+	return (double)(val)/100.0;
+}

+ 46 - 0
app/CJT188/CJT188.h

@@ -0,0 +1,46 @@
+#ifndef __CJT188_H
+#define __CJT188_H
+
+#include "stm32f2xx.h"
+#include "stdint.h"
+#include "usart.h"
+#include "stdbool.h" 
+
+
+#define _CJT_RXSIZE 64
+#define _CTJ_CTRL_GPIO        USART_485_DE_GPIO_PORT
+#define _CTJ_CTRL_PIN         USART_485_DE_PIN
+
+typedef struct
+{
+  uint16_t              rxIndex;  
+  uint8_t               rxBuf[_CJT_RXSIZE];
+  uint32_t              rxTime;
+  uint8_t               txBusy;
+  uint32_t              timeout; 
+	uint8_t								done;  
+}CTJBus_t;
+
+
+#define gettick( )                           ( xTaskGetTickCount() )
+
+// 初始化
+bool cjt_init(uint32_t timeout);
+// 回调函数
+void cjt_callback(UART_HandleTypeDef *husart);
+// 接收函数
+uint16_t cjt_receiveRaw(uint32_t timeout);
+// 发送函数
+bool cjt_sendRaw(uint8_t *data, uint16_t size, uint32_t timeout);
+
+// 读取数据
+bool cjt_read_meter_data(uint8_t* slaveAddress, double* data, uint8_t protocol);
+// 读取地址
+bool cjt_read_addr(uint8_t *data);
+// 设置地址
+bool cjt_set_addr(uint8_t *addr);
+
+double data_translate_to_float(uint8_t* msg);
+
+
+#endif

+ 10 - 0
app/HARDWARE/include/iwdg.h

@@ -0,0 +1,10 @@
+#ifndef __IWDG_H
+#define __IWDG_H
+
+#include "stm32f2xx_hal.h"
+
+extern IWDG_HandleTypeDef hiwdg;
+
+void IWDG_Configuration(void);
+void feedDog(void);
+#endif

+ 1 - 1
app/HARDWARE/include/malloc.h

@@ -56,7 +56,7 @@ void myfree(uint8_t memx,void *ptr);  			           //
 void *mymalloc(uint8_t memx,uint32_t size);			       //内存分配(外部调用)
 void *myrealloc(uint8_t memx,void *ptr,uint32_t size); //重新分配内存(外部调用)
 
-
+void *mycalloc(size_t num, size_t size) ;
 
 #endif
 

+ 11 - 0
app/HARDWARE/include/reset.h

@@ -0,0 +1,11 @@
+#ifndef __RESET_H
+#define __RESET_H
+
+#include "stm32f2xx.h"
+
+#define RESET_PIN                  	GPIO_PIN_7   
+
+void resetConfig(void);
+void reset_task_creat(void);
+
+#endif

+ 24 - 0
app/HARDWARE/source/iwdg.c

@@ -0,0 +1,24 @@
+#include "iwdg.h"
+
+IWDG_HandleTypeDef hiwdg;
+
+void IWDG_Configuration(void)
+{	
+    IWDG_ENABLE_WRITE_ACCESS(&hiwdg);
+		// 4 		min:0.125ms  	max:512ms
+		// 8 		min:0.25ms  	max:1024ms
+		// 16 	min:0.5ms  		max:2048ms
+		// 32 	min:1ms  			max:4096ms
+		// 64 	min:2ms  			max:8192ms
+		// 128	min:4ms  			max:16384ms
+		// 256	min:8ms  			max:32768ms
+		hiwdg.Instance = IWDG;
+		hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
+		hiwdg.Init.Reload = 8000;
+		HAL_IWDG_Init(&hiwdg);
+}
+
+void feedDog(void)
+{
+	HAL_IWDG_Refresh(&hiwdg);
+}

+ 9 - 2
app/HARDWARE/source/malloc.c

@@ -215,8 +215,15 @@ void *myrealloc(uint8_t memx,void *ptr,uint32_t size)
 }
 
 
-
-
+#include "string.h"
+void *mycalloc(size_t num, size_t size) {
+    void* ptr = mymalloc(SRAMEX,num * size); // 分配内存
+    if (ptr == NULL) {
+        return NULL; // 检查内存分配是否成功
+    }
+    memset(ptr, 0, num * size); // 初始化内存为零
+    return ptr;
+}
 
 
 

+ 43 - 0
app/HARDWARE/source/reset.c

@@ -0,0 +1,43 @@
+#include "reset.h"
+#include "timer.h"
+#include "usart.h"
+#include "myFile.h"
+
+static GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+void resetConfig(void)
+{
+	/*开启LED相关的GPIO外设时钟*/
+		__HAL_RCC_GPIOG_CLK_ENABLE();
+
+			/*选择要控制的GPIO引脚*/															   
+    GPIO_InitStruct.Pin = RESET_PIN;
+		/*设置引脚模式为输出模式*/
+    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+		/*设置引脚为上拉模式*/
+    GPIO_InitStruct.Pull = GPIO_PULLUP;
+		/*设置引脚速率 */  
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+		/*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/
+    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);	
+}
+
+void reset_task(void const* arg)
+{
+	while(1)
+	{
+		if(HAL_GPIO_ReadPin(GPIOG, RESET_PIN) == RESET)
+		{
+			DeleteDirFile("device.txt");	
+			__set_PRIMASK(1);
+			NVIC_SystemReset();
+		}		
+		vTaskDelay(100);	
+	}
+}
+
+void reset_task_creat(void)
+{
+		osThreadDef(RESET, reset_task, osPriorityNormal, 0, configMINIMAL_STACK_SIZE * 4);
+		osThreadCreate (osThread(RESET), NULL);
+}

+ 11 - 8
app/HARDWARE/source/usart.c

@@ -5,6 +5,7 @@
 #include "tcp_server.h"
 #include "timer.h"
 #include "updata.h"
+#include "cjt188.h"
 
 uint8_t rxByte;
 uint8_t rx_232_Buffer[RX_BUFFER_SIZE];
@@ -27,12 +28,12 @@ DMA_HandleTypeDef 	DMA_DEBUG_RX;
 //重定向c库函数printf到串口,重定向后可使用printf函数
 int fputc(int ch, FILE *f)
 {
-//		/* 发送一个字节数据到串口 */
-//		USART_232->DR = (ch & (uint16_t)0x01FF);
-//		
-//		/* 等待发送完毕 */
-//		while (__HAL_UART_GET_FLAG(&USART_InitStruct_232, USART_FLAG_TXE) == RESET);	
-//		return (ch);
+		/* 发送一个字节数据到串口 */
+		USART_485->DR = (ch & (uint16_t)0x01FF);
+		
+		/* 等待发送完毕 */
+		while (__HAL_UART_GET_FLAG(&USART_InitStruct_485, USART_FLAG_TXE) == RESET);	
+		return (ch);
 }
 
 
@@ -233,9 +234,11 @@ void USART_485_IRQHandler(void){
 			GATEWAY_PARAMS *get;
 			get= get_gateway_config_params();
 			if(get->device_params->protocol == 3)
-			mmodbus_callback(&USART_InitStruct_485);
+				mmodbus_callback(&USART_InitStruct_485);
 			else if(get->device_params->protocol == 1 || get->device_params->protocol == 2)
-			dlt_callback(&USART_InitStruct_485);
+				dlt_callback(&USART_InitStruct_485);
+			else if (get->device_params->protocol == 4)
+				cjt_callback(&USART_InitStruct_485);
 }
 
 

+ 918 - 0
app/IEC_SERVER/IEC10X/IEC101.c

@@ -0,0 +1,918 @@
+#include "IEC101.h"
+#include "usart.h"
+#include "timer.h"
+
+
+uint8_t flgDIR, flgPRM, flgFCB = 0, flgFCV, flgACD;
+
+uint8_t LINK_ADDRESS = 0x01; //定义链路地址
+uint8_t ProtocolRxBuffer[64] = {0}; //存储主站发过来的命令
+uint8_t TxBuffer[64] = {0};
+uint8_t TxAppBuffer[64] = {0};
+uint8_t RxControlField = 0; //接收到数据中的控制域
+uint8_t RxDIR, RxPRM, RxFCB, RxFCV, RxFunctionCode;
+uint8_t LastFCB;
+uint8_t TxDIR, TxPRM, TxFCB, TxFCV, TxFunctionCode;
+TimeStructure NowTimeStruct;
+uint16_t LastType;
+
+uint8_t Info[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //开关量数量 0/1/2 跌落 3跌落状态 4温度状态 5 欠压 6/7/8/9 漏保
+uint8_t InfoTemp[8]; //温度
+uint8_t DataFromGPRSBuffer[128];
+uint8_t moduleMaskEn; // 模块故障MASK
+/*
+ ****************************************************************************************************
+ * 功能描述:初始化链路层参数
+ * 输入参数:
+ * 返回参数:
+ * 说    明:
+ ****************************************************************************************************
+ */
+void LinkInit(void) {
+	RxDIR = M2S_DIR; //固定值,从站接收到主站的数据时DIR为0,
+	RxPRM = 0; //非固定值
+	TxPRM = 0; //非固定值
+	RxFCB = 0;
+	RxFCV = 0;
+	TxDIR = S2M_DIR; //固定值从站发出数据时DIR为1
+}
+
+//此函数用于CheckSum函数之后,即已经判断为有效帧后再校验地址是否正确
+//返回SUCCESS地址正确,返回ERROR地址不正确
+uint8_t CheckLinkAddress(uint8_t* pBuffer) {
+	uint16_t LinkAddress = 0;
+
+	if (pBuffer[0] == 0x10) {
+		LinkAddress = (uint16_t)pBuffer[2];
+	}
+	else if (pBuffer[0] == 0x68) {
+		LinkAddress = (uint16_t)pBuffer[5];
+	}
+	else {
+		return ERROR; //链路地址不正确
+	}
+
+	if (LinkAddress == LINK_ADDRESS) {
+		return SUCCESS; //链路地址正确
+	}
+	else {
+		return ERROR; //链路地址不正确
+	}
+}
+
+/*
+ ****************************************************************************************************
+ * 功能描述:
+ * 输入参数:
+ * 返回参数:校验和的值
+ * 说    明:
+ ****************************************************************************************************
+ */
+uint8_t GetCheckSum(uint8_t* pBuffer) {
+	uint8_t i;
+	uint8_t TempSum = 0;
+	uint8_t DataLength = 0;
+
+	if (pBuffer[0] == 0x10)
+		TempSum = pBuffer[1] + pBuffer[2];
+
+	else if (pBuffer[0] == 0x68) {
+		DataLength = pBuffer[1]; //获取可变帧数据长度
+
+		for (i = 0; i < DataLength; i++)
+			TempSum += pBuffer[i + 4];
+	}
+	else
+		TempSum = 0;
+
+	return TempSum;
+	//////////////////////////////////////////////////////////////////////////
+}
+
+/*
+ ****************************************************************************************************
+ * 功能描述:
+ * 输入参数:
+ * 返回参数:
+ * 说    明:
+ ****************************************************************************************************
+ */
+uint8_t CheckError(uint8_t* pBuffer) {
+	uint8_t Length1, Length2;
+
+	if ((pBuffer[0] == 0x68) && (pBuffer[3] == 0x68)) {
+		//可变帧长
+		Length1 = pBuffer[1];
+		Length2 = pBuffer[2];
+
+		if (Length1 != Length2)
+			return 0; //两个长度字节不相等,返回错误
+		else
+			//校验和相等且最后一个字节为0x16
+			if ((GetCheckSum(pBuffer) == pBuffer[4 + Length1]) && (pBuffer[5 + Length1] == 0x16)) {
+			RxControlField = pBuffer[4]; //帧校验成功后取得控制域数据
+			return VARIABLE_DATA;
+		}
+		else
+			return 0;
+	}
+
+	/* 固定帧长第一字节和最后字节校验 */
+	else if ((pBuffer[0] == 0x10) && (pBuffer[5] == 0x16)) {
+		//检验和是否相等
+		if (GetCheckSum(pBuffer) == pBuffer[3]) {
+			RxControlField = pBuffer[1]; //取得控制域数据
+			return STABLE_DATA;
+		}
+		else
+			return 0;
+	}
+	else
+		//无效帧,丢弃
+		return 0;
+}
+
+/*
+ ****************************************************************************************************
+ * 功能描述:
+ * 输入参数:
+ * 返回参数:
+ * 说    明:
+ ****************************************************************************************************
+ */
+uint8_t Protocol101_RxLink(void) {
+	RxDIR = (RxControlField >> 7) & 0x01; //取得DIR位
+	RxPRM = (RxControlField >> 6) & 0x01; //取得PRM位
+	RxFCB = (RxControlField >> 5) & 0x01; //取得FCB位
+	RxFCV = (RxControlField >> 4) & 0x01; //取得FCV位
+	RxFunctionCode = RxControlField & 0x0F; //取得功能码
+
+	if ((RxDIR == M2S_DIR) && (RxPRM == 1))
+		return SUCCESS; //接收标志位判断
+	else {
+		// newFrame = 1;
+		LastFCB = RxFCB;
+		return ERROR;
+	}
+}
+
+/*
+ ****************************************************************************************************
+ * 功能描述:
+ * 输入参数:
+ * 返回参数:
+ * 说    明:
+ ****************************************************************************************************
+ */
+uint8_t SetTxControlField(uint8_t PRM, uint8_t FCB, uint8_t FCV, uint8_t FuncCode) {
+	uint8_t Temp = 0;
+	Temp |= ((S2M_DIR & 0x01) << 7);
+	Temp |= ((PRM & 0x01) << 6);
+	Temp |= ((FCB & 0x01) << 5);
+	Temp |= ((FCV & 0x01) << 4);
+	Temp |= (FuncCode & 0x0F);
+	return Temp;
+}
+
+void SendStableData(uint8_t PRM, uint8_t FCB, uint8_t FCV, uint8_t FuncCode) {
+	uint8_t TxCrtlField = 0;
+	uint8_t CheckSum = 0;
+	uint8_t FrameData[6] = {0x00};
+	TxCrtlField = SetTxControlField(PRM, FCB, FCV, FuncCode);
+	FrameData[0] = 0x10;
+	FrameData[1] = TxCrtlField;
+	FrameData[2] = LINK_ADDRESS;
+	CheckSum = FrameData[1] + FrameData[2];
+	FrameData[3] = CheckSum;
+	FrameData[4] = 0x16;
+	SendData(FrameData, 5); //串口发送
+}
+
+#if 0
+void SendVariableData( uint8_t PRM, uint8_t FCB, uint8_t FCV, uint8_t FuncCode, uint8_t* pBuffer )
+{
+	ASDU_DataStructure ASDU_DataStruct;
+	ASDU_DataStruct.TypeID = 100;
+	ASDU_DataStruct.Qualifier = 1;
+	ASDU_DataStruct.Reason = 7;
+	ASDU_DataStruct.ASDU_Address = LINK_ADDRESS;
+	ASDU_DataStruct.InfoAddress1 = 0x0000;
+	ASDU_DataStruct.InfoData1 = 0X14;
+	ASDU_Init( ASDU_DataStruct, TxAppBuffer );
+}
+#endif
+
+#if 0
+/*
+ ****************************************************************************************************
+ * 功能描述:
+ * 输入参数:TxAppLength:应用层数据不为0,指的是ASDU的长度,发送的是可变帧长数据
+ *           pTxBuffer:链路层数据缓存,最后通过物理通道发送出去
+ *           pTxAppBuffer:应用层数据缓冲区,会在应用层将数据转移到pTxBuffer中
+ * 返回参数:无
+ * 说    明:
+ ****************************************************************************************************
+ */
+void Protocol101_TxLink( uint8_t TxAppLength, uint8_t* pTxBuffer, uint8_t* pTxAppBuffer )
+{
+	uint8_t i;
+	uint8_t TempSum = 0;
+	uint8_t TxCounter = 0;
+
+	//如果应用层数据不为0,则表示发送的是可变帧长数据
+	if ( TxAppLength != 0 ) {
+		pTxBuffer[0] = 0x68;
+		pTxBuffer[1] = TxAppLength + 3;
+		pTxBuffer[2] = TxAppLength + 3;
+		pTxBuffer[3] = 0x68;
+		pTxBuffer[4] = SetTxControlField();
+		TempSum += pTxBuffer[4];
+		pTxBuffer[5] = LINK_ADDRESS & 0xff;
+		TempSum += pTxBuffer[5];
+		pTxBuffer[6] = ( LINK_ADDRESS >> 8 ) & 0xff;
+		TempSum += pTxBuffer[6];
+		TxCounter = 7;
+
+		for ( i = 0; i <= TxAppLength - 1; i++ ) {
+			pTxBuffer[TxCounter] = pTxAppBuffer[i];
+			TempSum += pTxBuffer[TxCounter++];
+		}
+
+		TempSum &= 0xff;
+		pTxBuffer[TxCounter++] = TempSum;
+		pTxBuffer[TxCounter++] = 0x16;
+
+	} else {
+		pTxBuffer[0] = 0x10;
+		pTxBuffer[1] = GetTxControlField();
+		pTxBuffer[2] = LINK_ADDRESS & 0xff;
+		pTxBuffer[3] = ( LINK_ADDRESS >> 8 ) & 0xff;
+
+		//计算校验和
+		for ( i = 1; i < 4; i++ )
+			TempSum += pTxBuffer[i];
+
+		pTxBuffer[4] = TempSum;
+		pTxBuffer[5] = 0x16;
+		TxCounter = 6;
+	}
+
+	SendData( pTxBuffer, TxCounter ); //串口发送
+}
+
+#endif
+uint8_t ASDU_Init(ASDU_DataStructure* ASDU_Struct, uint8_t NumOfInfo, uint8_t* pBuffer) {
+	uint8_t i = 0;
+	uint8_t ASDU_Length = 0;
+	uint8_t ASDU_AddressLow = 0;
+	uint8_t ASDU_AddressHigh = 0;
+	ASDU_AddressLow = LINK_ADDRESS;
+	ASDU_AddressHigh = (LINK_ADDRESS >> 8);
+	pBuffer[0] = ASDU_Struct->TypeID;
+	pBuffer[1] = ASDU_Struct->Qualifier;
+	pBuffer[2] = ASDU_Struct->Reason & 0xff;
+	pBuffer[3] = ASDU_Struct->Reason >> 8;
+	pBuffer[4] = ASDU_Struct->ASDU_Address & 0xff;
+	pBuffer[5] = ASDU_Struct->ASDU_Address >> 8;
+	pBuffer[6] = ASDU_Struct->InfoAddress & 0xFF;
+	pBuffer[7] = ASDU_Struct->InfoAddress >> 8;
+	pBuffer[8] = ASDU_Struct->InfoAddress >> 16;
+	ASDU_Length = 9;
+
+	while (NumOfInfo--) {
+		pBuffer[i + 9] = ASDU_Struct->InfoData[i];
+		ASDU_Length += 1;
+		i++;
+	}
+
+	return ASDU_Length;
+}
+
+#if 0
+/*
+ ****************************************************************************************************
+ * 功能描述:
+ * 输入参数:
+ *
+ *
+
+ *
+ * 返回参数:
+ * 说    明:
+ ****************************************************************************************************
+ */
+void Protocol101_TxApp( uint8_t TypeID, uint8_t Qual, uint8_t SendReason, uint8_t Func, uint8_t TxAppLength )
+{
+	TxFunctionCode = Func & 0x0F;
+
+	if ( TxAppLength != 0 ) {
+		TxAppBuffer[0] = TypeID;                            //类型标识
+		TxAppBuffer[1] = Qual;    //信息体数量
+		TxAppBuffer[2] = ( SendReason & 0xff );
+		TxAppBuffer[3] = LINK_ADDRESS & 0xFF;         //公共地址低位
+		TxAppBuffer[4] = ( LINK_ADDRESS >> 8 ) & 0xFF;  //公共地址高位
+	}
+
+	Protocol101_TxLink( TxAppLength, TxBuffer, TxAppBuffer );
+}
+
+#endif
+uint8_t ResponseLinkStatus(void) {
+	uint8_t Temp = 11;
+	// txAppLength = 0;
+	// i = linkState;
+	Temp &= 0x0F;
+
+	switch (Temp) {
+	case 11:
+		TxFCB = 0;
+		SendStableData(0, 0, 0, 11);
+		// Protocol101_TxApp(0,0,0,0x0b,0);		/* 0x0b 链路状态响应 */
+		break;
+
+	case 14:
+		// Protocol101_TxApp(0,0,0,0x0e,0);		/* 0x0e 链路服务未工作 */
+		break;
+
+	default:
+		// Protocol101_TxApp(0,0,0,0x0f,0);		/* 0x0f 链路服务未完成 */
+		break;
+	}
+
+	// linkState = 0;
+	return 1;
+}
+
+//主站复位远方链路
+uint8_t ResponseResetRemoteLink(void) {
+	SendStableData(0, 0, 0, 0);
+	return SUCCESS;
+}
+// 发送1级数据, 掉用函数,查看当前所有的信息
+void Data1Frame(){
+
+	
+}
+
+// 发送2级数据, 掉用函数,查看当前所有的信息
+void Data2Frame(){
+
+	
+}
+// 激活结束帧
+void overFrame(){
+	uint8_t data[32];
+	data[0] = data[3] = 0x68;
+	data[1] = data[2] = 0x0C;
+	data[4] = 0x08;
+	data[5] = LINK_ADDRESS;
+	data[6] = LastType >> 8; 
+	data[7] = 0x01;
+	data[8] = 0x0A;
+	data[9] = 0x00;
+	data[10] = data[11] = 0xFF;
+	data[12] = data[13] = data[14] = 0x00;
+	if(LastType >> 8 == 0x64)
+		data[15] = 0x14;// 结束全局召唤
+	else if(LastType >> 8 == 0x65)
+		data[15] = 0x05;// 结束召唤电度
+	uint8_t crc = 0;
+	for(uint8_t i = 0; i < 12; i++){
+		crc += data[i + 4];
+	}
+	data[16] = crc;
+	data[17] = 0x16;
+	SendData(data, 18); //串口发送
+}
+//确认帧
+uint8_t ConfirmFrame(void) {
+	uint8_t TxCtrlField = 0;
+	uint8_t CheckSum = 0;
+	uint8_t FrameData[6] = {0x00};
+	TxCtrlField |= ((M2S_DIR & 0x01) << 7);
+	TxCtrlField |= ((0 & 0x01) << 6);
+	TxCtrlField |= ((1 & 0x01) << 5);
+	TxCtrlField |= ((0 & 0x01) << 4);
+	TxCtrlField |= (0 & 0x0F);
+	FrameData[0] = 0x10;
+	FrameData[1] = TxCtrlField;
+	FrameData[2] = LINK_ADDRESS;
+	CheckSum = FrameData[1] + FrameData[2];
+	FrameData[3] = CheckSum;
+	FrameData[4] = 0x16;
+	SendData(FrameData, 5); //串口发送
+}
+//确认总召唤
+uint8_t ResponseCallAll(void) {
+	uint8_t i = 0;
+	uint8_t TxCtrlField = 0;
+	uint8_t TempCallAllBuf[64] = {0x00};
+	uint8_t TempASDU_Buf[32] = {0x00};
+	uint8_t ASDU_Length = 0; // ASDU的长度,并非可变帧长中的L值
+	// uint8_t SendLength = 0; //可变帧长总长度,最后通过串口发送
+	uint8_t CheckSum = 0;
+	ASDU_DataStructure ASDU_DataStruct;
+	//获取控制域的值
+	TxCtrlField |= ((M2S_DIR & 0x01) << 7);
+	TxCtrlField |= ((0 & 0x01) << 6);
+	TxCtrlField |= ((1 & 0x01) << 5);
+	TxCtrlField |= ((0 & 0x01) << 4);
+	TxCtrlField |= (8 & 0x0F);
+	ASDU_DataStruct.TypeID = 0x64;
+	ASDU_DataStruct.Qualifier = 0x01;
+	ASDU_DataStruct.Reason = 0x0007;
+	ASDU_DataStruct.ASDU_Address = 0xffff;
+	ASDU_DataStruct.InfoAddress = 0x000000;
+	ASDU_DataStruct.InfoData[0] = 0x14;
+	ASDU_Length = ASDU_Init(&ASDU_DataStruct, 1,
+							TempASDU_Buf); //填充ASDU数据并返回ASDU的长度(此值并非L)
+	TempCallAllBuf[0] = 0x68;
+	TempCallAllBuf[1] = ASDU_Length + 2;
+	TempCallAllBuf[2] = ASDU_Length + 2;
+	TempCallAllBuf[3] = 0x68;
+	TempCallAllBuf[4] = TxCtrlField;
+	TempCallAllBuf[5] = LINK_ADDRESS;
+
+	for (i = 0; i < ASDU_Length; i++)
+		TempCallAllBuf[i + 6] = TempASDU_Buf[i];
+
+	for (i = 0; i < (ASDU_Length + 2); i++)
+		CheckSum += TempCallAllBuf[i + 4]; //计算校验和
+
+	TempCallAllBuf[ASDU_Length + 6] = CheckSum;
+	TempCallAllBuf[ASDU_Length + 7] = 0x16;
+	SendData(TempCallAllBuf, (ASDU_Length + 8));
+	return 1;
+}
+
+void UpdateDataForCallAll(void) {
+	uint8_t i = 0;
+	uint8_t TxCtrlField = 0;
+	uint8_t TempCallAllBuf[64] = {0x00};
+	uint8_t TempASDU_Buf[32] = {0x00};
+	uint8_t ASDU_Length = 0; // ASDU的长度,并非可变帧长中的L值
+	// uint8_t SendLength = 0; //可变帧长总长度,最后通过串口发送
+	uint8_t CheckSum = 0;
+	ASDU_DataStructure ASDU_DataStruct;
+	//获取控制域的值
+	TxCtrlField = SetTxControlField(0, 0, 0, 0x00);
+	ASDU_DataStruct.TypeID = 0x1; //无时标的单点信息
+	ASDU_DataStruct.Qualifier = 0x8a; //单地址,6个数据
+	ASDU_DataStruct.Reason = 0x14; //响应总召唤
+	ASDU_DataStruct.ASDU_Address = LINK_ADDRESS;
+	ASDU_DataStruct.InfoAddress = 0x0001;
+
+	if (moduleMaskEn == 0) { //非屏蔽状态及时发送状态
+
+		ASDU_DataStruct.InfoData[0] = Info[0];
+		ASDU_DataStruct.InfoData[1] = Info[1];
+		ASDU_DataStruct.InfoData[2] = Info[2];
+		ASDU_DataStruct.InfoData[3] = Info[3];
+		ASDU_DataStruct.InfoData[4] = Info[4];
+		ASDU_DataStruct.InfoData[5] = Info[5];
+		ASDU_DataStruct.InfoData[6] = Info[6]; //漏保1
+		ASDU_DataStruct.InfoData[7] = Info[7]; //漏保2
+		ASDU_DataStruct.InfoData[8] = Info[8]; //漏保3
+		ASDU_DataStruct.InfoData[9] = Info[9]; //漏保4
+	}
+	else {
+		ASDU_DataStruct.InfoData[0] = 0; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[1] = 0; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[2] = 0; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[3] = 0; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[4] = 0; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[5] = 0; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[6] = 0; //漏保1
+		ASDU_DataStruct.InfoData[7] = 0; //漏保2
+		ASDU_DataStruct.InfoData[8] = 0; //漏保3
+		ASDU_DataStruct.InfoData[9] = 0; //漏保4
+	}
+
+	TxCtrlField = SetTxControlField(0, 0, 0, 0x00);
+	ASDU_Length = ASDU_Init(&ASDU_DataStruct, 10,
+							TempASDU_Buf); //填充ASDU数据并返回ASDU的长度(此值并非L)
+	TempCallAllBuf[0] = 0x68;
+	TempCallAllBuf[1] = ASDU_Length + 3;
+	TempCallAllBuf[2] = ASDU_Length + 3;
+	TempCallAllBuf[3] = 0x68;
+	TempCallAllBuf[4] = TxCtrlField;
+	TempCallAllBuf[5] = LINK_ADDRESS & 0xFF;
+	TempCallAllBuf[6] = LINK_ADDRESS >> 8;
+
+	for (i = 0; i < ASDU_Length; i++)
+		TempCallAllBuf[i + 7] = TempASDU_Buf[i];
+
+	for (i = 0; i < (ASDU_Length + 3); i++)
+		CheckSum += TempCallAllBuf[i + 4]; //计算校验和
+
+	TempCallAllBuf[ASDU_Length + 7] = CheckSum;
+	TempCallAllBuf[ASDU_Length + 8] = 0x16;
+	SendData(TempCallAllBuf, (ASDU_Length + 9));
+}
+
+///遥测1:温度
+void UpdateTempForCallAll(void) {
+	uint8_t i = 0;
+	uint8_t TempCallAllBuf[64] = {0x00};
+	uint8_t TxCtrlField = 0;
+	ASDU_DataStructure ASDU_DataStruct;
+	uint8_t TempASDU_Buf[32] = {0x00};
+	uint8_t ASDU_Length = 0; // ASDU的长度,并非可变帧长中的L值
+	uint8_t CheckSum = 0;
+	//获取控制域的值
+	TxCtrlField = SetTxControlField(0, 0, 0, 0x00);
+	ASDU_DataStruct.TypeID = 0x15; //类型标示,不带品质因数的遥测量
+	ASDU_DataStruct.Qualifier = 0x83; //品质因数,单地址3个数据
+	ASDU_DataStruct.Reason = 0x14; //响应总召
+	ASDU_DataStruct.ASDU_Address = LINK_ADDRESS;
+	ASDU_DataStruct.InfoAddress = 0x0001;
+
+	if (moduleMaskEn == 0) { //非屏蔽状态及时发送状态
+		ASDU_DataStruct.InfoData[0] = InfoTemp[0];
+		ASDU_DataStruct.InfoData[1] = InfoTemp[1];
+		ASDU_DataStruct.InfoData[2] = InfoTemp[2];
+	}
+	else {
+		ASDU_DataStruct.InfoData[0] = 20; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[1] = 20; //屏蔽状态都按正常处理
+		ASDU_DataStruct.InfoData[2] = 20; //屏蔽状态都按正常处理
+	}
+
+	TxCtrlField = SetTxControlField(0, 0, 0, 0x00);
+	ASDU_Length = ASDU_Init(&ASDU_DataStruct, 3,
+							TempASDU_Buf); //填充ASDU数据并返回ASDU的长度(此值并非L)
+	TempCallAllBuf[0] = 0x68;
+	TempCallAllBuf[1] = ASDU_Length + 3;
+	TempCallAllBuf[2] = ASDU_Length + 3;
+	TempCallAllBuf[3] = 0x68;
+	TempCallAllBuf[4] = TxCtrlField;
+	TempCallAllBuf[5] = LINK_ADDRESS & 0xFF;
+	TempCallAllBuf[6] = LINK_ADDRESS >> 8;
+
+	for (i = 0; i < ASDU_Length; i++)
+		TempCallAllBuf[i + 7] = TempASDU_Buf[i];
+
+	for (i = 0; i < (ASDU_Length + 3); i++)
+		CheckSum += TempCallAllBuf[i + 4]; //计算校验和
+
+	TempCallAllBuf[ASDU_Length + 7] = CheckSum;
+	TempCallAllBuf[ASDU_Length + 8] = 0x16;
+	SendData(TempCallAllBuf, (ASDU_Length + 9));
+}
+
+void EndOfCallAll(void) {
+	uint8_t i = 0;
+	uint8_t TxCtrlField = 0;
+	uint8_t TempCallAllBuf[64] = {0x00};
+	uint8_t TempASDU_Buf[32] = {0x00};
+	uint8_t ASDU_Length = 0; // ASDU的长度,并非可变帧长中的L值
+	// uint8_t SendLength = 0; //可变帧长总长度,最后通过串口发送
+	uint8_t CheckSum = 0;
+	ASDU_DataStructure ASDU_DataStruct;
+	//获取控制域的值
+	TxCtrlField = SetTxControlField(0, 0, 0, 0x00);
+	ASDU_DataStruct.TypeID = 0x64;
+	ASDU_DataStruct.Qualifier = 0x01;
+	ASDU_DataStruct.Reason = 0x0A;
+	ASDU_DataStruct.ASDU_Address = LINK_ADDRESS;
+	ASDU_DataStruct.InfoAddress = 0x0000;
+	ASDU_DataStruct.InfoData[0] = 0x14;
+	ASDU_Length = ASDU_Init(&ASDU_DataStruct, 1,
+							TempASDU_Buf); //填充ASDU数据并返回ASDU的长度(此值并非L)
+	TempCallAllBuf[0] = 0x68;
+	TempCallAllBuf[1] = ASDU_Length + 3;
+	TempCallAllBuf[2] = ASDU_Length + 3;
+	TempCallAllBuf[3] = 0x68;
+	TempCallAllBuf[4] = TxCtrlField;
+	TempCallAllBuf[5] = LINK_ADDRESS & 0xFF;
+	TempCallAllBuf[6] = LINK_ADDRESS >> 8;
+
+	for (i = 0; i < ASDU_Length; i++)
+		TempCallAllBuf[i + 7] = TempASDU_Buf[i];
+
+	for (i = 0; i < (ASDU_Length + 3); i++)
+		CheckSum += TempCallAllBuf[i + 4]; //计算校验和
+
+	TempCallAllBuf[ASDU_Length + 7] = CheckSum;
+	TempCallAllBuf[ASDU_Length + 8] = 0x16;
+	SendData(TempCallAllBuf, (ASDU_Length + 9));
+}
+
+void GetClockFromServer(TimeStructure* TimeStruct, uint8_t* pBuffer) {
+	uint16_t Temp = 0;
+	Temp = pBuffer[15] << 8;
+	Temp += pBuffer[14];
+	TimeStruct->MilliSec = (Temp % 1000);
+	TimeStruct->Sec = (uint8_t)(Temp / 1000);
+	TimeStruct->Min = pBuffer[16];
+	TimeStruct->Hour = pBuffer[17];
+	TimeStruct->Day = pBuffer[18];
+	TimeStruct->Month = pBuffer[19];
+	TimeStruct->Year = pBuffer[20];
+}
+
+// InfoAdress -- 上单元编号:1~6
+// Info -- 上单元状态:0-正常;1-跌落
+//*Time -- 时间结构体
+
+void ChangeUpdate(uint16_t InfoAdress, uint8_t Info, TimeStructure* Time) {
+	uint8_t i;
+	uint8_t CheckSum = 0;
+	uint8_t InfoArray[32] = {0x00};
+	// static uint8_t PreInfo = 0;
+	// static uint16_t PreInfoAdress = 0;
+	// if ((PreInfoAdress != InfoAdress) || (PreInfo != Info))
+	//{
+	InfoArray[0] = 0x68;
+	InfoArray[1] = 0x12;
+	InfoArray[2] = 0x12;
+	InfoArray[3] = 0x68;
+	InfoArray[4] = SetTxControlField(1, 0, 0, 3);
+	InfoArray[5] = LINK_ADDRESS & 0xFF;
+	InfoArray[6] = LINK_ADDRESS >> 8;
+	InfoArray[7] = 0x1E; // 0x1E==30,带CP56Time2a时标的单点信息
+	InfoArray[8] = 0x01;
+	InfoArray[9] = 0x03; //传送原因:突发
+	InfoArray[10] = LINK_ADDRESS & 0xFF;
+	InfoArray[11] = LINK_ADDRESS >> 8;
+	InfoArray[12] = InfoAdress & 0xFF;
+	InfoArray[13] = InfoAdress >> 8;
+	InfoArray[14] = Info;
+	InfoArray[15] = (uint8_t)((Time->Sec * 1000) + Time->MilliSec);
+	InfoArray[16] = ((Time->Sec * 1000) + Time->MilliSec) >> 8;
+	InfoArray[17] = Time->Min;
+	InfoArray[18] = Time->Hour;
+	InfoArray[19] = Time->Day;
+	InfoArray[20] = Time->Month;
+	InfoArray[21] = Time->Year;
+
+	for (i = 0; i < 0x12; i++)
+		CheckSum += InfoArray[i + 4];
+
+	InfoArray[22] = CheckSum;
+	InfoArray[23] = 0x16;
+	//   PreInfoAdress = InfoAdress;
+	//   PreInfo = Info;
+	SendData(InfoArray, 24);
+	// }
+}
+
+void TempChangeUpdate(uint16_t InfoAdress, uint8_t Info, TimeStructure* Time) {
+	uint8_t i;
+	uint8_t CheckSum = 0;
+	uint8_t InfoArray[32] = {0x00};
+	static uint8_t PreInfo = 0;
+	static uint16_t PreInfoAdress = 0;
+
+	if ((PreInfoAdress != InfoAdress) || (PreInfo != Info)) {
+		InfoArray[0] = 0x68;
+		InfoArray[1] = 0x12;
+		InfoArray[2] = 0x12;
+		InfoArray[3] = 0x68;
+		InfoArray[4] = SetTxControlField(1, 0, 0, 3);
+		InfoArray[5] = LINK_ADDRESS & 0xFF;
+		InfoArray[6] = LINK_ADDRESS >> 8;
+		InfoArray[7] = 0x23; // 0x23==35,带CP56Time2a时标的测量标量值
+		InfoArray[8] = 0x01;
+		InfoArray[9] = 0x03; //传送原因:突发
+		InfoArray[10] = LINK_ADDRESS & 0xFF;
+		InfoArray[11] = LINK_ADDRESS >> 8;
+		InfoArray[12] = InfoAdress & 0xFF;
+		InfoArray[13] = InfoAdress >> 8;
+		InfoArray[14] = Info;
+		InfoArray[15] = (uint8_t)((Time->Sec * 1000) + Time->MilliSec);
+		InfoArray[16] = ((Time->Sec * 1000) + Time->MilliSec) >> 8;
+		InfoArray[17] = Time->Min;
+		InfoArray[18] = Time->Hour;
+		InfoArray[19] = Time->Day;
+		InfoArray[20] = Time->Month;
+		InfoArray[21] = Time->Year;
+
+		for (i = 0; i < 0x12; i++)
+			CheckSum += InfoArray[i + 4];
+
+		InfoArray[22] = CheckSum;
+		InfoArray[23] = 0x16;
+		PreInfoAdress = InfoAdress;
+		PreInfo = Info;
+		SendData(InfoArray, 24);
+	}
+}
+
+void ResponseTimeSynchronous(void) {
+	uint8_t i = 0;
+	uint8_t CheckSum = 0;
+	uint16_t Temp = 0;
+	uint8_t Time[32] = {0x00};
+	Temp = NowTimeStruct.Sec * 1000 + NowTimeStruct.MilliSec;
+	Time[0] = 0x68;
+	Time[1] = 0x11;
+	Time[2] = 0x11;
+	Time[3] = 0x68;
+	Time[4] = SetTxControlField(0, 0, 0, 0);
+	Time[5] = LINK_ADDRESS & 0xFF;
+	Time[6] = LINK_ADDRESS >> 8;
+	Time[7] = 0x67;
+	Time[8] = 0x01;
+	Time[9] = 0x07; //激活确认
+	Time[10] = LINK_ADDRESS & 0xFF;
+	Time[11] = LINK_ADDRESS >> 8;
+	Time[12] = 0x00;
+	Time[13] = 0x00;
+	Time[14] = Temp & 0xFF;
+	Time[15] = Temp >> 8;
+	Time[16] = NowTimeStruct.Min;
+	Time[17] = NowTimeStruct.Hour;
+	Time[18] = NowTimeStruct.Day;
+	Time[19] = NowTimeStruct.Month;
+	Time[20] = NowTimeStruct.Year;
+
+	for (i = 0; i < 17; i++)
+		CheckSum += Time[i + 4];
+
+	Time[21] = CheckSum;
+	Time[22] = 0x16;
+	SendData(Time, 23);
+}
+/*
+   更改这个结构,以实现通过GPRS发送一个数据时
+   */
+uint8_t DataProcess(void) {
+	uint8_t TempRxFunctionCode = 0;
+	if (CheckLinkAddress(rx_232_Buffer) == SUCCESS) {
+		if (CheckError(rx_232_Buffer) == SUCCESS) {
+			//数据校验通过,可以开始处理数据
+			if (Protocol101_RxLink() == SUCCESS) {
+				TempRxFunctionCode = RxFunctionCode;
+				switch (TempRxFunctionCode) {
+				case 0: //主站复位远方链路
+					ResponseResetRemoteLink();
+					break;
+
+				case 1: //主站复位用户进程		
+					break;
+
+				case 2: //发送\确认链路测试功能
+					
+					break;
+
+				case 3: //发送\确认用户数据
+					if ((rx_232_Buffer[7] == 0x64) && ((rx_232_Buffer[14] == 0x14) || (rx_232_Buffer[15] == 0x14))) {
+						ResponseCallAll();
+						// SysDelay(1000);
+						UpdateDataForCallAll();
+	 					UpdateTempForCallAll(); // SysDelay(1000);
+						EndOfCallAll();
+					}
+
+					if (rx_232_Buffer[7] == 0x67) {
+//						//获取时间
+//						GetClockFromServer(&NowTimeStruct, ProtocolRxBuffer);
+//						//将时间写入时钟芯片
+//						Time_Data[0] = NowTimeStruct.Year;
+//						Time_Data[1] = NowTimeStruct.Month;
+//						Time_Data[2] = NowTimeStruct.Day;
+//						Time_Data[3] = NowTimeStruct.Hour;
+//						Time_Data[4] = NowTimeStruct.Min;
+//						Time_Data[5] = NowTimeStruct.Sec;
+//						//回复确认帧
+//						ResponseTimeSynchronous();
+					}
+
+					break;
+
+				case 4: //发送\无回答用户数据
+					break;
+
+				case 9: //主站请求链路状态
+					ResponseLinkStatus();
+					break;
+				case 10://请求1级用户数据
+					break;
+				case 11://请求2级用户数据
+					break;
+				}
+				
+				
+				
+				
+				return SUCCESS;
+			}
+		}
+	}
+
+	return ERROR;
+}
+
+//判断属于哪条命令
+uint8_t IEC_Protocol(uint8_t* pBuffer) {
+	uint8_t pCheckErrorResult;
+	pCheckErrorResult = CheckError(pBuffer);
+	if (pCheckErrorResult == STABLE_DATA) {
+		if (CheckLinkAddress(pBuffer) == 1) {
+			if (pBuffer[1] == 0x49) {
+				ResponseLinkStatus();
+			}
+			else if (pBuffer[1] == 0x40) {
+				ResponseResetRemoteLink();
+			}
+			//固定帧
+			return STABLE_DATA;
+		}
+	}
+	else if (pCheckErrorResult == VARIABLE_DATA) {
+		if (CheckLinkAddress(pBuffer) == 1) {
+			if ((pBuffer[7] == 0x64) && (pBuffer[14] == 0x14)) //总召命令
+			{
+				ResponseCallAll();
+			}
+			//可变帧长
+			return VARIABLE_DATA;
+		}
+	}
+	else {
+		return INVALID_DATA;
+	}
+	return INVALID_DATA;
+}
+//////////////////////////////////////////
+//																			//
+//						测试功能函数						   	//
+//																		  //
+//////////////////////////////////////////
+static void test(void const* arg) {
+		while (1)		
+		{
+			while(recv_232_done == 0)  delay_ms(100);// 轮询到232串口接收到数据
+			recv_232_done = 0;
+			// 先判断是固定帧还是可变帧,并储存控制域->RxControlField
+			if(CheckError(rx_232_Buffer) == STABLE_DATA){// 固定帧
+				if (CheckLinkAddress(rx_232_Buffer) == SUCCESS) {
+					if (Protocol101_RxLink() == SUCCESS) { // 取得功能码RxFunctionCode
+						if (rx_232_Buffer[1] == 0x49)// 请求链路
+							ResponseLinkStatus();
+						else if (rx_232_Buffer[1] == 0x40)// 复位链路
+							ResponseResetRemoteLink();
+						
+						// 请求1级数据 FCB位每次取反,这里没做FCB的取反判断
+						else if (rx_232_Buffer[1] == 0x7A || rx_232_Buffer[1] == 0x5A)
+						{
+							if(LastType == 0x6406)// 回应总召唤
+							{
+								ResponseCallAll();
+								LastType = 0x6407;
+							}
+							else if(LastType == 0x6407)// 回应已有的1级数据
+							{
+								uint8_t have_data = 0;//后续调用函数,查看当前所有的数据
+								if(have_data)	Data1Frame();//判断是否还有数据
+								else	overFrame();
+							}	
+							
+							if(LastType == 0x6506)// 回应电度召唤
+							{
+								ResponseCallAll();
+								LastType = 0x6507;
+							}
+							else if(LastType == 0x6507)// 回应已有的1级数据
+							{
+								uint8_t have_data = 0;//后续调用函数,查看当前所有的数据
+								if(have_data)	Data2Frame();//判断是否还有数据
+								else	overFrame();
+							}	
+						}
+						else if (rx_232_Buffer[1] == 0x7B || rx_232_Buffer[1] == 0x5B)// 请求2级数据 FCB位每次取反
+						{
+							uint8_t noneData = 0xE5;
+							SendData(&noneData,1);
+						}
+					}//Protocol101_RxLink
+				}//CheckLinkAddress
+			}//CheckError
+			else if(CheckError(rx_232_Buffer) == VARIABLE_DATA)// 可变帧
+			{
+				if (CheckLinkAddress(rx_232_Buffer) == SUCCESS) {
+					if (Protocol101_RxLink() == SUCCESS) { // 取得功能码RxFunctionCode
+						if ((rx_232_Buffer[6] == 0x64) && rx_232_Buffer[8] == 0x06 && (rx_232_Buffer[15] == 0x14)) //总召命令激活
+						{
+							LastType = 0x6406;
+							ConfirmFrame();
+						}
+						if ((rx_232_Buffer[6] == 0x65) && rx_232_Buffer[8] == 0x06 && (rx_232_Buffer[15] == 0x05)) //总召命令激活
+						{
+							LastType = 0x6506;
+							ConfirmFrame();
+						}
+					}//Protocol101_RxLink
+				}//CheckLinkAddress
+				
+			}//CheckError
+	} //while
+} //test
+
+void creatTest(void)
+{
+	osThreadDef(IEC, test, osPriorityNormal, 0, configMINIMAL_STACK_SIZE * 4);
+	osThreadCreate (osThread(IEC), NULL);
+}
+

+ 217 - 0
app/IEC_SERVER/IEC10X/IEC101.h

@@ -0,0 +1,217 @@
+#ifndef __IEC101_H
+#define __IEC101_H
+
+#include "stm32f2xx.h"
+
+
+#define SendData USART_232_Send
+
+#define INVALID_DATA 0
+#define VARIABLE_DATA 1 // 可变帧
+#define STABLE_DATA 2		// 固定帧
+
+//在监视方向上的过程信息
+#define NULL 0 //未定义
+#define M_SP_NA_1 1 //单点信息
+#define M_SP_TA_1 2 //带时标的单点信息
+#define M_DP_NA_1 3 //双点信息
+#define M_DP_TA_1 4 //带时标的双点信息
+#define M_ST_NA_1 5 //步位置信息
+#define M_ST_TA_1 6 //带时标的步位置信息
+#define M_BO_NA_1 7 //32比特串
+#define M_BO_TA_1 8 //带时标的32比特串
+#define M_ME_NA_1 9 //测量值,规一化值
+#define M_ME_TA_1 10 //测量值,带时标的规一化值
+#define M_ME_NB_1 11 //测量值,标度化值
+#define M_ME_TB_1 12 //测量值,带时标的标度化值
+#define M_ME_NC_1 13 //测量值,短浮点数
+#define M_ME_TC_1 14 //测量值,带时标的短浮点数
+#define M_IT_NA_1 15 //累计量
+#define M_IT_TA_1 16 //带时标的累计量
+#define M_EP_TA_1 17 //带时标的继电设备保护事件
+#define M_EP_TB_1 18 //时标的继电保护设备成组启动事件
+#define M_EP_TC_1 19 //带时标的继电保护设备成组输出电路信息
+#define M_PS_NA_1 20 //带变位检出的成组单点信息
+#define M_ME_ND_1 21 //测量值,不带品质描述词的规一化值
+#define RESERVED_22 22 //为将来兼容定义保留
+#define RESERVED_23 23 //为将来兼容定义保留
+#define RESERVED_24 24 //为将来兼容定义保留
+#define RESERVED_25 25 //为将来兼容定义保留
+#define RESERVED_26 26 //为将来兼容定义保留
+#define RESERVED_27 27 //为将来兼容定义保留
+#define RESERVED_28 28 //为将来兼容定义保留
+#define RESERVED_29 29 //为将来兼容定义保留
+#define M_SP_TB_1 30 //带CP56Time2a时标的单点信息
+#define M_DP_TB_1 31 //带CP56Time2a时标的双点信息
+#define M_ST_TB_1 32 //带CP56Time2a时标的步位置信息
+#define M_BO_TB_1 33 //带CP56Time2a时标的32比特串
+#define M_ME_TD_1 34 //带CP56Time2a时标的测量值, 规一化值
+#define M_ME_TE_1 35 //带CP56Time2a时标的测量值, 标度化值
+#define M_ME_TF_1 36 //带CP56Time2a时标的测量值, 短浮点数
+#define M_IT_TB_1 37 //带CP56Time2a时标的累计量
+#define M_EP_TD_1 38 //带CP56Time2a时标的继电保护设备事件
+#define M_EP_TE_1 39 //带CP56Time2a时标的继电保护设备成组启动事件
+#define M_EP_TF_1 40 //带CP56Time2a时标的继电保护设备成组输出电路信息
+#define RESERVED_41 41 //为将来兼容定义保留
+#define RESERVED_42 42 //为将来兼容定义保留
+#define RESERVED_43 43 //为将来兼容定义保留
+#define RESERVED_44 44 //为将来兼容定义保留
+
+//在控制方向上的过程信息
+#define C_SC_NA_1 45 //单点命令
+#define C_DC_NA_1 46 //双点命令
+#define C_RC_NA_1 47 //步调节命令
+#define C_SE_NA_1 48 //设定值命令, 规一化值
+#define C_SE_NB_1 49 //设定值命令, 标度化值
+#define C_SE_NC_1 50 //设定值命令, 短浮点数
+#define C_BO_NA_1 51 //32比特串
+#define RESERVED_52 52 //为将来兼容定义保留
+#define RESERVED_53 53 //为将来兼容定义保留
+#define RESERVED_54 54 //为将来兼容定义保留
+#define RESERVED_55 55 //为将来兼容定义保留
+#define RESERVED_56 56 //为将来兼容定义保留
+#define RESERVED_57 57 //为将来兼容定义保留
+#define RESERVED_58 58 //为将来兼容定义保留
+#define RESERVED_59 59 //为将来兼容定义保留
+#define RESERVED_60 60 //为将来兼容定义保留
+#define RESERVED_61 61 //为将来兼容定义保留
+#define RESERVED_62 62 //为将来兼容定义保留
+#define RESERVED_63 63 //为将来兼容定义保留
+#define RESERVED_64 64 //为将来兼容定义保留
+#define RESERVED_65 65 //为将来兼容定义保留
+#define RESERVED_66 66 //为将来兼容定义保留
+#define RESERVED_67 67 //为将来兼容定义保留
+#define RESERVED_68 68 //为将来兼容定义保留
+#define RESERVED_69 69 //为将来兼容定义保留
+
+//在监视方向的系统命令
+#define M_EI_NA_1 70 //初始化结束
+#define RESERVED_71 71 //为将来兼容定义保留
+#define RESERVED_72 72 //为将来兼容定义保留
+#define RESERVED_73 73 //为将来兼容定义保留
+#define RESERVED_74 74 //为将来兼容定义保留
+#define RESERVED_75 75 //为将来兼容定义保留
+#define RESERVED_76 76 //为将来兼容定义保留
+#define RESERVED_77 77 //为将来兼容定义保留
+#define RESERVED_78 78 //为将来兼容定义保留
+#define RESERVED_79 79 //为将来兼容定义保留
+#define RESERVED_80 80 //为将来兼容定义保留
+#define RESERVED_81 81 //为将来兼容定义保留
+#define RESERVED_82 82 //为将来兼容定义保留
+#define RESERVED_83 83 //为将来兼容定义保留
+#define RESERVED_84 84 //为将来兼容定义保留
+#define RESERVED_85 85 //为将来兼容定义保留
+#define RESERVED_86 86 //为将来兼容定义保留
+#define RESERVED_87 87 //为将来兼容定义保留
+#define RESERVED_88 88 //为将来兼容定义保留
+#define RESERVED_89 89 //为将来兼容定义保留
+#define RESERVED_90 90 //为将来兼容定义保留
+#define RESERVED_91 91 //为将来兼容定义保留
+#define RESERVED_92 92 //为将来兼容定义保留
+#define RESERVED_93 93 //为将来兼容定义保留
+#define RESERVED_94 94 //为将来兼容定义保留
+#define RESERVED_95 95 //为将来兼容定义保留
+#define RESERVED_96 96 //为将来兼容定义保留
+#define RESERVED_97 97 //为将来兼容定义保留
+#define RESERVED_98 98 //为将来兼容定义保留
+#define RESERVED_99 99 //为将来兼容定义保留
+
+//在控制方向的系统命令
+#define C_IC_NA_1 100 //总召唤命令
+#define C_CI_NA_1 101 //计数量召唤命令
+#define C_RD_NA_1 102 //读命令
+#define C_CS_NA_1 103 //时钟同步命令
+#define C_TS_NA_1 104 //测试命今
+#define C_RP_NA_1 105 //复位进程命令
+#define C_CD_NA_1 106 //延时获得命今
+#define RESERVED_107 107 //为将来兼容定义保留
+#define RESERVED_108 108 //为将来兼容定义保留
+#define RESERVED_109 109 //为将来兼容定义保留
+
+//在控制方向的参数命令
+#define P_ME_NA_1 110 //测量值参数, 规一化值
+#define P_ME_NB_1 111 //测量值参数, 标度化值
+#define P_ME_NC_1 112 //测量值参数, 短浮点数
+#define P_AC_NA_1 113 //参数激活
+#define RESERVED_114 114 //为将来兼容定义保留
+#define RESERVED_115 115 //为将来兼容定义保留
+#define RESERVED_116 116 //为将来兼容定义保留
+#define RESERVED_117 117 //为将来兼容定义保留
+#define RESERVED_118 118 //为将来兼容定义保留
+#define RESERVED_119 119 //为将来兼容定义保留
+
+//文件传输
+#define F_FR_NA_1 120 //文件准备就绪
+#define F_SR_NA_1 121 //节准备就绪
+#define F_SC_NA_1 122 //召唤目录, 选择文件, 召唤文件,召唤节
+#define F_LS_NA_1 123 //最后的节,最后的段
+#define F_AF_NA_1 124 //认可文件,认可节
+#define F_SG_NA_1 125 //段
+#define F_DR_TA_1 126 //目录
+#define RESERVED_127 127 //为将来兼容定义保留
+
+//定义控制域功能码(平衡传输)
+//启动站向从动站传输报文控制域的功能码(PRM=1)
+#define RST_LINK_FUNC 0x00
+#define RST_PROCESS_FUNC 0x01
+#define TEST_LINK_FUNC 0x02
+#define USER_DATA_FUNC 0x03
+#define NO_ANSWER_DATA_FUNC 0x04
+#define LINK_STATUS_FUNC 0x09
+
+//从动站向启动站传输报文控制域的功能码(PRM=0)
+#define RETURN_OK_FUNC 0x00
+#define RETURN_NO_OK_FUNC 0x01
+#define ACK_LINK_STATUS 0x0B
+
+#define S2M_DIR 1 //从从站(B站)到主站(A站)DIR为1
+#define M2S_DIR 0 //从主站(A站)到从站(B站)DIR为0
+
+typedef struct {
+	uint8_t TypeID;
+	uint8_t Qualifier;
+	uint16_t Reason;
+	uint16_t ASDU_Address;
+	uint16_t InfoAddress;
+	uint8_t InfoData[16];
+} ASDU_DataStructure;
+
+typedef struct {
+	uint8_t Year;
+	uint8_t Month;
+	uint8_t Day;
+	uint8_t Hour;
+	uint8_t Min;
+	uint8_t Sec;
+	uint16_t MilliSec;
+} TimeStructure;
+
+extern uint8_t Info[16]; //开关量数量 0/1/2 跌落 3跌落状态 4温度状态 5 欠压 6/7/8/9 漏保
+extern uint8_t InfoTemp[8]; //温度
+extern uint8_t DataFromGPRSBuffer[128];
+extern uint8_t moduleMaskEn; // 模块故障MASK
+
+extern unsigned char Time_Data[8];
+
+extern uint8_t ProtocolRxBuffer[64]; //存储主站发过来的命令
+
+extern uint8_t LINK_ADDRESS; //定义链路地址
+
+
+void creatTest(void);
+
+uint8_t CheckError(uint8_t* pBuffer);
+
+uint8_t IEC_Protocol(uint8_t* pBuffer);
+
+uint8_t DataProcess(void);
+
+void LinkInit(void);
+
+uint8_t ResponseCallAll(void);
+
+void ChangeUpdate(uint16_t InfoAdress, uint8_t Info, TimeStructure* Time);
+
+void TempChangeUpdate(uint16_t InfoAdress, uint8_t Info, TimeStructure* Time);
+#endif //__101_PROTOCOL_H__
+

+ 0 - 0
app/IEC_SERVER/IEC10X/test.c


+ 129 - 0
app/IEC_SERVER/lib60870-C/CMakeLists.txt

@@ -0,0 +1,129 @@
+cmake_minimum_required(VERSION 3.0)
+
+# automagically detect if we should cross-compile
+if(DEFINED ENV{TOOLCHAIN})
+	set(CMAKE_C_COMPILER	$ENV{TOOLCHAIN}gcc)
+	set(CMAKE_CXX_COMPILER	$ENV{TOOLCHAIN}g++)
+	set(CMAKE_AR	"$ENV{TOOLCHAIN}ar" CACHE FILEPATH "CW archiver" FORCE)
+endif()
+
+project(lib60870-C)
+ENABLE_TESTING()
+
+set(LIB_VERSION_MAJOR "2")
+set(LIB_VERSION_MINOR "3")
+set(LIB_VERSION_PATCH "2")
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/")
+
+macro(ADD_C_FLAGS flags)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flags}")
+endmacro()
+
+# feature checks
+include(CheckLibraryExists)
+check_library_exists(rt clock_gettime "time.h" CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
+
+# check if we are on a little or a big endian
+include (TestBigEndian)
+test_big_endian(PLATFORM_IS_BIGENDIAN)
+
+option(BUILD_HAL "Build the platform abstraction layer (HAL)" ON)
+option(BUILD_COMMON "Build common code (shared with other libraries - e.g. libiec61850)" ON)
+
+option(BUILD_EXAMPLES "Build the examples" ON)
+option(BUILD_TESTS "Build the tests" ON)
+
+if(BUILD_HAL)
+
+if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/dependencies/mbedtls-2.28)
+set(WITH_MBEDTLS 1)
+else()
+message("NOTE: mbedtls 2.28 is required for TLS support!")
+endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/dependencies/mbedtls-2.28)
+
+endif(BUILD_HAL)
+
+include_directories(
+    ${CMAKE_CURRENT_LIST_DIR}/config
+    ${CMAKE_CURRENT_LIST_DIR}/src/file-service
+    ${CMAKE_CURRENT_LIST_DIR}/src/inc/api
+    ${CMAKE_CURRENT_LIST_DIR}/src/inc/internal
+    ${CMAKE_CURRENT_LIST_DIR}/src/common/inc
+    ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc
+)
+
+if(WITH_MBEDTLS)
+include_directories(
+    ${CMAKE_CURRENT_LIST_DIR}/src/hal/tls/mbedtls
+    ${CMAKE_CURRENT_LIST_DIR}/dependencies/mbedtls-2.28/include
+)
+
+file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/dependencies/mbedtls-2.28/library/*.c)
+
+add_definitions(-DCONFIG_CS104_SUPPORT_TLS=1)
+add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h")
+
+endif(WITH_MBEDTLS)
+
+
+set(API_HEADERS 
+	${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_time.h 
+	${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_thread.h
+	${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_socket.h
+	${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_serial.h
+	${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_base.h
+	${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/tls_config.h
+	${CMAKE_CURRENT_LIST_DIR}/src/common/inc/linked_list.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs101_master.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs101_slave.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs104_slave.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/iec60870_master.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/iec60870_slave.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/iec60870_common.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs101_information_objects.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs104_connection.h
+	${CMAKE_CURRENT_LIST_DIR}/src/inc/api/link_layer_parameters.h
+	${CMAKE_CURRENT_LIST_DIR}/src/file-service/cs101_file_service.h
+)
+
+include(CheckCCompilerFlag)
+
+check_c_compiler_flag("-Wredundant-decls" SUPPORT_REDUNDANT_DECLS)
+if (SUPPORT_REDUNDANT_DECLS)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls")
+endif(SUPPORT_REDUNDANT_DECLS)
+
+# write the detected stuff to this file
+# configure_file(config/lib60870_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config/lib60870_config.h)
+
+if(BUILD_EXAMPLES)
+	add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/examples)
+endif(BUILD_EXAMPLES)
+
+if(BUILD_TESTS)
+	add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tests)
+endif(BUILD_TESTS)
+
+add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src)
+
+INSTALL(FILES ${API_HEADERS} DESTINATION include/lib60870 COMPONENT Development)
+
+IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
+INCLUDE(InstallRequiredSystemLibraries)
+ 
+SET(CPACK_PACKAGE_DESCRIPTION "IEC 60870-5-101/104 master/slave library")
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IEC 60870-5-101/104 master/slave library")
+SET(CPACK_PACKAGE_VENDOR "MZ Automation GmbH")
+SET(CPACK_PACKAGE_CONTACT "info@mz-automation.de")
+SET(CPACK_PACKAGE_VERSION_MAJOR "${LIB_VERSION_MAJOR}")
+SET(CPACK_PACKAGE_VERSION_MINOR "${LIB_VERSION_MINOR}")
+SET(CPACK_PACKAGE_VERSION_PATCH "${LIB_VERSION_PATCH}")
+SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CMAKE_SYSTEM_PROCESSOR}")
+SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
+ 
+SET(CPACK_COMPONENTS_ALL Libraries Development Applications)
+#set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}")
+INCLUDE(CPack)
+ 
+ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")

Datei-Diff unterdrückt, da er zu groß ist
+ 2441 - 0
app/IEC_SERVER/lib60870-C/Doxyfile


+ 155 - 0
app/IEC_SERVER/lib60870-C/Makefile

@@ -0,0 +1,155 @@
+LIB60870_HOME=.
+
+include make/target_system.mk
+
+ifndef WITHOUT_COMMON
+
+LIB_SOURCE_DIRS = src/common
+
+endif
+
+LIB_SOURCE_DIRS += src/iec60870
+LIB_SOURCE_DIRS += src/iec60870/cs101
+LIB_SOURCE_DIRS += src/iec60870/cs104
+LIB_SOURCE_DIRS += src/iec60870/link_layer
+LIB_SOURCE_DIRS += src/iec60870/apl
+
+ifndef WITHOUT_HAL
+
+ifeq ($(HAL_IMPL), WIN32)
+LIB_SOURCE_DIRS += src/hal/socket/win32
+LIB_SOURCE_DIRS += src/hal/thread/win32
+LIB_SOURCE_DIRS += src/hal/time/win32
+LIB_SOURCE_DIRS += src/hal/memory
+else ifeq ($(HAL_IMPL), POSIX)
+LIB_SOURCE_DIRS += src/hal/socket/linux
+LIB_SOURCE_DIRS += src/hal/thread/linux
+LIB_SOURCE_DIRS += src/hal/time/unix
+LIB_SOURCE_DIRS += src/hal/serial/linux
+LIB_SOURCE_DIRS += src/hal/memory
+else ifeq ($(HAL_IMPL), BSD)
+LIB_SOURCE_DIRS += src/hal/socket/bsd
+LIB_SOURCE_DIRS += src/hal/thread/bsd
+LIB_SOURCE_DIRS += src/hal/time/unix
+LIB_SOURCE_DIRS += src/hal/memory
+endif
+
+ifdef WITH_MBEDTLS
+LIB_SOURCE_DIRS += dependencies/mbedtls-2.28/library
+LIB_SOURCE_DIRS += src/hal/tls/mbedtls
+LIB_INCLUDE_DIRS += src/hal/tls/mbedtls
+LIB_INCLUDE_DIRS += dependencies/mbedtls-2.28/include
+CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"'
+CFLAGS += -D'CONFIG_CS104_SUPPORT_TLS=1'
+endif
+
+endif
+
+LIB_INCLUDE_DIRS += config
+LIB_INCLUDE_DIRS += src/inc/api
+LIB_INCLUDE_DIRS += src/inc/internal
+LIB_INCLUDE_DIRS += src/hal/inc
+LIB_INCLUDE_DIRS += src/common/inc
+
+
+LIB_INCLUDES = $(addprefix -I,$(LIB_INCLUDE_DIRS))
+
+ifndef INSTALL_PREFIX
+INSTALL_PREFIX = ./.install
+endif
+
+LIB_API_HEADER_FILES = src/hal/inc/hal_time.h 
+LIB_API_HEADER_FILES += src/hal/inc/hal_thread.h
+LIB_API_HEADER_FILES += src/hal/inc/hal_socket.h
+LIB_API_HEADER_FILES += src/hal/inc/hal_serial.h
+LIB_API_HEADER_FILES += src/hal/inc/hal_base.h
+LIB_API_HEADER_FILES += src/common/inc/linked_list.h
+LIB_API_HEADER_FILES += src/inc/api/cs101_information_objects.h
+LIB_API_HEADER_FILES += src/inc/api/cs101_master.h
+LIB_API_HEADER_FILES += src/inc/api/cs101_slave.h
+LIB_API_HEADER_FILES += src/inc/api/cs104_connection.h
+LIB_API_HEADER_FILES += src/inc/api/cs104_slave.h
+LIB_API_HEADER_FILES += src/inc/api/iec60870_common.h
+LIB_API_HEADER_FILES += src/inc/api/iec60870_master.h
+LIB_API_HEADER_FILES += src/inc/api/iec60870_slave.h
+LIB_API_HEADER_FILES += src/inc/api/link_layer_parameters.h
+LIB_API_HEADER_FILES += src/hal/inc/tls_config.h
+LIB_API_HEADER_FILES += src/file-service/cs101_file_service.h
+
+LIB_TEST_SOURCES = tests/all_tests.c
+LIB_TEST_SOURCES += tests/unity/unity.c
+
+LIB_TEST_INCLUDE_DIRS = tests/unity
+
+TEST_INCLUDES = $(addprefix -I,$(LIB_TEST_INCLUDE_DIRS))
+
+get_sources_from_directory  = $(wildcard $1/*.c)
+get_sources = $(foreach dir, $1, $(call get_sources_from_directory,$(dir)))
+src_to = $(addprefix $(LIB_OBJS_DIR)/,$(subst .c,$1,$2))
+
+LIB_SOURCES = $(call get_sources,$(LIB_SOURCE_DIRS))
+
+LIB_OBJS = $(call src_to,.o,$(LIB_SOURCES))
+TEST_OBJS = $(call src_to,.o,$(LIB_TEST_SOURCES))
+CFLAGS += -std=gnu99
+#CFLAGS += -Wno-error=format 
+CFLAGS += -Wstrict-prototypes -Wall -Wextra
+
+ifneq ($(HAL_IMPL), WIN32)
+CFLAGS += -Wuninitialized 
+endif
+
+CFLAGS += -Wsign-compare 
+CFLAGS += -Wpointer-arith 
+CFLAGS += -Wnested-externs 
+CFLAGS += -Wmissing-declarations 
+CFLAGS += -Wshadow
+CFLAGS += -Wall
+#CFLAGS += -Werror  
+
+all:	lib
+
+static_checks:	lib
+	splint -preproc +posixlib +skip-sys-headers +gnuextensions $(LIB_INCLUDES) $(LIB_SOURCES)
+
+cppcheck:	lib
+	cppcheck --xml --force --std=c99 --enable=all $(LIB_INCLUDES) $(LIB_SOURCES) 2> cppcheck-output.xml
+
+lib:	$(LIB_NAME)
+
+tests:	$(TEST_NAME)
+
+dynlib: CFLAGS += -fPIC
+
+dynlib:	$(DYN_LIB_NAME)
+
+.PHONY:	examples
+
+examples:
+	cd examples; $(MAKE)
+	
+$(TEST_NAME):	$(LIB_OBJS) $(TEST_OBJS)
+	$(CC) -o $(TEST_NAME) $(LIB_OBJS) $(TEST_OBJS) -lpthread
+
+$(LIB_NAME):	$(LIB_OBJS)
+	$(AR) r $(LIB_NAME) $(LIB_OBJS)
+	$(RANLIB) $(LIB_NAME)
+	
+$(DYN_LIB_NAME):	$(LIB_OBJS)
+	$(CC) $(LDFLAGS) $(DYNLIB_LDFLAGS) -shared -o $(DYN_LIB_NAME) $(LIB_OBJS) $(LDLIBS)
+
+$(LIB_OBJS_DIR)/%.o: %.c config
+	@echo compiling $(notdir $<)
+	$(SILENCE)mkdir -p $(dir $@)
+	$(CC) $(CFLAGS) -c $(LIB_INCLUDES) $(TEST_INCLUDES) $(OUTPUT_OPTION) $<
+	
+install:	$(LIB_NAME)
+	mkdir -p $(INSTALL_PREFIX)/include
+	mkdir -p $(INSTALL_PREFIX)/lib
+	cp $(LIB_API_HEADER_FILES) $(INSTALL_PREFIX)/include
+	cp $(LIB_NAME) $(INSTALL_PREFIX)/lib
+
+clean:
+	rm -f $(EXAMPLES)
+	rm -rf $(LIB_OBJS_DIR)
+

+ 83 - 0
app/IEC_SERVER/lib60870-C/config/lib60870_config.h

@@ -0,0 +1,83 @@
+/*
+ * lib60870_config.h
+ */
+
+#ifndef CONFIG_LIB60870_CONFIG_H_
+#define CONFIG_LIB60870_CONFIG_H_
+
+
+/* print debugging information with printf if set to 1 */
+#define CONFIG_DEBUG_OUTPUT 0
+
+/**
+ * Define the maximum slave message queue size (for CS 101)
+ *
+ * When set to -1 the message queue size is not limited can be set by the application
+ */
+#define CONFIG_SLAVE_MESSAGE_QUEUE_SIZE -1
+
+/**
+ * Define the default size for the slave (outstation) message queue. This is used also
+ * to buffer ASDUs in the case when the connection is lost.
+ *
+ * For each queued message a maximum of 256 bytes of memory are required. Usually messages are
+ * smaller so more then thus number of messages can be stored in the message queue
+ */
+#define CONFIG_CS104_MESSAGE_QUEUE_SIZE 100
+
+/**
+ * This is a connection specific ASDU queue for the slave (outstation). It is used for connection
+ * specific ASDUs like those that are automatically generated by the stack or created in
+ * the slave side callback. The messages in the queue are removed when the connection is lost.
+ *
+ * For each queued message about 256 bytes of memory are required.
+ */
+#define CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE 50
+
+/**
+ * Compile the library to use threads. This will require semaphore support
+ */
+#define CONFIG_USE_THREADS 1
+
+/**
+ * Compile the library using semaphore to protect critical objects.
+ * Required when CONFIG_USE_THREADS = 1.
+ */
+#define CONFIG_USE_SEMAPHORES 1
+
+/**
+ * Compile library with support for SINGLE_REDUNDANCY_GROUP server mode (only CS104 server)
+ */
+#define CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP 1
+
+/**
+ * Compile library with support for MULTIPLE_REDUNDANCY_GROUPS server mode (only CS104 server)
+ */
+#define CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS 1
+
+/**
+ * Compile library with support for CONNECTION_IS_REDUNDANCY_GROUP server mode (only CS104 server)
+ */
+#define CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP 1
+
+/**
+ * Set the maximum number of client connections
+ */
+#define CONFIG_CS104_MAX_CLIENT_CONNECTIONS 100
+
+/* activate TCP keep alive mechanism. 1 -> activate */
+#define CONFIG_ACTIVATE_TCP_KEEPALIVE 0
+
+/* time (in s) between last message and first keepalive message */
+#define CONFIG_TCP_KEEPALIVE_IDLE 5
+
+/* time between subsequent keepalive messages if no ack received */
+#define CONFIG_TCP_KEEPALIVE_INTERVAL 2
+
+/* number of not missing keepalive responses until socket is considered dead */
+#define CONFIG_TCP_KEEPALIVE_CNT 2
+
+/* test command without timestamp is not allowed for CS104. Set to 1 to enable it anyway. */
+#define CONFIG_ALLOW_C_TS_NA_1_FOR_CS104 0
+
+#endif /* CONFIG_LIB60870_CONFIG_H_ */

+ 8 - 0
app/IEC_SERVER/lib60870-C/dependencies/README

@@ -0,0 +1,8 @@
+README
+------
+
+Please add optional dependencies in this folder
+
+For TLS support with mbedtls download the source tarball of version 2.28.x and extract here in the subfolder (Version 2.28.3 https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v2.28.3.tar.gz)
+
+Rename the extracted mbedtls folder to mbedtls-2.28 (otherwise the build script cannot find the folder and will compile the library without TLS support).

BIN
app/IEC_SERVER/lib60870-C/doxydoc/mz-automation.ico


+ 7 - 0
app/IEC_SERVER/lib60870-C/make/common_targets.mk

@@ -0,0 +1,7 @@
+$(LIB_NAME):
+		cd $(LIB60870_HOME); $(MAKE) -f Makefile
+
+lib:	$(LIB_NAME)
+	
+libclean:	clean
+	cd $(LIB60870_HOME); $(MAKE) -f Makefile clean

+ 3 - 0
app/IEC_SERVER/lib60870-C/make/stack_includes.mk

@@ -0,0 +1,3 @@
+INCLUDES += -I$(LIB60870_HOME)/src/inc/api
+INCLUDES += -I$(LIB60870_HOME)/src/hal/inc
+INCLUDES += -I$(LIB60870_HOME)/src/tls

+ 185 - 0
app/IEC_SERVER/lib60870-C/make/target_system.mk

@@ -0,0 +1,185 @@
+UNAME := $(shell uname)
+
+MIPSEL_TOOLCHAIN_PREFIX=mipsel-openwrt-linux-
+#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-
+#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
+#ARM_TOOLCHAIN_PREFIX=arm-poky-linux-gnueabi-
+ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-
+UCLINUX_ARM_TOOLCHAIN_PREFIX=arm-uclinux-elf-
+UCLINUX_XPORT_TOOLCHAIN_PREFIX=m68k-uclinux-
+#MINGW_TOOLCHAIN_PREFIX=i586-mingw32msvc-
+MINGW_TOOLCHAIN_PREFIX=i686-w64-mingw32-
+MINGW64_TOOLCHAIN_PREFIX=x86_64-w64-mingw32-
+
+#POWERPC_TOOLCHAIN_PREFIX=powerpc-poky-linux-
+POWERPC_TOOLCHAIN_PREFIX=powerpc-linux-gnu-
+
+ifndef TARGET
+ifeq ($(UNAME), Linux)
+TARGET=POSIX
+else ifeq ($(findstring MINGW,$(UNAME)), MINGW)
+TARGET=WIN32
+else ifeq ($(UNAME), Darwin)
+TARGET=BSD
+else ifeq ($(UNAME), FreeBSD)
+TARGET=BSD
+endif
+endif
+
+ifeq ($(TARGET), WIN32)
+WINDOWS=1
+endif
+
+ifeq ($(TARGET), WIN64)
+WINDOWS=1
+endif
+
+ifdef WINDOWS
+ifeq ($(UNAME), Linux)
+ifeq ($(TARGET), WIN32)
+TOOLCHAIN_PREFIX=$(MINGW_TOOLCHAIN_PREFIX)
+endif
+ifeq ($(TARGET), WIN64)
+TOOLCHAIN_PREFIX=$(MINGW64_TOOLCHAIN_PREFIX)
+endif
+else
+TOOLCHAIN_PREFIX=
+endif
+endif
+
+ifeq ($(TARGET), LINUX-POWERPC)
+TOOLCHAIN_PREFIX=$(POWERPC_TOOLCHAIN_PREFIX)
+endif
+
+ifeq ($(TARGET), LINUX-MIPSEL)
+TOOLCHAIN_PREFIX=$(MIPSEL_TOOLCHAIN_PREFIX)
+endif
+
+ifeq ($(TARGET), LINUX-ARM)
+TOOLCHAIN_PREFIX=$(ARM_TOOLCHAIN_PREFIX)
+CFLAGS += -mno-unaligned-access
+# CFLAGS += -mcpu=arm926ej-s
+endif
+
+ifeq ($(TARGET), UCLINUX-WAGO)
+TOOLCHAIN_PREFIX=$(UCLINUX_ARM_TOOLCHAIN_PREFIX)
+CFLAGS += -msoft-float
+CFLAGS += -Wall
+CFLAGS += -DEMBED
+CFLAGS += -Dlinux -D__linux__ -Dunix
+CFLAGS += -D__uClinux__
+CFLAGS += -DTARGET=UCLINUX-WAGO
+LDFLAGS += -Wl,-move-rodata -Wl,-elf2flt
+endif
+
+ifeq ($(TARGET), UCLINUX-XPORT)
+TOOLCHAIN_PREFIX=$(UCLINUX_XPORT_TOOLCHAIN_PREFIX)
+CFLAGS += -DPLATFORM_BYTE_ORDER
+CFLAGS += -mcpu=5208  
+CFLAGS += -fno-builtin -fno-common 
+CFLAGS += -fno-dwarf2-cfi-asm -msep-data -DCONFIG_COLDFIRE -D__linux__ -Dunix -D__uClinux__
+endif
+
+
+ifdef WINDOWS
+HAL_IMPL = WIN32
+LIB_OBJS_DIR = $(LIB60870_HOME)/build_win32
+CFLAGS=-g -DWIN32
+
+ifeq ($(TARGET), WIN32)
+CFLAGS+=-m32
+endif
+
+ifeq ($(TARGET), WIN64)
+CFLAGS+=-m64
+endif
+
+LDLIBS=-lws2_32
+DYNLIB_LDFLAGS=-Wl,-no-undefined -Wl,--enable-runtime-pseudo-reloc -Wl,--output-def,libiec61850.def,--out-implib,libiec61850.a
+
+
+# on Windows: only compile with ethernet support if winpcap files are in third_party/winpcap!
+ifneq (, $(wildcard  $(LIB60870_HOME)/third_party/winpcap/Include/.))
+
+ifeq ($(TARGET), WIN64)
+LDFLAGS += -L$(LIB60870_HOME)/third_party/winpcap/Lib/x64
+else
+LDFLAGS += -L$(LIB60870_HOME)/third_party/winpcap/Lib
+endif
+
+LDLIBS+=-liphlpapi -lwpcap
+else
+$(warning winpcap not found - will build without GOOSE support!)
+CFLAGS += -DEXCLUDE_ETHERNET_WINDOWS
+EXCLUDE_ETHERNET_WINDOWS = 1
+endif
+
+
+else 
+ifeq ($(TARGET), BSD)
+HAL_IMPL = BSD
+else
+HAL_IMPL = POSIX
+endif
+
+
+LDLIBS = -lpthread
+
+
+ifeq ($(TARGET), LINUX-MIPSEL)
+LIB_OBJS_DIR = $(LIB60870_HOME)/build-mipsel
+else ifeq ($(TARGET), LINUX-ARM)
+LIB_OBJS_DIR = $(LIB60870_HOME)/build-arm
+else ifeq ($(TARGET), UCLINUX-WAGO)
+LIB_OBJS_DIR = $(LIB60870_HOME)/build-wago
+CFLAGS += -DTARGET_SYSTEM_UCLINUX_WAGO
+else ifeq ($(TARGET), LINUX-POWERPC)
+LIB_OBJS_DIR = $(LIB60870_HOME)/build-powerpc
+else
+LIB_OBJS_DIR = $(LIB60870_HOME)/build
+endif
+
+CFLAGS += -g 
+#CFLAGS += -Os
+
+DYNLIB_LDFLAGS=-lpthread
+endif
+
+ifneq ($(TARGET), CLANG-CHECK)
+CC=$(TOOLCHAIN_PREFIX)gcc
+CPP=$(TOOLCHAIN_PREFIX)g++
+endif
+
+ifeq ($(TARGET), BSD)
+CC=cc
+CPP=c++
+endif
+
+AR=$(TOOLCHAIN_PREFIX)ar
+RANLIB=$(TOOLCHAIN_PREFIX)ranlib
+
+
+ifeq ($(TARGET), WIN32)
+PROJECT_BINARY_NAME := $(PROJECT_BINARY_NAME).exe
+endif
+
+LIB_NAME = $(LIB_OBJS_DIR)/liblib60870.a
+
+TEST_NAME = $(LIB_OBJS_DIR)/tests.exe
+
+ifeq ($(TARGET), BSD)
+CFLAGS += -arch i386
+LDFLAGS += -arch i386
+endif
+
+ifeq ($(TARGET), WIN32)
+DYN_LIB_NAME = $(LIB_OBJS_DIR)/lib60870.dll
+else 
+
+ifeq ($(TARGET), BSD)
+DYN_LIB_NAME = $(LIB_OBJS_DIR)/liblib60870.dylib
+else
+DYN_LIB_NAME = $(LIB_OBJS_DIR)/liblib60870.so
+endif
+
+endif

+ 173 - 0
app/IEC_SERVER/lib60870-C/src/CMakeLists.txt

@@ -0,0 +1,173 @@
+
+set (lib_common_SRCS
+./file-service/file_server.c
+./iec60870/apl/cpXXtime2a.c
+./iec60870/cs101/cs101_asdu.c
+./iec60870/cs101/cs101_bcr.c
+./iec60870/cs101/cs101_information_objects.c
+./iec60870/cs101/cs101_master_connection.c
+./iec60870/cs101/cs101_master.c
+./iec60870/cs101/cs101_queue.c
+./iec60870/cs101/cs101_slave.c
+./iec60870/cs104/cs104_connection.c
+./iec60870/cs104/cs104_frame.c
+./iec60870/cs104/cs104_slave.c
+./iec60870/link_layer/buffer_frame.c
+./iec60870/link_layer/link_layer.c
+./iec60870/link_layer/serial_transceiver_ft_1_2.c
+./iec60870/frame.c
+./iec60870/lib60870_common.c
+)
+
+if (BUILD_COMMON)
+list (APPEND lib_common_SRCS ./common/linked_list.c)
+endif (BUILD_COMMON)
+
+if (BUILD_HAL)
+
+set (lib_linux_SRCS
+./hal/serial/linux/serial_port_linux.c
+./hal/socket/linux/socket_linux.c
+./hal/thread/linux/thread_linux.c
+./hal/time/unix/time.c
+./hal/memory/lib_memory.c
+)
+
+set (lib_windows_SRCS
+./hal/serial/win32/serial_port_win32.c
+./hal/socket/win32/socket_win32.c
+./hal/thread/win32/thread_win32.c
+./hal/time/win32/time.c
+./hal/memory/lib_memory.c
+)
+
+set (lib_bsd_SRCS
+./hal/serial/linux/serial_port_linux.c
+./hal/socket/bsd/socket_bsd.c
+./hal/thread/bsd/thread_bsd.c
+./hal/time/unix/time.c
+./hal/memory/lib_memory.c
+)
+
+set (lib_macos_SRCS
+./hal/serial/linux/serial_port_linux.c
+./hal/socket/bsd/socket_bsd.c
+./hal/thread/macos/thread_macos.c
+./hal/time/unix/time.c
+./hal/memory/lib_memory.c
+)
+
+endif (BUILD_HAL)
+
+IF(WIN32)
+
+IF(MSVC)
+set_source_files_properties(${lib_common_SRCS} ${lib_windows_SRCS}
+                                       PROPERTIES LANGUAGE CXX)
+ENDIF()
+
+set (library_SRCS
+    ${lib_common_SRCS}
+    ${lib_windows_SRCS}
+)
+
+set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}\"/DEF:${CMAKE_CURRENT_SOURCE_DIR}/vs/lib60870.def\"") 
+
+ELSEIF(UNIX)
+IF(APPLE)
+set (library_SRCS
+    ${lib_common_SRCS}
+    ${lib_macos_SRCS}
+)
+ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+set (library_SRCS
+    ${lib_common_SRCS}
+    ${lib_bsd_SRCS}
+)
+ELSE()
+set (library_SRCS
+    ${lib_common_SRCS}
+    ${lib_linux_SRCS}	
+)
+ENDIF(APPLE)
+ENDIF(WIN32)
+
+IF(WITH_MBEDTLS)
+
+list (APPEND library_SRCS ${tls_SRCS})
+list (APPEND library_SRCS ./hal/tls/mbedtls/tls_mbedtls.c)
+
+add_definitions(-DLIB60870_HAS_TLS_SUPPORT=1)
+
+ENDIF(WITH_MBEDTLS)
+
+include (GenerateExportHeader)
+
+set(RES_FILES "")
+if ( WIN32 )
+	# Adding RC resource file for adding information to the archive
+	set(RES_FILES "${CMAKE_CURRENT_BINARY_DIR}/version.rc")
+	message(STATUS "Generating RC file : ${RES_FILES}")
+	configure_file(
+			${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in
+			${RES_FILES}
+			@ONLY)
+	if( MINGW )
+		set(CMAKE_RC_COMPILER_INIT windres)
+		ENABLE_LANGUAGE(RC)
+		SET(CMAKE_RC_COMPILE_OBJECT
+		"<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
+	endif(MINGW)
+	set(library_SRCS ${library_SRCS} ${RES_FILES})
+endif( WIN32 )
+
+add_library (lib60870-shared SHARED ${library_SRCS} )
+
+set_target_properties(lib60870-shared PROPERTIES
+           OUTPUT_NAME lib60870
+           SOVERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}"
+	   WINDOWS_EXPORT_ALL_SYMBOLS true
+)
+
+
+GENERATE_EXPORT_HEADER(lib60870-shared
+			BASE_NAME lib60870-shared
+			EXPORT_MACRO_NAME lib60870-shared_EXPORT
+			EXPORT_FILE_NAME lib60870-shared_export.h
+			STATIC_DEFINE lib60870-shared_BUILT_AS_STATIC
+)
+
+add_library (lib60870 STATIC ${library_SRCS})
+
+IF(UNIX)
+  IF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
+     target_link_libraries (lib60870
+         -lpthread
+         -lm
+         -lrt
+     )
+  ELSE ()
+     target_link_libraries (lib60870
+         -lpthread
+         -lm
+     )
+  ENDIF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
+
+  configure_file(
+    ${CMAKE_CURRENT_LIST_DIR}/lib60870.pc.in
+    ${CMAKE_CURRENT_BINARY_DIR}/lib60870.pc @ONLY
+  )
+  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib60870.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig")
+ENDIF(UNIX)
+IF(MINGW)
+  target_link_libraries(lib60870-shared ws2_32 iphlpapi)
+  target_link_libraries(lib60870 ws2_32 iphlpapi)
+ENDIF(MINGW)
+
+
+
+install (TARGETS lib60870 lib60870-shared
+	RUNTIME DESTINATION bin COMPONENT Applications
+	ARCHIVE DESTINATION lib COMPONENT Libraries
+    LIBRARY DESTINATION lib COMPONENT Libraries
+)

+ 175 - 0
app/IEC_SERVER/lib60870-C/src/common/inc/linked_list.h

@@ -0,0 +1,175 @@
+/*
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef LINKED_LIST_H_
+#define LINKED_LIST_H_
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup common_api_group
+ */
+/**@{*/
+
+/**
+ * \defgroup LINKED_LIST LinkedList data type definition and handling functions
+ */
+/**@{*/
+
+
+struct sLinkedList {
+	void* data;
+	struct sLinkedList* next;
+};
+
+/**
+ * \brief Reference to a linked list or to a linked list element.
+ */
+typedef struct sLinkedList* LinkedList;
+
+/**
+ * \brief Create a new LinkedList object
+ *
+ * \return the newly created LinkedList instance
+ */
+LinkedList
+LinkedList_create(void);
+
+/**
+ * \brief Delete a LinkedList object
+ *
+ * This function destroy the LinkedList object. It will free all data structures used by the LinkedList
+ * instance. It will call free for all elements of the linked list. This function should only be used if
+ * simple objects (like dynamically allocated strings) are stored in the linked list.
+ *
+ * \param self the LinkedList instance
+ */
+void
+LinkedList_destroy(LinkedList self);
+
+
+typedef void (*LinkedListValueDeleteFunction) (void*);
+
+/**
+ * \brief Delete a LinkedList object
+ *
+ * This function destroy the LinkedList object. It will free all data structures used by the LinkedList
+ * instance. It will call a user provided function for each data element. This user provided function is
+ * responsible to properly free the data element.
+ *
+ * \param self the LinkedList instance
+ * \param valueDeleteFunction a function that is called for each data element of the LinkedList with the pointer
+ *         to the linked list data element.
+ */
+void
+LinkedList_destroyDeep(LinkedList self, LinkedListValueDeleteFunction valueDeleteFunction);
+
+/**
+ * \brief Delete a LinkedList object without freeing the element data
+ *
+ * This function should be used statically allocated data objects are stored in the LinkedList instance.
+ * Other use cases would be if the data elements in the list should not be deleted.
+ *
+ * \param self the LinkedList instance
+ */
+void
+LinkedList_destroyStatic(LinkedList self);
+
+/**
+ * \brief Add a new element to the list
+ *
+ * This function will add a new data element to the list. The new element will the last element in the
+ * list.
+ *
+ * \param self the LinkedList instance
+ * \param data data to append to the LinkedList instance
+ */
+void
+LinkedList_add(LinkedList self, void* data);
+
+/**
+ * \brief Removed the specified element from the list
+ *
+ * \param self the LinkedList instance
+ * \param data data to remove from the LinkedList instance
+ */
+bool
+LinkedList_remove(LinkedList self, const void* data);
+
+/**
+ * \brief Get the list element specified by index (starting with 0).
+ *
+ * \param self the LinkedList instance
+ * \param index index of the requested element.
+ */
+LinkedList
+LinkedList_get(LinkedList self, int index);
+
+/**
+ * \brief Get the next element in the list (iterator).
+ *
+ * \param self the LinkedList instance
+ */
+LinkedList
+LinkedList_getNext(LinkedList self);
+
+/**
+ * \brief Get the last element in the list.
+ *
+ * \param listElement the LinkedList instance
+ */
+LinkedList
+LinkedList_getLastElement(LinkedList self);
+
+/**
+ * \brief Insert a new element int the list
+ *
+ * \param listElement the LinkedList instance
+ */
+LinkedList
+LinkedList_insertAfter(LinkedList listElement, void* data);
+
+/**
+ * \brief Get the size of the list
+ *
+ * \param self the LinkedList instance
+ *
+ * \return number of data elements stored in the list
+ */
+int
+LinkedList_size(LinkedList self);
+
+void*
+LinkedList_getData(LinkedList self);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINKED_LIST_H_ */

+ 186 - 0
app/IEC_SERVER/lib60870-C/src/common/linked_list.c

@@ -0,0 +1,186 @@
+/*
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "linked_list.h"
+#include "lib_memory.h"
+
+#ifndef CONFIG_COMPILE_WITHOUT_COMMON
+
+LinkedList
+LinkedList_getLastElement(LinkedList list)
+{
+    while (list->next != NULL) {
+        list = list->next;
+    }
+    return list;
+}
+
+LinkedList
+LinkedList_create()
+{
+    LinkedList newList;
+
+    newList = (LinkedList) GLOBAL_MALLOC(sizeof(struct sLinkedList));
+    newList->data = NULL;
+    newList->next = NULL;
+
+    return newList;
+}
+
+/**
+ * Destroy list (free). Also frees element data with helper function.
+ */
+void
+LinkedList_destroyDeep(LinkedList list, LinkedListValueDeleteFunction valueDeleteFunction)
+{
+    LinkedList nextElement = list;
+    LinkedList currentElement;
+
+    do {
+        currentElement = nextElement;
+        nextElement = currentElement->next;
+
+        if (currentElement->data != NULL)
+            valueDeleteFunction(currentElement->data);
+
+        GLOBAL_FREEMEM(currentElement);
+    }
+    while (nextElement != NULL);
+}
+
+void
+LinkedList_destroy(LinkedList list)
+{
+    LinkedList_destroyDeep(list, Memory_free);
+}
+
+/**
+ * Destroy list (free) without freeing the element data
+ */
+void
+LinkedList_destroyStatic(LinkedList list)
+{
+    LinkedList nextElement = list;
+    LinkedList currentElement;
+
+    do {
+        currentElement = nextElement;
+        nextElement = currentElement->next;
+        GLOBAL_FREEMEM(currentElement);
+    }
+    while (nextElement != NULL);
+}
+
+int
+LinkedList_size(LinkedList list)
+{
+    LinkedList nextElement = list;
+    int size = 0;
+
+    while (nextElement->next != NULL) {
+        nextElement = nextElement->next;
+        size++;
+    }
+
+    return size;
+}
+
+void
+LinkedList_add(LinkedList list, void* data)
+{
+    LinkedList newElement = LinkedList_create();
+
+    newElement->data = data;
+
+    LinkedList listEnd = LinkedList_getLastElement(list);
+
+    listEnd->next = newElement;
+}
+
+bool
+LinkedList_remove(LinkedList list, const void* data)
+{
+    LinkedList lastElement = list;
+
+    LinkedList currentElement = list->next;
+
+    while (currentElement != NULL) {
+        if (currentElement->data == data) {
+            lastElement->next = currentElement->next;
+            GLOBAL_FREEMEM(currentElement);
+            return true;
+        }
+
+        lastElement = currentElement;
+        currentElement = currentElement->next;
+    }
+
+    return false;
+}
+
+LinkedList
+LinkedList_insertAfter(LinkedList list, void* data)
+{
+    LinkedList originalNextElement = LinkedList_getNext(list);
+
+    LinkedList newElement = LinkedList_create();
+
+    newElement->data = data;
+    newElement->next = originalNextElement;
+
+    list->next = newElement;
+
+    return newElement;
+}
+
+LinkedList
+LinkedList_getNext(LinkedList list)
+{
+    return list->next;
+}
+
+LinkedList
+LinkedList_get(LinkedList list, int index)
+{
+    LinkedList element = LinkedList_getNext(list);
+
+    int i = 0;
+
+    while (i < index) {
+        element = LinkedList_getNext(element);
+
+        if (element == NULL)
+            return NULL;
+
+        i++;
+    }
+
+    return element;
+}
+
+void*
+LinkedList_getData(LinkedList self)
+{
+    return self->data;
+}
+
+#endif
+

+ 143 - 0
app/IEC_SERVER/lib60870-C/src/file-service/cs101_file_service.h

@@ -0,0 +1,143 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef LIB60870_C_FILE_SERVICE_FILE_SERVICE_H_
+#define LIB60870_C_FILE_SERVICE_FILE_SERVICE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "iec60870_common.h"
+#include "iec60870_slave.h"
+
+typedef enum
+{
+    CS101_FILE_ERROR_SUCCESS,
+    CS101_FILE_ERROR_TIMEOUT,
+    CS101_FILE_ERROR_FILE_NOT_READY,
+    CS101_FILE_ERROR_SECTION_NOT_READY,
+    CS101_FILE_ERROR_UNKNOWN_CA,
+    CS101_FILE_ERROR_UNKNOWN_IOA,
+    CS101_FILE_ERROR_UNKNOWN_SERVICE,
+    CS101_FILE_ERROR_PROTOCOL_ERROR,
+    CS101_FILE_ERROR_ABORTED_BY_REMOTE
+} CS101_FileErrorCode;
+
+typedef struct sCS101_FileServer* CS101_FileServer;
+
+typedef struct sCS101_IFileReceiver* CS101_IFileReceiver;
+
+struct sCS101_IFileReceiver
+{
+    void* object; /* user provided context object */
+
+    void (*finished) (CS101_IFileReceiver self, CS101_FileErrorCode result);
+    void (*segmentReceived) (CS101_IFileReceiver self, uint8_t sectionName, int offset, int size, uint8_t* data);
+};
+
+typedef struct sCS101_IFileProvider* CS101_IFileProvider;
+
+struct sCS101_IFileProvider {
+    int ca;
+    int ioa;
+    uint8_t nof;
+
+    void* object; /* user provided context object */
+
+    uint64_t (*getFileDate) (CS101_IFileProvider self);
+    int (*getFileSize) (CS101_IFileProvider self);
+    int (*getSectionSize) (CS101_IFileProvider self, int sectionNumber);
+
+    /**
+     * \brief Get section data (will be called by the file service implementation)
+     *
+     * \param offset byte offset of segment in the section
+     * \param size of segment (has to be at most the size of the provided data buffer)
+     * \param data buffer to store the segment data
+     */
+    bool (*getSegmentData) (CS101_IFileProvider self, int sectionNumber, int offset, int size, uint8_t* data);
+
+    void (*transferComplete) (CS101_IFileProvider self, bool success);
+};
+
+/**
+ * \brief Will be called by the \ŕef CS101_FileServer when the master sends a FILE READY (file download announcement) message to the slave
+ *
+ * \param parameter user provided context parameter
+ * \param ca CA of the file
+ * \param ioa IOA of the file
+ * \param nof the name of file (file type) of the file
+ * \param lengthOfFile the length of the file to be sent
+ * \param[out] errCode error code of file service (0 - ok, 1 - unknown CA, 2 - unknown IOA, 3-unknown name of file, 4 - file not ready )
+ *
+ * \return file handle (interface to access the file)
+ */
+typedef CS101_IFileReceiver (*CS101_FileReadyHandler) (void* parameter, int ca, int ioa, uint16_t nof, int lengthOfFile, int* errCode);
+
+typedef struct sCS101_FilesAvailable* CS101_FilesAvailable;
+
+struct sCS101_FilesAvailable
+{
+    /**
+     * Used to handle request directory call
+     *
+     * \return The next file or NULL if no more file available
+     */
+    CS101_IFileProvider (*getNextFile) (void* parameter, CS101_IFileProvider continueAfter);
+
+    CS101_IFileProvider (*getFile) (void* parameter, int ca, int ioa, uint16_t nof, int* errCode);
+
+    void* parameter;
+};
+
+
+/**
+ * \brief Handle transparent files (implement CS101_IFileProvider interface)
+ */
+typedef struct sCS101_TransparentFile* CS101_TransparentFile;
+
+CS101_IFileProvider
+CS101_TransparentFile_create(int ca, int ioa, uint8_t nof);
+
+CS101_FileServer
+CS101_FileServer_create(CS101_AppLayerParameters alParams);
+
+void
+CS101_FileServer_destroy(CS101_FileServer self);
+
+void
+CS101_FileServer_setFilesAvailableIfc(CS101_FileServer self, CS101_FilesAvailable ifc);
+
+void
+CS101_FileServer_setFileReadyHandler(CS101_FileServer self, CS101_FileReadyHandler handler, void* parameter);
+
+CS101_SlavePlugin
+CS101_FileServer_getSlavePlugin(CS101_FileServer self);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* LIB60870_C_FILE_SERVICE_FILE_SERVICE_H_ */

+ 907 - 0
app/IEC_SERVER/lib60870-C/src/file-service/file_server.c

@@ -0,0 +1,907 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "cs101_file_service.h"
+#include "lib_memory.h"
+#include "iec60870_slave.h"
+#include "lib60870_internal.h"
+#include "hal_time.h"
+
+typedef enum
+{
+    UNSELECTED_IDLE,
+    WAITING_FOR_FILE_CALL,
+    WAITING_FOR_SECTION_CALL,
+    TRANSMIT_SECTION,
+    WAITING_FOR_SECTION_ACK,
+    WAITING_FOR_FILE_ACK,
+    SEND_ABORT,
+    TRANSFER_COMPLETED,
+    WAITING_FOR_SECTION_READY,
+    RECEIVE_SECTION,
+} FileServerState;
+
+struct sCS101_FileServer
+{
+    struct sCS101_SlavePlugin plugin;
+
+    CS101_IFileReceiver fileReceiver;
+
+    CS101_FileReadyHandler fileReadyHandler; /* handler that is called when the master wants to send a file */
+    void* fileReadyHandlerParameter;
+
+    CS101_FilesAvailable filesAvailable;
+
+    int ca;
+    int ioa;
+    uint8_t oa;
+    uint16_t nof;
+
+    uint64_t lastSendTime;
+
+    FileServerState state;
+
+    uint8_t currentSectionNumber;
+    int currentSectionOffset;
+    int currentSectionSize;
+    uint8_t sectionChecksum;
+    uint8_t fileChecksum;
+
+    int maxSegmentSize; /* max. size of segment payload data */
+
+    uint64_t timeout;
+
+    CS101_IFileProvider selectedFile;
+    IMasterConnection selectedConnection;
+};
+
+static void
+sendCallFile(CS101_FileServer self, IMasterConnection connection, int oa)
+{
+    sCS101_StaticASDU _asdu;
+    uint8_t ioBuf[64];
+
+    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+    /* send call file */
+    CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+            oa, self->ca, false, false);
+
+    CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
+        FileCallOrSelect_create((FileCallOrSelect) &ioBuf, self->ioa, self->nof, 0, 2/*=request-file*/));
+
+    IMasterConnection_sendASDU(connection, newAsdu);
+}
+
+static void
+sendCallSection(CS101_FileServer self, IMasterConnection connection, int oa)
+{
+    sCS101_StaticASDU _asdu;
+    uint8_t ioBuf[64];
+
+    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+    /* send call file */
+    CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+            oa, self->ca, false, false);
+
+    CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
+            FileCallOrSelect_create((FileCallOrSelect) &ioBuf, self->ioa, self->nof, self->currentSectionNumber, 6/*=request-section*/));
+
+    DEBUG_PRINT("Send CALL SECTION number: %i\n", self->currentSectionNumber);
+
+    IMasterConnection_sendASDU(connection, newAsdu);
+}
+
+static void
+sendFileAck(CS101_FileServer self, IMasterConnection connection, int oa, uint8_t nos, uint8_t afq)
+{
+    sCS101_StaticASDU _asdu;
+    uint8_t ioBuf[64];
+
+    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+    /* send call file */
+    CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+            oa, self->ca, false, false);
+
+    CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
+        FileACK_create((FileACK) &ioBuf, self->ioa, self->nof, nos, afq));
+
+    IMasterConnection_sendASDU(connection, newAsdu);
+}
+
+static void
+sendSectionReady(CS101_FileServer self, IMasterConnection connection, int oa)
+{
+    sCS101_StaticASDU _asdu;
+    uint8_t ioBuf[64];
+
+    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+    /* send call file */
+    CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+            oa, self->ca, false, false);
+
+    CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
+        SectionReady_create((SectionReady) &ioBuf, self->ioa, self->nof, self->currentSectionNumber, self->currentSectionSize, false));
+
+    IMasterConnection_sendASDU(connection, newAsdu);
+}
+
+static void
+sendLastSection(CS101_FileServer self, IMasterConnection connection, int oa)
+{
+    sCS101_StaticASDU _asdu;
+    uint8_t ioBuf[64];
+
+    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+    /* send call file */
+    CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+            oa, self->ca, false, false);
+
+    CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
+           FileLastSegmentOrSection_create((FileLastSegmentOrSection) &ioBuf, self->ioa, self->nof, self->currentSectionNumber, 1 /* file-transfer-without-deact */, self->fileChecksum));
+
+    IMasterConnection_sendASDU(connection, newAsdu);
+}
+
+static void
+sendFileReady(CS101_FileServer self, IMasterConnection connection, int oa, uint32_t lof, bool positive)
+{
+    sCS101_StaticASDU _asdu;
+    uint8_t ioBuf[64];
+
+    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+    /* send call file */
+    CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+            oa, self->ca, false, false);
+
+    CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
+        FileReady_create((FileReady) &ioBuf, self->ioa, self->nof, lof, positive));
+
+    IMasterConnection_sendASDU(connection, newAsdu);
+}
+
+static uint8_t
+calculateChecksum(uint8_t* data, int size)
+{
+    uint8_t checksum = 0;
+
+    int i;
+
+    for (i = 0; i < size; i++) {
+        checksum += data[i];
+    }
+
+    return checksum;
+}
+
+static bool
+sendSegment(CS101_FileServer self, IMasterConnection connection, int oa)
+{
+    int currentSegmentSize = self->currentSectionSize - self->currentSectionOffset;
+
+    DEBUG_PRINT("sendSegment(%i/%i)\n", self->currentSectionOffset, self->currentSectionSize);
+
+    if (currentSegmentSize > 0) {
+        if (currentSegmentSize > self->maxSegmentSize)
+            currentSegmentSize = self->maxSegmentSize;
+
+        sCS101_StaticASDU _asdu;
+        uint8_t ioBuf[64];
+        uint8_t segmentData[255];
+
+        CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+        /* send call file */
+        CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+                oa, self->ca, false, false);
+
+        self->selectedFile->getSegmentData(self->selectedFile, self->currentSectionNumber - 1, self->currentSectionOffset, currentSegmentSize, segmentData);
+
+        CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
+                FileSegment_create((FileSegment) &ioBuf, self->ioa, self->nof, self->currentSectionNumber, segmentData, currentSegmentSize));
+
+        IMasterConnection_sendASDU(connection, newAsdu);
+
+        self->currentSectionOffset += currentSegmentSize;
+
+        self->lastSendTime = Hal_getTimeInMs();
+        self->sectionChecksum += calculateChecksum(segmentData, currentSegmentSize);
+
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+static void
+sendLastSegment(CS101_FileServer self, IMasterConnection connection, int oa)
+{
+    sCS101_StaticASDU _asdu;
+    uint8_t ioBuf[64];
+
+    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
+
+    /* send call file */
+    CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_FILE_TRANSFER,
+            oa, self->ca, false, false);
+
+    CS101_ASDU_addInformationObject(newAsdu,
+            (InformationObject)
+            FileLastSegmentOrSection_create((FileLastSegmentOrSection) &ioBuf, self->ioa, self->nof, self->currentSectionNumber,
+                    3 /* section-transfer-without-deact */, self->sectionChecksum));
+
+    DEBUG_PRINT("Send LAST SEGMENT (NoS=%i, CHS=%i)\n", self->currentSectionNumber, self->sectionChecksum);
+
+    self->fileChecksum += self->sectionChecksum;
+    self->sectionChecksum = 0;
+
+    IMasterConnection_sendASDU(connection, newAsdu);
+}
+
+/**
+ *
+ * Call inside asdu handler
+ */
+static CS101_SlavePlugin_Result
+CS101_FileServer_handleAsdu(void* parameter, IMasterConnection connection,  CS101_ASDU asdu)
+{
+    CS101_FileServer self = (CS101_FileServer) parameter;
+
+    CS101_SlavePlugin_Result result = CS101_PLUGIN_RESULT_NOT_HANDLED;
+
+    IEC60870_5_TypeID typeId = CS101_ASDU_getTypeID(asdu);
+
+    /* check if type if is in range of file services */
+    if ((typeId >= F_FR_NA_1) && (typeId <= F_SC_NB_1)) {
+
+        /* check for timeout */
+        if (self->state != UNSELECTED_IDLE) {
+            if (Hal_getTimeInMs() > self->lastSendTime + self->timeout) {
+                DEBUG_PRINT ("Abort file transfer due to timeout\n");
+
+                 self->state = UNSELECTED_IDLE;
+            }
+        }
+
+        int oa = CS101_ASDU_getOA(asdu);
+
+        uint8_t ioBuf[250];
+
+        switch (typeId) {
+
+        case F_FR_NA_1: /* File Ready */
+
+            DEBUG_PRINT("Received file ready F_FR_NA_1\n");
+
+            if (self->fileReadyHandler) {
+
+                FileReady fileReady = (FileReady) CS101_ASDU_getElementEx(asdu, (InformationObject) ioBuf, 0);
+
+                int ioa = InformationObject_getObjectAddress((InformationObject) fileReady);
+
+                self->fileReceiver = NULL;
+
+                int errCode = 0;
+
+                if (self->fileReadyHandler) {
+                    self->fileReceiver = self->fileReadyHandler(self->fileReadyHandlerParameter, CS101_ASDU_getCA(asdu), ioa, FileReady_getNOF(fileReady), FileReady_getLengthOfFile(fileReady), &errCode);
+                }
+
+                if (self->fileReceiver) {
+                    self->ca = CS101_ASDU_getCA(asdu);
+                    self->ioa = ioa;
+                    self->oa = oa;
+                    self->nof = FileReady_getNOF(fileReady);
+                    self->fileChecksum = 0;
+
+                    sendCallFile(self, connection, oa);
+
+                    self->lastSendTime = Hal_getTimeInMs();
+                    self->state = WAITING_FOR_SECTION_READY;
+                }
+                else {
+                	if (errCode == 1) {
+                        CS101_ASDU_setNegative(asdu, true);
+                        CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_CA);
+                        IMasterConnection_sendASDU(connection, asdu);
+                	}
+                	else if (errCode == 2) {
+                        CS101_ASDU_setNegative(asdu, true);
+                        CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
+                        IMasterConnection_sendASDU(connection, asdu);
+                	}
+                	else {
+                		self->ca = CS101_ASDU_getCA(asdu);
+						self->ioa = ioa;
+						self->nof = FileReady_getNOF(fileReady);
+                		sendFileReady(self, connection, oa, 0, false);
+                	}
+
+                }
+
+            }
+            else {
+                CS101_ASDU_setNegative(asdu, true);
+                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
+                IMasterConnection_sendASDU(connection, asdu);
+            }
+
+            break;
+
+        case F_SR_NA_1: /* Section Ready */
+
+            if (self->state == WAITING_FOR_SECTION_READY) {
+
+                SectionReady sectionReady = (SectionReady) CS101_ASDU_getElementEx(asdu, (InformationObject) ioBuf, 0);
+
+                self->currentSectionNumber = SectionReady_getNameOfSection(sectionReady);
+                self->currentSectionOffset = 0;
+                self->currentSectionSize = SectionReady_getLengthOfSection(sectionReady);
+
+                /* send call section */
+                sendCallSection(self, connection, oa);
+
+                self->lastSendTime = Hal_getTimeInMs();
+                self->state = RECEIVE_SECTION;
+            }
+
+
+            break;
+
+        case F_SG_NA_1: /* Segment */
+
+            if (self->state == RECEIVE_SECTION) {
+
+                FileSegment segment = (FileSegment) CS101_ASDU_getElementEx(asdu, (InformationObject) ioBuf, 0);
+
+                uint8_t nos = FileSegment_getNameOfSection(segment);
+                uint8_t los = FileSegment_getLengthOfSegment(segment);
+
+                DEBUG_PRINT("Received F_SG_NA_1(segment) (NoS=%i, LoS=%i)\n", nos, los);
+
+                if (self->fileReceiver) {
+                    self->fileReceiver->segmentReceived(self->fileReceiver, nos, self->currentSectionOffset, los, FileSegment_getSegmentData(segment));
+                }
+
+                self->currentSectionOffset += los;
+
+                self->lastSendTime = Hal_getTimeInMs();
+            }
+            else {
+                DEBUG_PRINT ("Unexpected F_SG_NA_1(file segment)\n");
+            }
+
+            break;
+
+        case F_LS_NA_1: /* Last Segment/Section */
+            {
+                DEBUG_PRINT ("Received F_LS_NA_1 (last segment/section)\n");
+
+                FileLastSegmentOrSection lastSection = (FileLastSegmentOrSection) CS101_ASDU_getElementEx(asdu, (InformationObject) ioBuf, 0);
+
+                uint8_t lsq = FileLastSegmentOrSection_getLSQ(lastSection);
+
+                if (self->state == RECEIVE_SECTION) {
+
+                    if (lsq == 3 /* SECTION_TRANSFER_WITHOUT_DEACT */) {
+
+                        DEBUG_PRINT("Send segment ACK for NoS=%i\n", FileLastSegmentOrSection_getNameOfSection(lastSection));
+
+                        sendFileAck(self, connection, oa, FileLastSegmentOrSection_getNameOfSection(lastSection), 3 /* POS_ACK_SECTION */);
+
+                        self->lastSendTime = Hal_getTimeInMs();
+                        self->state = WAITING_FOR_SECTION_READY;
+
+                    }
+                    else if (lsq == 2 /* FILE_TRANSFER_WITH_DEACT */) {
+                        /* master aborted transfer */
+
+                        if (self->fileReceiver) {
+                            self->fileReceiver->finished(self->fileReceiver, CS101_FILE_ERROR_ABORTED_BY_REMOTE);
+                        }
+
+                        self->state = UNSELECTED_IDLE;
+                    }
+                    else {
+                        DEBUG_PRINT("Unexpected LSQ\n");
+                    }
+
+                }
+                else if (self->state == WAITING_FOR_SECTION_READY) {
+
+                    if (lsq == 1 /* FILE_TRANSFER_WITHOUT_DEACT */) {
+                        DEBUG_PRINT("Send file ACK\n");
+
+                        sendFileAck(self, connection, oa, FileLastSegmentOrSection_getNameOfSection(lastSection), 1 /* POS_ACK_FILE */);
+
+                        self->lastSendTime = Hal_getTimeInMs();
+
+                        if (self->fileReceiver) {
+                            self->fileReceiver->finished(self->fileReceiver, CS101_FILE_ERROR_SUCCESS);
+                        }
+
+                        self->state = UNSELECTED_IDLE;
+                    }
+                    else if (lsq == 2 /* FILE_TRANSFER_WITH_DEACT */) {
+                        /* master aborted transfer */
+
+                        if (self->fileReceiver) {
+                            self->fileReceiver->finished(self->fileReceiver, CS101_FILE_ERROR_ABORTED_BY_REMOTE);
+                        }
+
+                        self->state = UNSELECTED_IDLE;
+                    }
+                    else {
+                        DEBUG_PRINT("Unexpected LSQ\n");
+                    }
+                }
+            }
+
+            break;
+
+        case F_AF_NA_1: /*  124 - ACK file, ACK section */
+
+            DEBUG_PRINT("Received file/section ACK F_AF_NA_1\n");
+
+            if (self->state != UNSELECTED_IDLE) {
+
+                FileACK ack = (FileACK) CS101_ASDU_getElementEx(asdu, (InformationObject) ioBuf, 0);
+
+                uint8_t afq = FileACK_getAFQ(ack);
+
+                if (afq == 1 /* POS_ACK_FILE */) {
+                    DEBUG_PRINT("Received positive file ACK\n");
+
+                    if (self->state == WAITING_FOR_FILE_ACK) {
+
+                        if (self->selectedFile)
+                            self->selectedFile->transferComplete(self->selectedFile, true);
+
+                        /* TODO remove file from list of available files */
+
+                        self->selectedFile = NULL;
+                        self->selectedConnection = NULL;
+
+                        self->state = UNSELECTED_IDLE;
+
+                    }
+                    else
+                    {
+                        DEBUG_PRINT("Unexpected file transfer state --> abort file transfer\n");
+
+                        self->state = SEND_ABORT;
+                    }
+                }
+                else if ((afq & 0x0f) == 2 /* NEG_ACK_FILE */) {
+                    DEBUG_PRINT("Received negative file ACK - stop transfer\n");
+
+                    if (self->state == WAITING_FOR_FILE_ACK) {
+
+                        if (self->selectedFile)
+                            self->selectedFile->transferComplete(self->selectedFile, false);
+
+                        self->selectedFile = NULL;
+                        self->selectedConnection = NULL;
+
+                        self->state = UNSELECTED_IDLE;
+                    }
+                    else {
+                        DEBUG_PRINT("Unexpected file transfer state --> abort file transfer\n");
+
+                        self->state = SEND_ABORT;
+                    }
+                }
+                else if ((afq & 0x0f) == 4 /* NEG_ACK_SECTION */) {
+                    DEBUG_PRINT("Received negative file section ACK - repeat section\n");
+
+                    if (self->state == WAITING_FOR_SECTION_ACK) {
+                        self->currentSectionOffset = 0;
+                        self->sectionChecksum = 0;
+
+                        sendSectionReady(self, connection, oa);
+
+                        self->lastSendTime = Hal_getTimeInMs();
+                        self->state = TRANSMIT_SECTION;
+
+                    }
+                    else
+                    {
+                        DEBUG_PRINT("Unexpected file transfer state --> abort file transfer\n");
+
+                        self->state = SEND_ABORT;
+                    }
+                }
+                else if ((afq & 0x0f) == 3 /* POS_ACK_SECTION */) {
+                    DEBUG_PRINT("Received positive section ACK\n");
+
+                    if (self->state == WAITING_FOR_SECTION_ACK) {
+                        self->currentSectionNumber++;
+
+                        int nextSectionSize = self->selectedFile->getSectionSize(self->selectedFile, self->currentSectionNumber - 1);
+
+                        self->currentSectionOffset = 0;
+
+                        if (nextSectionSize <= 0) {
+                            DEBUG_PRINT("Received positive file section ACK - send last section indication\n");
+
+                            sendLastSection(self, connection, oa);
+
+                            self->lastSendTime = Hal_getTimeInMs();
+                            self->state = WAITING_FOR_FILE_ACK;
+                        }
+                        else {
+                            DEBUG_PRINT("Received positive file section ACK - send next section ready indication");
+
+                            self->currentSectionSize = nextSectionSize;
+
+                            sendSectionReady(self, connection, oa);
+
+                            self->lastSendTime = Hal_getTimeInMs();
+                            self->state = WAITING_FOR_SECTION_CALL;
+                        }
+
+                        self->sectionChecksum = 0;
+                    }
+                    else
+                    {
+                        DEBUG_PRINT("Unexpected file transfer state --> abort file transfer\n");
+
+                        self->state = SEND_ABORT;
+                    }
+                }
+
+            }
+            else {
+                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_CA);
+                IMasterConnection_sendASDU(connection, asdu);
+            }
+
+            break;
+
+        case F_SC_NA_1: /* 122 - Call/Select directory/file/section */
+
+            DEBUG_PRINT("Received call/select F_SC_NA_1\n");
+
+            if (CS101_ASDU_getCOT(asdu) == CS101_COT_FILE_TRANSFER)
+            {
+                FileCallOrSelect sc = (FileCallOrSelect) CS101_ASDU_getElementEx(asdu, (InformationObject) ioBuf, 0);
+
+                uint8_t scq = FileCallOrSelect_getSCQ(sc);
+                int ioa = InformationObject_getObjectAddress((InformationObject) sc);
+                uint16_t nof = FileCallOrSelect_getNOF(sc);
+
+                if (scq == 1 /* SELECT_FILE */) {
+
+                    DEBUG_PRINT("Received SELECT FILE\n");
+
+                    if (self->state == UNSELECTED_IDLE) {
+
+                        CS101_IFileProvider file = NULL;
+
+                        int errCode = 0;
+
+                        if (self->filesAvailable)
+                            file = self->filesAvailable->getFile(self->filesAvailable->parameter, CS101_ASDU_getCA(asdu), ioa, nof, &errCode);
+
+                        if (file == NULL) {
+
+                        	if (errCode == 1) {
+                                CS101_ASDU_setNegative(asdu, true);
+                                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_CA);
+                                IMasterConnection_sendASDU(connection, asdu);
+                        	}
+                        	else if (errCode == 2) {
+                                CS101_ASDU_setNegative(asdu, true);
+                                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
+                                IMasterConnection_sendASDU(connection, asdu);
+                        	}
+                        	else {
+                        		self->ca = CS101_ASDU_getCA(asdu);
+        						self->ioa = ioa;
+        						self->nof = nof;
+                        		sendFileReady(self, connection, oa, 0, false);
+                        	}
+
+                        }
+                        else {
+                            /* TODO check if file is already selected by other client */
+
+                            self->selectedFile = file;
+                            self->selectedConnection = connection;
+                            self->ioa = ioa;
+                            self->ca = CS101_ASDU_getCA(asdu);
+                            self->nof = nof;
+
+                            int fileSize = file->getFileSize(file);
+
+                            DEBUG_PRINT("Send FILE READY\n");
+
+                            sendFileReady(self, connection, oa, fileSize, true);
+
+                            self->lastSendTime = Hal_getTimeInMs();
+                            self->state = WAITING_FOR_FILE_CALL;
+                        }
+
+                    }
+                    else
+                    {
+                        DEBUG_PRINT("Unexpected select file message\n");
+                    }
+
+                }
+                else if (scq == 3 /* DEACTIVATE_FILE */) {
+
+                    DEBUG_PRINT("Received DEACTIVATE FILE\n");
+
+                    if (self->state == UNSELECTED_IDLE) {
+                        self->selectedFile = NULL;
+                        self->selectedConnection = NULL;
+
+                        self->state = UNSELECTED_IDLE;
+                    }
+                    else
+                    {
+                        DEBUG_PRINT("Unexpected DEACTIVATE FILE message\n");
+                    }
+                }
+                else if (scq == 2 /* REQUEST_FILE */) {
+
+                    DEBUG_PRINT("Received CALL FILE\n");
+
+                    if (self->state == WAITING_FOR_FILE_CALL) {
+
+                        /* TODO check if NoF matches */
+
+                        if ((ioa != self->ioa) || (CS101_ASDU_getCA(asdu) != self->ca)) {
+
+                            DEBUG_PRINT("Call for file that is not selected!\n");
+
+                            if (CS101_ASDU_getCA(asdu) != self->ca)
+                                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_CA);
+                            else
+                                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
+
+                            CS101_ASDU_setNegative(asdu, true);
+
+                            IMasterConnection_sendASDU(connection, asdu);
+
+                        }
+                        else {
+                            self->currentSectionNumber = 1;
+                            self->currentSectionOffset = 0;
+                            self->fileChecksum = 0;
+                            self->currentSectionSize = self->selectedFile->getSectionSize(self->selectedFile, 0);
+
+                            DEBUG_PRINT("Send SECTION READY\n");
+
+                            sendSectionReady(self, connection, oa);
+
+                            self->lastSendTime = Hal_getTimeInMs();
+                            self->state = WAITING_FOR_SECTION_CALL;
+                        }
+
+                    }
+                    else
+                    {
+                        DEBUG_PRINT("Unexpected CALL FILE message\n");
+                    }
+
+                }
+                else if (scq == 6 /* REQUEST_SECTION */ ) {
+
+                    uint8_t nos = FileCallOrSelect_getNameOfSection(sc);
+
+                    DEBUG_PRINT("Received CALL SECTION (NoS = %i)\n", nos);
+
+                    if (self->state == WAITING_FOR_SECTION_CALL) {
+
+                        /* TODO check if NoF matches */
+
+                        if ((ioa != self->ioa) || (CS101_ASDU_getCA(asdu) != self->ca)) {
+
+                            DEBUG_PRINT("Call for file that is not selected!\n");
+
+                            if (CS101_ASDU_getCA(asdu) != self->ca)
+                                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_CA);
+                            else
+                                CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
+
+                            CS101_ASDU_setNegative(asdu, true);
+
+                            IMasterConnection_sendASDU(connection, asdu);
+
+                        }
+                        else {
+
+                            if (CS101_ASDU_isNegative(asdu)) {
+
+                                self->currentSectionNumber++;
+                                self->currentSectionOffset = 0;
+
+                                self->currentSectionSize = self->selectedFile->getSectionSize(self->selectedFile, self->currentSectionNumber - 1);
+
+                                if (self->currentSectionSize > 0) {
+
+                                    DEBUG_PRINT("Send F_SR_NA_1 (section ready) (NoS = %i)\n", self->currentSectionNumber);
+
+                                    sendSectionReady(self, connection, oa);
+
+                                    self->lastSendTime = Hal_getTimeInMs();
+                                    self->state = WAITING_FOR_SECTION_CALL;
+                                }
+                                else {
+
+                                    DEBUG_PRINT("Send F_LS_NA_1 (last section))\n");
+
+                                    sendLastSection(self, connection, oa);
+
+                                    self->lastSendTime = Hal_getTimeInMs();
+                                    self->state = WAITING_FOR_FILE_ACK;
+                                }
+
+                            }
+                            else { /* positive */
+                                self->currentSectionSize = self->selectedFile->getSectionSize(self->selectedFile, nos - 1);
+
+                                if (self->currentSectionSize > 0) {
+                                    self->currentSectionNumber = nos;
+                                    self->currentSectionOffset = 0;
+
+                                    self->state = TRANSMIT_SECTION;
+                                }
+                                else {
+                                    DEBUG_PRINT("Unexpected number of section -> send negative confirm\n");
+
+                                    CS101_ASDU_setNegative(asdu, true);
+
+                                    IMasterConnection_sendASDU(connection, asdu);
+
+                                    self->lastSendTime = Hal_getTimeInMs();
+                                }
+                            }
+                        }
+                    }
+                    else {
+                        DEBUG_PRINT("Unexpected CALL SECTION message\n");
+                    }
+
+                }
+
+            }
+            else if (CS101_ASDU_getCOT(asdu) == CS101_COT_REQUEST) {
+                /* call directory */
+
+                /* TODO send directory */
+            }
+            else {
+                DEBUG_PRINT("Received unexpected COT = %i\n", CS101_ASDU_getCOT(asdu));
+            }
+
+            break;
+
+        default:
+            DEBUG_PRINT("Received unexpected type ID %i in file service\n", typeId);
+            break;
+        }
+
+        result = CS101_PLUGIN_RESULT_HANDLED;
+    }
+
+
+    return result;
+}
+
+static void
+CS101_FileServer_runTask(void* parameter, IMasterConnection connection)
+{
+    CS101_FileServer self = (CS101_FileServer) parameter;
+
+    if (self->state != UNSELECTED_IDLE) {
+
+        if (self->state == TRANSMIT_SECTION) {
+
+            if (self->selectedConnection == connection) {
+
+                if (self->selectedFile) {
+
+                    if (sendSegment(self, connection, self->oa) == false) {
+                        sendLastSegment(self, connection, self->oa);
+
+                        self->fileChecksum += self->sectionChecksum;
+                        self->sectionChecksum = 0;
+
+                        self->lastSendTime = Hal_getTimeInMs();
+                        self->state = WAITING_FOR_SECTION_ACK;
+                    }
+
+                }
+
+            }
+
+        }
+
+        /* check for timeout */
+        if (Hal_getTimeInMs() > self->lastSendTime + self->timeout) {
+            DEBUG_PRINT ("Abort file transfer due to timeout\n");
+
+             self->state = UNSELECTED_IDLE;
+        }
+
+    }
+}
+
+bool (*handleAsdu) (void* parameter, IMasterConnection connection, CS101_ASDU asdu);
+void (*runTask) (void* parameter, IMasterConnection connection);
+
+void* parameter;
+
+CS101_FileServer
+CS101_FileServer_create(CS101_AppLayerParameters alParams)
+{
+    CS101_FileServer self = (CS101_FileServer) GLOBAL_CALLOC(1, sizeof(struct sCS101_FileServer));
+
+    if (self) {
+        self->timeout = 3000;
+        self->state = UNSELECTED_IDLE;
+
+        self->maxSegmentSize = FileSegment_GetMaxDataSize(alParams);
+
+        self->plugin.parameter = self;
+        self->plugin.handleAsdu = CS101_FileServer_handleAsdu;
+        self->plugin.runTask = CS101_FileServer_runTask;
+    }
+
+    return self;
+}
+
+void
+CS101_FileServer_destroy(CS101_FileServer self)
+{
+    GLOBAL_FREEMEM(self);
+}
+
+void
+CS101_FileServer_setFilesAvailableIfc(CS101_FileServer self, CS101_FilesAvailable ifc)
+{
+    self->filesAvailable = ifc;
+}
+
+void
+CS101_FileServer_setFileReadyHandler(CS101_FileServer self, CS101_FileReadyHandler handler, void* parameter)
+{
+    self->fileReadyHandler = handler;
+    self->fileReadyHandlerParameter = parameter;
+}
+
+CS101_SlavePlugin
+CS101_FileServer_getSlavePlugin(CS101_FileServer self)
+{
+    return &(self->plugin);
+}
+
+
+

+ 201 - 0
app/IEC_SERVER/lib60870-C/src/hal/CMakeLists.txt

@@ -0,0 +1,201 @@
+cmake_minimum_required(VERSION 3.5.1)
+
+# automagically detect if we should cross-compile
+if(DEFINED ENV{TOOLCHAIN})
+    set(CMAKE_C_COMPILER        $ENV{TOOLCHAIN}gcc)
+    set(CMAKE_CXX_COMPILER      $ENV{TOOLCHAIN}g++)
+    set(CMAKE_AR        "$ENV{TOOLCHAIN}ar" CACHE FILEPATH "CW archiver" FORCE)
+endif()
+
+project(hal)
+
+set(LIBHAL_VERSION_MAJOR "2")
+set(LIBHAL_VERSION_MINOR "1")
+set(LIBHAL_VERSION_PATCH "0")
+
+# feature checks
+include(CheckLibraryExists)
+check_library_exists(rt clock_gettime "time.h" CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
+
+# check if we are on a little or a big endian
+include (TestBigEndian)
+test_big_endian(PLATFORM_IS_BIGENDIAN)
+
+if(WIN32)
+
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib")
+message("Found winpcap -> compile ethernet HAL layer (required for GOOSE/SV support)")
+set(WITH_WPCAP 1)
+include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Include")
+else()
+message("winpcap not found -> skip ethernet HAL layer (no GOOSE/SV support)")
+endif()
+
+endif(WIN32)
+
+include_directories(
+    ${CMAKE_CURRENT_LIST_DIR}/inc
+)
+
+set (libhal_linux_SRCS
+ ${CMAKE_CURRENT_LIST_DIR}/socket/linux/socket_linux.c
+ ${CMAKE_CURRENT_LIST_DIR}/ethernet/linux/ethernet_linux.c
+ ${CMAKE_CURRENT_LIST_DIR}/thread/linux/thread_linux.c
+ ${CMAKE_CURRENT_LIST_DIR}/filesystem/linux/file_provider_linux.c
+ ${CMAKE_CURRENT_LIST_DIR}/time/unix/time.c
+ ${CMAKE_CURRENT_LIST_DIR}/serial/linux/serial_port_linux.c
+ ${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c
+)
+
+set (libhal_windows_SRCS
+ ${CMAKE_CURRENT_LIST_DIR}/socket/win32/socket_win32.c
+ ${CMAKE_CURRENT_LIST_DIR}/thread/win32/thread_win32.c
+ ${CMAKE_CURRENT_LIST_DIR}/filesystem/win32/file_provider_win32.c
+ ${CMAKE_CURRENT_LIST_DIR}/time/win32/time.c
+ ${CMAKE_CURRENT_LIST_DIR}/serial/win32/serial_port_win32.c
+ ${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c
+)
+
+if(WITH_WPCAP)
+set (libhal_windows_SRCS ${libhal_windows_SRCS}
+ ${CMAKE_CURRENT_LIST_DIR}/ethernet/win32/ethernet_win32.c
+)
+endif(WITH_WPCAP)
+
+set (libhal_bsd_SRCS
+ ${CMAKE_CURRENT_LIST_DIR}/socket/bsd/socket_bsd.c
+ ${CMAKE_CURRENT_LIST_DIR}/ethernet/bsd/ethernet_bsd.c
+ ${CMAKE_CURRENT_LIST_DIR}/thread/bsd/thread_bsd.c
+ ${CMAKE_CURRENT_LIST_DIR}/filesystem/linux/file_provider_linux.c
+ ${CMAKE_CURRENT_LIST_DIR}/time/unix/time.c
+ ${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c
+)
+
+set (libhal_macos_SRCS
+ ${CMAKE_CURRENT_LIST_DIR}/socket/bsd/socket_bsd.c
+ ${CMAKE_CURRENT_LIST_DIR}/ethernet/bsd/ethernet_bsd.c
+ ${CMAKE_CURRENT_LIST_DIR}/thread/macos/thread_macos.c
+ ${CMAKE_CURRENT_LIST_DIR}/filesystem/linux/file_provider_linux.c
+ ${CMAKE_CURRENT_LIST_DIR}/time/unix/time.c
+ ${CMAKE_CURRENT_LIST_DIR}/memory/lib_memory.c
+)
+
+IF(WIN32)
+
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib")
+message("Found winpcap -> can compile with GOOSE support")
+set(WITH_WPCAP 1)
+endif()
+
+set (libhal_SRCS
+    ${libhal_windows_SRCS}
+)
+
+IF(MSVC)
+set_source_files_properties(${libhal_SRCS}
+                                       PROPERTIES LANGUAGE CXX)
+ENDIF()
+
+ELSEIF(UNIX)
+IF(APPLE)
+set (libhal_SRCS
+    ${libhal_macos_SRCS}
+)
+ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+set (libhal_SRCS
+    ${libhal_bsd_SRCS}
+)
+ELSE()
+set (libhal_SRCS
+    ${libhal_linux_SRCS}
+)
+ENDIF(APPLE)
+ENDIF(WIN32)
+
+#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC" )
+#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC" )
+
+if(WITH_MBEDTLS)
+message("Found mbedtls -> can compile HAL with TLS support")
+set(WITH_MBEDTLS 1)
+endif(WITH_MBEDTLS)
+
+if(WITH_MBEDTLS)
+include_directories(
+	${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls
+    ${MBEDTLS_INCLUDE_DIR}
+)
+
+if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
+link_directories(${CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH})
+else()
+file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.28/library/*.c)
+endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
+
+add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h")
+
+set (libhal_SRCS ${libhal_SRCS}
+  ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls/tls_mbedtls.c
+)
+
+IF(MSVC)
+set_source_files_properties(${libhal_SRCS}
+                                       PROPERTIES LANGUAGE CXX)
+ENDIF()
+
+list (APPEND libhal_SRCS ${tls_SRCS})
+
+endif(WITH_MBEDTLS)
+
+add_library (hal STATIC ${libhal_SRCS})
+
+add_library (hal-shared STATIC ${libhal_SRCS})
+
+target_compile_definitions(hal-shared PRIVATE EXPORT_FUNCTIONS_FOR_DLL)
+
+SET_TARGET_PROPERTIES(hal-shared PROPERTIES
+  COMPILE_FLAGS "-fPIC"
+)
+
+IF(UNIX)
+  IF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
+     target_link_libraries (hal
+         -lpthread
+         -lrt
+     )
+  ELSE ()
+     target_link_libraries (hal
+         -lpthread
+     )
+  ENDIF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
+ENDIF(UNIX)
+
+IF(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
+  target_link_libraries(hal mbedcrypto mbedx509 mbedtls)
+ENDIF(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
+
+IF(MINGW)
+  target_link_libraries(hal ws2_32 iphlpapi)
+ENDIF(MINGW)
+
+iF(WITH_WPCAP)
+target_link_libraries(hal
+	${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib
+	${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/packet.lib
+)
+ENDIF(WITH_WPCAP)
+
+set(BINDIR "bin")
+set(LIBDIR "lib")
+if(UNIX)
+    # GNUInstallDirs is required for Debian multiarch
+    include(GNUInstallDirs)
+    set(LIBDIR ${CMAKE_INSTALL_LIBDIR})
+    set(BINDIR ${CMAKE_INSTALL_BINDIR})
+endif()
+
+install (TARGETS hal hal-shared
+	RUNTIME DESTINATION ${BINDIR} COMPONENT Applications
+	ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries
+    LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries
+)

+ 51 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/hal_base.h

@@ -0,0 +1,51 @@
+/*
+ *  hal_base.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef HAL_INC_HAL_BASE_H_
+#define HAL_INC_HAL_BASE_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef __GNUC__
+#define ATTRIBUTE_PACKED __attribute__ ((__packed__))
+#else
+#define ATTRIBUTE_PACKED
+#endif
+
+#ifndef DEPRECATED
+#if defined(__GNUC__) || defined(__clang__)
+  #define DEPRECATED __attribute__((deprecated))
+#else
+  #define DEPRECATED
+#endif
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__
+    #ifdef EXPORT_FUNCTIONS_FOR_DLL
+        #define PAL_API __declspec(dllexport)
+    #else
+        #define PAL_API
+    #endif
+
+    #define PAL_INTERNAL
+#else
+    #if __GNUC__ >= 4
+        #define PAL_API __attribute__ ((visibility ("default")))
+        #define PAL_INTERNAL  __attribute__ ((visibility ("hidden")))
+    #else
+        #define PAL_API
+        #define PAL_INTERNAL
+    #endif
+#endif
+
+#endif /* HAL_INC_HAL_BASE_H_ */

+ 140 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/hal_serial.h

@@ -0,0 +1,140 @@
+/*
+ *  hal_serial.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_
+#define SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_
+
+#include "hal_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file hal_serial.h
+ * \brief Abstraction layer for serial ports.
+ * Has to be implemented for the serial link layer of CS 101.
+ */
+
+/*! \addtogroup hal Platform (Hardware/OS) abstraction layer
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_SERIAL Access to serial interfaces
+ *
+ *  Serial interface abstraction layer. This functions have to be implemented to
+ *  port lib60870 to new platforms when the serial link layers are required.
+ *
+ * @{
+ */
+
+typedef struct sSerialPort* SerialPort;
+
+typedef enum {
+    SERIAL_PORT_ERROR_NONE = 0,
+    SERIAL_PORT_ERROR_INVALID_ARGUMENT = 1,
+    SERIAL_PORT_ERROR_INVALID_BAUDRATE = 2,
+    SERIAL_PORT_ERROR_OPEN_FAILED = 3,
+    SERIAL_PORT_ERROR_UNKNOWN = 99
+} SerialPortError;
+
+/**
+ * \brief Create a new SerialPort instance
+ *
+ * \param interfaceName identifier or name of the serial interface (e.g. "/dev/ttyS1" or "COM4")
+ * \param baudRate the baud rate in baud (e.g. 9600)
+ * \param dataBits the number of data bits (usually 8)
+ * \param parity defines what kind of parity to use ('E' - even parity, 'O' - odd parity, 'N' - no parity)
+ * \param stopBits the number of stop buts (usually 1)
+ *
+ * \return the new SerialPort instance
+ */
+PAL_API SerialPort
+SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, char parity, uint8_t stopBits);
+
+/**
+ * \brief Destroy the SerialPort instance and release all resources
+ */
+PAL_API void
+SerialPort_destroy(SerialPort self);
+
+/**
+ * \brief Open the serial interface
+ *
+ * \return true in case of success, false otherwise (use \ref SerialPort_getLastError for a detailed error code)
+ */
+PAL_API bool
+SerialPort_open(SerialPort self);
+
+/**
+ * \brief Close (release) the serial interface
+ */
+PAL_API void
+SerialPort_close(SerialPort self);
+
+/**
+ * \brief Get the baudrate used by the serial interface
+ *
+ * \return the baud rate in baud
+ */
+PAL_API int
+SerialPort_getBaudRate(SerialPort self);
+
+/**
+ * \brief Set the timeout used for message reception
+ *
+ * \param timeout the timeout value in ms.
+ */
+PAL_API void
+SerialPort_setTimeout(SerialPort self, int timeout);
+
+/**
+ * \brief Discard all data in the input buffer of the serial interface
+ */
+PAL_API void
+SerialPort_discardInBuffer(SerialPort self);
+
+/**
+ * \brief Read a byte from the interface
+ *
+ * \return number of read bytes of -1 in case of an error
+ */
+PAL_API int
+SerialPort_readByte(SerialPort self);
+
+/**
+ * \brief Write the number of bytes from the buffer to the serial interface
+ *
+ * \param buffer the buffer containing the data to write
+ * \param startPos start position in the buffer of the data to write
+ * \param numberOfBytes number of bytes to write
+ *
+ * \return number of bytes written, or -1 in case of an error
+ */
+PAL_API int
+SerialPort_write(SerialPort self, uint8_t* buffer, int startPos, int numberOfBytes);
+
+/**
+ * \brief Get the error code of the last operation
+ */
+PAL_API SerialPortError
+SerialPort_getLastError(SerialPort self);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* SRC_IEC60870_LINK_LAYER_SERIAL_PORT_H_ */

+ 358 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/hal_socket.h

@@ -0,0 +1,358 @@
+/*
+ *  socket_hal.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef SOCKET_HAL_H_
+#define SOCKET_HAL_H_
+
+#include "hal_base.h"
+
+/**
+ * \file hal_socket.h
+ * \brief Abstraction layer TCP/IP sockets
+ * Has to be implemented for CS 104 TCP/IP.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \defgroup hal Platform (Hardware/OS) abstraction layer
+   *
+   *  Platform abstraction layer. These functions have to be implemented when the library is
+   *  to be ported to new platforms. It might not be required to implement all interfaces
+   *  depending on the required library features.
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_SOCKET Interface to the TCP/IP stack (abstract socket layer)
+ *
+ *  Thread and Socket abstraction layer. This functions have to be implemented to
+ *  port lib60870 to a new hardware/OS platform when TCP/IP is required.
+ *
+ * @{
+ */
+
+/** Opaque reference for a server socket instance */
+typedef struct sServerSocket* ServerSocket;
+
+typedef struct sUdpSocket* UdpSocket;
+
+/** Opaque reference for a client or connection socket instance */
+typedef struct sSocket* Socket;
+
+/** Opaque reference for a set of server and socket handles */
+typedef struct sHandleSet* HandleSet;
+
+/** State of an asynchronous connect */
+typedef enum
+{
+    SOCKET_STATE_CONNECTING = 0,
+    SOCKET_STATE_FAILED = 1,
+    SOCKET_STATE_CONNECTED = 2
+} SocketState;
+
+
+/**
+ * \brief Create a new connection handle set (HandleSet)
+ *
+ * \return new HandleSet instance
+ */
+PAL_API HandleSet
+Handleset_new(void);
+
+/**
+ * \brief Reset the handle set for reuse
+ */
+PAL_API void
+Handleset_reset(HandleSet self);
+
+/**
+ * \brief add a socket to an existing handle set
+ *
+ * \param self the HandleSet instance
+ * \param sock the socket to add
+ */
+PAL_API void
+Handleset_addSocket(HandleSet self, const Socket sock);
+
+/**
+ * \brief remove a socket from an existing handle set
+ */
+void
+Handleset_removeSocket(HandleSet self, const Socket sock);
+
+/**
+ * \brief wait for a socket to become ready
+ *
+ * This function is corresponding to the BSD socket select function.
+ * It returns the number of sockets on which data is pending or 0 if no data is pending
+ * on any of the monitored connections. The function will return after "timeout" ms if no
+ * data is pending.
+ * The function shall return -1 if a socket error occures.
+ *
+ * \param self the HandleSet instance
+ * \param timeout in milliseconds (ms)
+ * \return It returns the number of sockets on which data is pending
+ *   or 0 if no data is pending on any of the monitored connections.
+ *   The function shall return -1 if a socket error occures.
+ */
+PAL_API int
+Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
+
+/**
+ * \brief destroy the HandleSet instance
+ *
+ * \param self the HandleSet instance to destroy
+ */
+PAL_API void
+Handleset_destroy(HandleSet self);
+
+/**
+ * \brief Create a new TcpServerSocket instance
+ *
+ * Implementation of this function is MANDATORY if server functionality is required.
+ *
+ * \param address ip address or hostname to listen on
+ * \param port the TCP port to listen on
+ *
+ * \return the newly create TcpServerSocket instance
+ */
+PAL_API ServerSocket
+TcpServerSocket_create(const char* address, int port);
+
+PAL_API UdpSocket
+UdpSocket_create(void);
+
+PAL_API bool
+UdpSocket_bind(UdpSocket self, const char* address, int port);
+
+PAL_API bool
+UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize);
+
+/**
+ * \brief Receive data from UDP socket (store data and (optionally) the IP address of the sender
+ *
+ * \param self UDP socket instance
+ * \param address (optional) buffer to store the IP address as string
+ * \param maxAddrSize (optional) size of the provided buffer to store the IP address
+ * \param msg buffer to store the UDP message data
+ * \param msgSize the maximum size of the message to receive
+ *
+ * \return number of received bytes or -1 in case of an error
+ */
+PAL_API int
+UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize);
+
+
+PAL_API void
+ServerSocket_listen(ServerSocket self);
+
+/**
+ * \brief accept a new incoming connection (non-blocking)
+ *
+ * This function shall accept a new incoming connection. It is non-blocking and has to
+ * return NULL if no new connection is pending.
+ *
+ * Implementation of this function is MANDATORY if server functionality is required.
+ *
+ * NOTE: The behaviour of this function changed with version 0.8!
+ *
+ * \param self server socket instance
+ *
+ * \return handle of the new connection socket or NULL if no new connection is available
+ */
+PAL_API Socket
+ServerSocket_accept(ServerSocket self);
+
+/**
+ * \brief active TCP keep alive for socket and set keep alive parameters
+ *
+ * NOTE: implementation is mandatory for IEC 61850 MMS
+ *
+ * \param self server socket instance
+ * \param idleTime time (in s) between last received message and first keep alive message
+ * \param interval time (in s) between subsequent keep alive messages if no ACK received
+ * \param count number of not missing keep alive ACKs until socket is considered dead
+ */
+PAL_API void
+Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count);
+
+/**
+ * \brief set the maximum number of pending connections in the queue
+ *
+ * Implementation of this function is OPTIONAL.
+ *
+ * \param self the server socket instance
+ * \param backlog the number of pending connections in the queue
+ *
+ */
+PAL_API void
+ServerSocket_setBacklog(ServerSocket self, int backlog);
+
+/**
+ * \brief destroy a server socket instance
+ *
+ * Free all resources allocated by this server socket instance.
+ *
+ * Implementation of this function is MANDATORY if server functionality is required.
+ *
+ * \param self server socket instance
+ */
+PAL_API void
+ServerSocket_destroy(ServerSocket self);
+
+/**
+ * \brief create a TCP client socket
+ *
+ * Implementation of this function is MANDATORY if client functionality is required.
+ *
+ * \return a new client socket instance.
+ */
+PAL_API Socket
+TcpSocket_create(void);
+
+/**
+ * \brief set the timeout to establish a new connection
+ *
+ * \param self the client socket instance
+ * \param timeoutInMs the timeout in ms
+ */
+PAL_API void
+Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs);
+
+/**
+ * \brief bind a socket to a particular IP address and port (for TcpSocket)
+ * 
+ * NOTE: Don't use the socket when this functions returns false!
+ * 
+ * \param self the client socket instance
+ * \param srcAddress the local IP address or hostname as C string
+ * \param srcPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
+ * 
+ * \return true in case of success, false otherwise
+ */ 
+PAL_API bool
+Socket_bind(Socket self, const char* srcAddress, int srcPort);
+
+/**
+ * \brief connect to a server
+ *
+ * Connect to a server application identified by the address and port parameter.
+ *
+ * The "address" parameter may either be a hostname or an IP address. The IP address
+ * has to be provided as a C string (e.g. "10.0.2.1").
+ *
+ * Implementation of this function is MANDATORY if client functionality is required.
+ *
+ * NOTE: return type changed from int to bool with version 0.8
+ *
+ * \param self the client socket instance
+ * \param address the IP address or hostname as C string
+ * \param port the TCP port of the application to connect to
+ *
+ * \return true if the connection was established successfully, false otherwise
+ */
+PAL_API bool
+Socket_connect(Socket self, const char* address, int port);
+
+PAL_API bool
+Socket_connectAsync(Socket self, const char* address, int port);
+
+PAL_API SocketState
+Socket_checkAsyncConnectState(Socket self);
+
+/**
+ * \brief read from socket to local buffer (non-blocking)
+ *
+ * The function shall return immediately if no data is available. In this case
+ * the function returns 0. If an error happens the function shall return -1.
+ *
+ * Implementation of this function is MANDATORY
+ *
+ * NOTE: The behaviour of this function changed with version 0.8!
+ *
+ * \param self the client, connection or server socket instance
+ * \param buf the buffer where the read bytes are copied to
+ * \param size the maximum number of bytes to read (size of the provided buffer)
+ *
+ * \return the number of bytes read or -1 if an error occurred
+ */
+PAL_API int
+Socket_read(Socket self, uint8_t* buf, int size);
+
+/**
+ * \brief send a message through the socket
+ *
+ * Implementation of this function is MANDATORY
+ *
+ * \param self client, connection or server socket instance
+ *
+ * \return number of bytes transmitted of -1 in case of an error
+ */
+PAL_API int
+Socket_write(Socket self, uint8_t* buf, int size);
+
+PAL_API char*
+Socket_getLocalAddress(Socket self);
+
+/**
+ * \brief Get the address of the peer application (IP address and port number)
+ *
+ * The peer address has to be returned as null terminated string
+ *
+ * Implementation of this function is MANDATORY (libiec61850)
+ *
+ * \param self the client, connection or server socket instance
+ *
+ * \return the IP address and port number as strings separated by the ':' character.
+ */
+PAL_API char*
+Socket_getPeerAddress(Socket self);
+
+/**
+ * \brief Get the address of the peer application (IP address and port number)
+ *
+ * The peer address has to be returned as null terminated string
+ *
+ * Implementation of this function is MANDATORY (lib60870 and libiec61850)
+ *
+ * \param self the client, connection or server socket instance
+ * \param peerAddressString a string to store the peer address (the string should have space
+ *        for at least 60 characters)
+ *
+ * \return the IP address and port number as strings separated by the ':' character. If the
+ *         address is an IPv6 address the IP part is encapsulated in square brackets.
+ */
+PAL_API char*
+Socket_getPeerAddressStatic(Socket self, char* peerAddressString);
+
+/**
+ * \brief destroy a socket (close the socket if a connection is established)
+ *
+ * This function shall close the connection (if one is established) and free all
+ * resources allocated by the socket.
+ *
+ * Implementation of this function is MANDATORY
+ *
+ * \param self the client, connection or server socket instance
+ */
+PAL_API void
+Socket_destroy(Socket self);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SOCKET_HAL_H_ */

+ 106 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/hal_thread.h

@@ -0,0 +1,106 @@
+/*
+ *  thread_hal.h
+ *
+ *  Multi-threading abstraction layer
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef THREAD_HAL_H_
+#define THREAD_HAL_H_
+
+#include "hal_base.h"
+#include "semphr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file hal_thread.h
+ * \brief Abstraction layer for threading and synchronization
+ */
+
+/*! \addtogroup hal
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_THREAD Threading and synchronization API
+ *
+ * @{
+ */
+
+/** Opaque reference of a Thread instance */
+typedef struct sThread* Thread;
+
+/** Qpaque reference of a Semaphore instance */
+typedef void* Semaphore;
+
+/** Reference to a function that is called when starting the thread */
+typedef void* (*ThreadExecutionFunction) (void*);
+
+/**
+ * \brief Create a new Thread instance
+ *
+ * \param function the entry point of the thread
+ * \param parameter a parameter that is passed to the threads start function
+ * \param autodestroy the thread is automatically destroyed if the ThreadExecutionFunction has finished.
+ *
+ * \return the newly created Thread instance
+ */
+PAL_API Thread
+Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy);
+
+/**
+ * \brief Start a Thread.
+ *
+ * This function invokes the start function of the thread. The thread terminates when
+ * the start function returns.
+ *
+ * \param thread the Thread instance to start
+ */
+PAL_API void
+Thread_start(Thread thread);
+
+/**
+ * \brief Destroy a Thread and free all related resources.
+ *
+ * \param thread the Thread instance to destroy
+ */
+PAL_API void
+Thread_destroy(Thread thread);
+
+/**
+ * \brief Suspend execution of the Thread for the specified number of milliseconds
+ */
+PAL_API void
+Thread_sleep(int millies);
+
+PAL_API SemaphoreHandle_t
+Semaphore_create(void);
+
+/* Wait until semaphore value is greater than zero. Then decrease the semaphore value. */
+PAL_API void
+Semaphore_wait(Semaphore self);
+
+PAL_API void
+Semaphore_post(Semaphore self);
+
+PAL_API void
+Semaphore_destroy(Semaphore self);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* THREAD_HAL_H_ */

+ 80 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/hal_time.h

@@ -0,0 +1,80 @@
+/*
+ *  time.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef HAL_C_
+#define HAL_C_
+
+#include "hal_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file hal_time.h
+ * \brief Abstraction layer for system time access
+ */
+
+/*! \addtogroup hal
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_TIME Time related functions
+ *
+ * @{
+ */
+
+typedef uint64_t nsSinceEpoch;
+typedef uint64_t msSinceEpoch;
+
+/**
+ * Get the system time in milliseconds.
+ *
+ * The time value returned as 64-bit unsigned integer should represent the milliseconds
+ * since the UNIX epoch (1970/01/01 00:00 UTC).
+ *
+ * \return the system time with millisecond resolution.
+ */
+PAL_API msSinceEpoch
+Hal_getTimeInMs(void);
+
+/**
+ * Get the system time in nanoseconds.
+ *
+ * The time value returned as 64-bit unsigned integer should represent the nanoseconds
+ * since the UNIX epoch (1970/01/01 00:00 UTC).
+ *
+ * \return the system time with nanosecond resolution.
+ */
+PAL_API nsSinceEpoch
+Hal_getTimeInNs(void);
+
+/**
+* Set the system time from ns time
+*
+* The time value returned as 64-bit unsigned integer should represent the nanoseconds
+* since the UNIX epoch (1970/01/01 00:00 UTC).
+*
+* \return true on success, otherwise false
+*/
+PAL_API bool
+Hal_setTimeInNs(nsSinceEpoch nsTime);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* HAL_C_ */

+ 53 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/lib_memory.h

@@ -0,0 +1,53 @@
+/*
+ *  lib_memory.h
+ *
+ *  Copyright 2014-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef MEMORY_H_
+#define MEMORY_H_
+
+#include "hal_base.h"
+
+#define CALLOC(nmemb, size) Memory_calloc(nmemb, size)
+#define MALLOC(size)        Memory_malloc(size)
+#define REALLOC(oldptr, size)   Memory_realloc(oldptr, size)
+#define FREEMEM(ptr)        Memory_free(ptr)
+
+#define GLOBAL_CALLOC(nmemb, size) Memory_calloc(nmemb, size)
+#define GLOBAL_MALLOC(size)        Memory_malloc(size)
+#define GLOBAL_REALLOC(oldptr, size)   Memory_realloc(oldptr, size)
+#define GLOBAL_FREEMEM(ptr)        Memory_free(ptr)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+typedef void
+(*MemoryExceptionHandler) (void* parameter);
+
+PAL_API void
+Memory_installExceptionHandler(MemoryExceptionHandler handler, void* parameter);
+
+PAL_API void*
+Memory_malloc(size_t size);
+
+PAL_API void*
+Memory_calloc(size_t nmemb, size_t size);
+
+PAL_API void *
+Memory_realloc(void *ptr, size_t size);
+
+PAL_API void
+Memory_free(void* memb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MEMORY_H_ */

+ 37 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/platform_endian.h

@@ -0,0 +1,37 @@
+/*
+ *  platform_endian.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef ENDIAN_H_
+#define ENDIAN_H_
+
+#ifndef PLATFORM_IS_BIGENDIAN
+#ifdef __GNUC__
+#ifdef __BYTE_ORDER__
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define PLATFORM_IS_BIGENDIAN 1
+#endif
+
+#else
+
+/* older versions of GCC have __BYTE_ORDER__ not defined! */
+#ifdef __PPC__
+#define PLATFORM_IS_BIGENDIAN 1
+#endif
+
+#endif /* __BYTE_ORDER__ */
+#endif /* __GNUC__ */
+#endif
+
+#if (PLATFORM_IS_BIGENDIAN == 1)
+#  define ORDER_LITTLE_ENDIAN 0
+#else
+#  define ORDER_LITTLE_ENDIAN 1
+#endif
+
+#endif /* ENDIAN_H_ */

+ 322 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/tls_config.h

@@ -0,0 +1,322 @@
+/*
+ * tls_config.h
+ *
+ * TLS Configuration API for protocol stacks using TCP/IP
+ *
+ * Copyright 2017-2022 Michael Zillgith
+ *
+ * Abstraction layer for configuration of different TLS implementations
+ *
+ */
+
+#ifndef SRC_TLS_CONFIG_H_
+#define SRC_TLS_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hal_base.h"
+
+/**
+ * \file tls_config.h
+ * \brief TLS API functions
+ */
+
+/*! \addtogroup hal Platform (Hardware/OS) abstraction layer
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup TLS_CONFIG_API TLS configuration
+ *
+ * @{
+ */
+
+typedef struct sTLSConfiguration* TLSConfiguration;
+
+/**
+ * \brief Create a new \ref TLSConfiguration object to represent TLS configuration and certificates
+ *
+ * WARNING: Configuration cannot be changed after using for the first time.
+ *
+ * \return the new TLS configuration
+ */
+PAL_API TLSConfiguration
+TLSConfiguration_create(void);
+
+/* will be called by stack automatically when appropriate */
+PAL_API void
+TLSConfiguration_setClientMode(TLSConfiguration self);
+
+typedef enum {
+   TLS_VERSION_NOT_SELECTED = 0,
+   TLS_VERSION_SSL_3_0 = 3,
+   TLS_VERSION_TLS_1_0 = 4,
+   TLS_VERSION_TLS_1_1 = 5,
+   TLS_VERSION_TLS_1_2 = 6,
+   TLS_VERSION_TLS_1_3 = 7
+} TLSConfigVersion;
+
+/**
+ * \brief Convert TLS version number to string
+ * 
+ * \param version TLS version number
+ * 
+ * \return the TLS version as null terminated string 
+ */
+PAL_API const char*
+TLSConfigVersion_toString(TLSConfigVersion version);
+
+typedef enum {
+    TLS_SEC_EVT_INFO = 0,
+    TLS_SEC_EVT_WARNING = 1,
+    TLS_SEC_EVT_INCIDENT = 2
+} TLSEventLevel;
+
+#define TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1
+#define TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION 2
+#define TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE 3
+#define TLS_EVENT_CODE_ALM_BAD_CERT 4
+#define TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED 5
+#define TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED 6
+#define TLS_EVENT_CODE_ALM_CERT_REQUIRED 7
+#define TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON 8
+#define TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION 9
+#define TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION 10
+#define TLS_EVENT_CODE_ALM_CERT_EXPIRED 11
+#define TLS_EVENT_CODE_ALM_CERT_REVOKED 12
+#define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13
+#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 14
+#define TLS_EVENT_CODE_ALM_NO_CIPHER 15
+
+typedef struct sTLSConnection* TLSConnection;
+
+/**
+ * \brief Get the peer address of the TLS connection
+ * 
+ * \param self the TLS connection instance
+ * \param peerAddrBuf user provided buffer that can hold at least 60 characters, or NULL to allow the function to allocate the memory for the buffer
+ * 
+ * \returns peer address:port as null terminated string 
+ */
+PAL_API char*
+TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf);
+
+/**
+ * \brief Get the TLS certificate used by the peer
+ * 
+ * \param self the TLS connection instance
+ * \param certSize[OUT] the certificate size in bytes
+ * 
+ * \return address of the certificate buffer 
+ */
+PAL_API uint8_t*
+TLSConnection_getPeerCertificate(TLSConnection self, int* certSize);
+
+/**
+ * \brief Get the TLS version used by the connection
+ * 
+ * \param self the TLS connection instance
+ * 
+ * \return TLS version
+ */
+PAL_API TLSConfigVersion
+TLSConnection_getTLSVersion(TLSConnection self);
+
+typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* message, TLSConnection con);
+
+/**
+ * \brief Set the security event handler
+ * 
+ * \param handler the security event callback handler
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+PAL_API void
+TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter);
+
+/**
+ * \brief enable or disable TLS session resumption (default: enabled)
+ * 
+ * NOTE: Depending on the used TLS version this is implemented by
+ * session IDs or by session tickets.
+ * 
+ * \param enable true to enable session resumption, false otherwise
+ */
+PAL_API void
+TLSConfiguration_enableSessionResumption(TLSConfiguration self, bool enable);
+
+/**
+ * \brief Set the maximum life time of a cached TLS session for session resumption in seconds
+ *
+ * \param intervalInSeconds the maximum lifetime of a cached TLS session
+ */
+PAL_API void
+TLSConfiguration_setSessionResumptionInterval(TLSConfiguration self, int intervalInSeconds);
+
+/**
+ * \brief Enables the validation of the certificate trust chain (enabled by default)
+ *
+ * \param value true to enable chain validation, false to disable
+ */
+PAL_API void
+TLSConfiguration_setChainValidation(TLSConfiguration self, bool value);
+
+/**
+ * \brief Set if only known certificates are accepted.
+ *
+ * If set to true only known certificates are accepted. Connections with unknown certificates
+ * are rejected even if they are signed by a trusted authority.
+ *
+ * \param value true to enable setting, false otherwise
+ */
+PAL_API void
+TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value);
+
+/**
+ * \brief Set own certificate (identity) from a byte buffer
+ *
+ * \param certificate the certificate buffer
+ * \param certLen the lenght of the certificate
+ *
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
+
+/**
+ * \brief Set own certificate (identity) from a certificate file
+ *
+ * \param filename of the certificate file
+ *
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Set the own private key from a byte buffer
+ *
+ * \param key the private key to use
+ * \param keyLen the length of the key
+ * \param password the password of the key or null if the key is not password protected
+ *
+ * \return true, when the key was set, false otherwise (e.g. unknown key format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword);
+
+/**
+ * \brief Set the own private key from a key file
+ *
+ * \param filename filename/path of the key file
+ * \param password the password of the key or null if the key is not password protected
+ *
+ * \return true, when the key was set, false otherwise (e.g. unknown key format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword);
+
+/**
+ * Add a certificate to the list of allowed peer certificates from a byte buffer
+ *
+ * \param certificate the certificate buffer
+ * \param certLen the length of the certificate buffer
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
+
+/**
+ * \brief Add a certificate to the list of allowed peer certificates
+ *
+ * \param filename filename of the certificate file
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Add a CA certificate used to validate peer certificates from a byte buffer
+ *
+ * \param certificate the certificate buffer
+ * \param certLen the length of the certificate buffer
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
+
+/**
+ * \brief Add a CA certificate used to validate peer certificates from a file
+ *
+ * \param filename filename of the certificate file
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Set the renegotiation timeout.
+ *
+ * After the timeout elapsed a TLS session renegotiation has to occur.
+ *
+ * \param timeInMs session renegotiation timeout in milliseconds
+ */
+PAL_API void
+TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs);
+
+/**
+ * \brief Set minimal allowed TLS version to use
+ */
+PAL_API void
+TLSConfiguration_setMinTlsVersion(TLSConfiguration self, TLSConfigVersion version);
+
+/**
+ * \brief Set maximal allowed TLS version to use
+ */
+PAL_API void
+TLSConfiguration_setMaxTlsVersion(TLSConfiguration self, TLSConfigVersion version);
+
+/**
+ * \brief Add a CRL (certificate revocation list) from buffer
+ *
+ * \param crl the buffer containing the CRL
+ * \param crlLen the length of the CRL buffer
+ * \return true, when the CRL was imported, false otherwise (e.g. unknown format)
+ */
+PAL_API bool
+TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen);
+
+/**
+ * \brief Add a CRL (certificate revocation list) from a file
+ *
+ * \param filename filename of the CRL file
+ * \return true, when the CRL was imported, false otherwise (e.g. unknown format)
+ */
+PAL_API bool
+TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Removes any CRL (certificate revocation list) currently in use
+ */
+PAL_API void
+TLSConfiguration_resetCRL(TLSConfiguration self);
+
+/**
+ * Release all resource allocated by the TLSConfiguration instance
+ *
+ * NOTE: Do not use the object after calling this function!
+ */
+PAL_API void
+TLSConfiguration_destroy(TLSConfiguration self);
+
+/** @} */
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_TLS_CONFIG_H_ */

+ 122 - 0
app/IEC_SERVER/lib60870-C/src/hal/inc/tls_socket.h

@@ -0,0 +1,122 @@
+/*
+ * tls_socket.h
+ *
+ * TLS socket API for protocol libraries using TCP/IP
+ *
+ * Copyright 2017-2021 Michael Zillgith, MZ Automation GmbH
+ *
+ * Abstraction layer for different TLS implementations
+ *
+ * The implementation has to connect the TLS API layer with the socket API layer
+ * and perform all TLS tasks like handshake, encryption/decryption.
+ *
+ */
+
+#ifndef SRC_TLS_SOCKET_API_H_
+#define SRC_TLS_SOCKET_API_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file tls_socket.h
+ * \brief Abstraction layer for different TLS implementations.
+ *
+ * The implementation has to connect the TLS API layer with the socket API layer
+ * and perform all TLS tasks like handshake, encryption/decryption.
+ */
+
+/*! \addtogroup hal Platform (Hardware/OS) abstraction layer
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_TLS_SOCKET Abstraction layer for different TLS implementations.
+ *
+ * The implementation has to connect the TLS API layer with the socket API layer
+ * and perform all TLS tasks like handshake, encryption/decryption.
+ *
+ * @{
+ */
+
+#include <stdint.h>
+#include "tls_config.h"
+#include "hal_socket.h"
+
+typedef struct sTLSSocket* TLSSocket;
+
+/**
+ * \brief This function create a new TLSSocket instance using the given Socket instance
+ *
+ * NOTE: This function also has to perform the TLS handshake
+ *
+ * \param socket the socket instance to use for the TLS connection
+ * \param configuration the TLS configuration object to use
+ * \param storeClientCert if true, the client certificate will be stored
+ *                        for later access by \ref TLSSocket_getPeerCertificate
+ *
+ * \return new TLS connection instance
+ */
+PAL_API TLSSocket
+TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert);
+
+/**
+ * \brief Perform a new TLS handshake/session renegotiation
+ */
+PAL_API bool
+TLSSocket_performHandshake(TLSSocket self);
+
+/**
+ * \brief Access the certificate used by the peer
+ *
+ * \param[out] certSize the size of the certificate in bytes
+ *
+ * \return the certificate byte buffer
+ */
+PAL_API uint8_t*
+TLSSocket_getPeerCertificate(TLSSocket self, int* certSize);
+
+/**
+ * \brief read from socket to local buffer (non-blocking)
+ *
+ * The function shall return immediately if no data is available. In this case
+ * the function returns 0. If an error happens the function shall return -1.
+ *
+ * \param self the client, connection or server socket instance
+ * \param buf the buffer where the read bytes are copied to
+ * \param size the maximum number of bytes to read (size of the provided buffer)
+ *
+ * \return the number of bytes read or -1 if an error occurred
+ */
+PAL_API int
+TLSSocket_read(TLSSocket self, uint8_t* buf, int size);
+
+/**
+ * \brief send a message through the socket
+ *
+ * Implementation of this function is MANDATORY
+ *
+ * \param self client, connection or server socket instance
+ *
+ * \return number of bytes transmitted of -1 in case of an error
+ */
+PAL_API int
+TLSSocket_write(TLSSocket self, uint8_t* buf, int size);
+
+/**
+ * \brief Closes the TLS connection and released all resources
+ */
+PAL_API void
+TLSSocket_close(TLSSocket self);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_TLS_SOCKET_API_H_ */

+ 70 - 0
app/IEC_SERVER/lib60870-C/src/hal/memory/lib_memory.c

@@ -0,0 +1,70 @@
+/*
+ *  lib_memory.c
+ *
+ *  Copyright 2014-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include <stdlib.h>
+#include "lib_memory.h"
+
+static MemoryExceptionHandler exceptionHandler = NULL;
+static void* exceptionHandlerParameter = NULL;
+
+static void
+noMemoryAvailableHandler(void)
+{
+    if (exceptionHandler != NULL)
+        exceptionHandler(exceptionHandlerParameter);
+}
+
+void
+Memory_installExceptionHandler(MemoryExceptionHandler handler, void* parameter)
+{
+    exceptionHandler = handler;
+    exceptionHandlerParameter = parameter;
+}
+
+void*
+Memory_malloc(size_t size)
+{
+    void* memory = malloc(size);
+
+    if (memory == NULL)
+        noMemoryAvailableHandler();
+
+    return memory;
+}
+
+
+void*
+Memory_calloc(size_t nmemb, size_t size)
+{
+    void* memory = calloc(nmemb, size);
+
+    if (memory == NULL)
+        noMemoryAvailableHandler();
+
+    return memory;
+}
+
+
+void *
+Memory_realloc(void *ptr, size_t size)
+{
+    void* memory = realloc(ptr, size);
+
+    if (memory == NULL)
+        noMemoryAvailableHandler();
+
+    return memory;
+}
+
+void
+Memory_free(void* memb)
+{
+    free(memb);
+}
+

+ 57 - 0
app/IEC_SERVER/lib60870-C/src/hal/serial/linux/serial_port_linux.c

@@ -0,0 +1,57 @@
+/*
+ *  serial_port_linux.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include "lib_memory.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "hal_serial.h"
+#include "hal_time.h"
+
+struct timeval
+{
+	long tv_sec;
+	long tv_usec;
+};
+
+struct sSerialPort {
+    char interfaceName[100];
+    int fd;
+    int baudRate;
+    uint8_t dataBits;
+    char parity;
+    uint8_t stopBits;
+    uint64_t lastSentTime;
+    struct timeval timeout;
+    SerialPortError lastError;
+};
+
+
+SerialPort
+SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, char parity, uint8_t stopBits)
+{
+    SerialPort self = (SerialPort) GLOBAL_MALLOC(sizeof(struct sSerialPort));
+
+    if (self != NULL) {
+        self->fd = -1;
+        self->baudRate = baudRate;
+        self->dataBits = dataBits;
+        self->stopBits = stopBits;
+        self->parity = parity;
+        self->lastSentTime = 0;
+        self->timeout.tv_sec = 0;
+        self->timeout.tv_usec = 100000; /* 100 ms */
+        strncpy(self->interfaceName, interfaceName, 99);
+        self->lastError = SERIAL_PORT_ERROR_NONE;
+    }
+
+    return self;
+}

+ 273 - 0
app/IEC_SERVER/lib60870-C/src/hal/serial/win32/serial_port_win32.c

@@ -0,0 +1,273 @@
+/*
+*  serial_port_win32.c
+*
+*  Copyright 2013-2021 Michael Zillgith
+*
+*  This file is part of Platform Abstraction Layer (libpal)
+*  for libiec61850, libmms, and lib60870.
+*/
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#define _CRT_NONSTDC_NO_DEPRECATE
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <windows.h>
+
+#include "lib_memory.h"
+
+#include "hal_serial.h"
+#include "hal_time.h"
+
+struct sSerialPort {
+	char interfaceName[100];
+	HANDLE comPort;
+	int baudRate;
+	uint8_t dataBits;
+	char parity;
+	uint8_t stopBits;
+	uint64_t lastSentTime;
+	int timeout;
+	SerialPortError lastError;
+};
+
+SerialPort
+SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, char parity, uint8_t stopBits)
+{
+	SerialPort self = (SerialPort)GLOBAL_MALLOC(sizeof(struct sSerialPort));
+
+	if (self != NULL) {
+		self->comPort = NULL;
+		self->baudRate = baudRate;
+		self->dataBits = dataBits;
+		self->stopBits = stopBits;
+		self->parity = parity;
+		self->lastSentTime = 0;
+		self->timeout = 100; /* 100 ms */
+		strncpy(self->interfaceName, interfaceName, 100);
+		self->lastError = SERIAL_PORT_ERROR_NONE;
+	}
+
+	return self;
+}
+
+void
+SerialPort_destroy(SerialPort self)
+{
+	if (self != NULL) {
+
+		if (self->comPort != INVALID_HANDLE_VALUE)
+			SerialPort_close(self);
+
+		GLOBAL_FREEMEM(self);
+	}
+}
+
+bool
+SerialPort_open(SerialPort self)
+{
+    COMMTIMEOUTS timeouts = { 0 };
+
+	self->comPort = CreateFile(self->interfaceName, GENERIC_READ | GENERIC_WRITE,
+		0, NULL, OPEN_EXISTING, 0, NULL);
+
+	if (self->comPort == INVALID_HANDLE_VALUE) {
+		self->lastError = SERIAL_PORT_ERROR_OPEN_FAILED;
+		return false;
+	}
+
+	DCB _serialParams = { 0 };
+	_serialParams.DCBlength = sizeof(DCB);
+
+	LPDCB serialParams = &_serialParams;
+
+	BOOL status = GetCommState(self->comPort, serialParams);
+
+	if (status == false) {
+		self->lastError = SERIAL_PORT_ERROR_UNKNOWN;
+		goto exit_error;
+	}
+
+	/* set baud rate */
+	switch (self->baudRate) {
+	case 110:
+		serialParams->BaudRate = CBR_110;
+		break;
+	case 300:
+		serialParams->BaudRate = CBR_300;
+		break;
+	case 600:
+		serialParams->BaudRate = CBR_600;
+		break;
+	case 1200:
+		serialParams->BaudRate = CBR_1200;
+		break;
+	case 2400:
+		serialParams->BaudRate = CBR_2400;
+		break;
+	case 4800:
+		serialParams->BaudRate = CBR_4800;
+		break;
+	case 9600:
+		serialParams->BaudRate = CBR_9600;
+		break;
+	case 19200:
+		serialParams->BaudRate = CBR_19200;
+		break;
+	case 38400:
+		serialParams->BaudRate = CBR_38400;
+		break;
+	case 57600:
+		serialParams->BaudRate = CBR_57600;
+		break;
+	case 115200:
+		serialParams->BaudRate = CBR_115200;
+		break;
+	default:
+		serialParams->BaudRate = CBR_9600;
+		self->lastError = SERIAL_PORT_ERROR_INVALID_BAUDRATE;
+	}
+
+	/* Set data bits (5/6/7/8) */
+	serialParams->ByteSize = self->dataBits;
+
+	/* Set stop bits (1/2) */
+	if (self->stopBits == 1)
+		serialParams->StopBits = ONESTOPBIT;
+	else /* 2 */
+		serialParams->StopBits = TWOSTOPBITS;
+
+	if (self->parity == 'N')
+		serialParams->Parity = NOPARITY;
+	else if (self->parity == 'E')
+		serialParams->Parity = EVENPARITY;
+	else /* 'O' */
+		serialParams->Parity = ODDPARITY;
+
+	status = SetCommState(self->comPort, serialParams);
+
+	if (status == false) {
+		self->lastError = SERIAL_PORT_ERROR_INVALID_ARGUMENT;
+		goto exit_error;
+	}
+
+	timeouts.ReadIntervalTimeout = 100;
+	timeouts.ReadTotalTimeoutConstant = 50;
+	timeouts.ReadTotalTimeoutMultiplier = 10;
+	timeouts.WriteTotalTimeoutConstant = 100;
+	timeouts.WriteTotalTimeoutMultiplier = 10;
+
+	status = SetCommTimeouts(self->comPort, &timeouts);
+
+	if (status == false) {
+		self->lastError = SERIAL_PORT_ERROR_UNKNOWN;
+		goto exit_error;
+	}
+
+	status = SetCommMask(self->comPort, EV_RXCHAR);
+
+	if (status == false) {
+		printf("SetCommMask failed!\n");
+		self->lastError = SERIAL_PORT_ERROR_UNKNOWN;
+		goto exit_error;
+	}
+
+	self->lastError = SERIAL_PORT_ERROR_NONE;
+
+	return true;
+
+exit_error:
+
+	if (self->comPort != INVALID_HANDLE_VALUE) {
+		CloseHandle(self->comPort);
+		self->comPort = INVALID_HANDLE_VALUE;
+	}
+
+	return false;
+}
+
+
+void
+SerialPort_close(SerialPort self)
+{
+	if (self->comPort != INVALID_HANDLE_VALUE) {
+		CloseHandle(self->comPort);
+		self->comPort = INVALID_HANDLE_VALUE;
+	}
+}
+
+int
+SerialPort_getBaudRate(SerialPort self)
+{
+	return self->baudRate;
+}
+
+void
+SerialPort_discardInBuffer(SerialPort self)
+{
+	PurgeComm(self->comPort, PURGE_RXCLEAR | PURGE_TXCLEAR);
+}
+
+void
+SerialPort_setTimeout(SerialPort self, int timeout)
+{
+	self->timeout = timeout;
+}
+
+SerialPortError
+SerialPort_getLastError(SerialPort self)
+{
+	return self->lastError;
+}
+
+int
+SerialPort_readByte(SerialPort self)
+{
+	uint8_t buf[1];
+
+	DWORD bytesRead = 0;
+
+	BOOL status = ReadFile(self->comPort, buf, 1, &bytesRead, NULL);
+
+	if (status == false) {
+		self->lastError = SERIAL_PORT_ERROR_UNKNOWN;
+		return -1;
+	}
+
+	self->lastError = SERIAL_PORT_ERROR_NONE;
+
+	if (bytesRead == 0)
+		return -1;
+	else
+		return (int) buf[0];
+}
+
+int
+SerialPort_write(SerialPort self, uint8_t* buffer, int startPos, int bufSize)
+{
+	//TODO assure minimum line idle time
+
+    self->lastError = SERIAL_PORT_ERROR_NONE;
+
+	DWORD numberOfBytesWritten;
+
+	BOOL status = WriteFile(self->comPort, buffer + startPos, bufSize, &numberOfBytesWritten, NULL);
+
+	if (status == false) {
+	    self->lastError = SERIAL_PORT_ERROR_UNKNOWN;
+	    return -1;
+	}
+
+	status = FlushFileBuffers(self->comPort);
+
+	if (status == false) {
+		printf("FlushFileBuffers failed!\n");
+	}
+
+	self->lastSentTime = Hal_getTimeInMs();
+
+	return (int) numberOfBytesWritten;
+}

+ 647 - 0
app/IEC_SERVER/lib60870-C/src/hal/socket/bsd/socket_bsd.c

@@ -0,0 +1,647 @@
+/*
+ *  socket_bsd.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include "hal_socket.h"
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <netinet/tcp.h> // required for TCP keepalive
+
+#include <signal.h>
+#include <poll.h>
+
+#include "linked_list.h"
+#include "hal_thread.h"
+#include "lib_memory.h"
+
+#ifndef DEBUG_SOCKET
+#define DEBUG_SOCKET 0
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+struct sSocket {
+    int fd;
+    uint32_t connectTimeout;
+};
+
+struct sServerSocket {
+    int fd;
+    int backLog;
+};
+
+struct sHandleSet {
+    LinkedList sockets;
+    bool pollfdIsUpdated;
+    struct pollfd* fds;
+    int nfds;
+};
+
+HandleSet
+Handleset_new(void)
+{
+    HandleSet self = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet));
+
+    if (self) {
+        self->sockets = LinkedList_create();
+        self->pollfdIsUpdated = false;
+        self->fds = NULL;
+        self->nfds = 0;
+    }
+
+    return self;
+}
+
+void
+Handleset_reset(HandleSet self)
+{
+    if (self) {
+        if (self->sockets) {
+            LinkedList_destroyStatic(self->sockets);
+            self->sockets = LinkedList_create();
+            self->pollfdIsUpdated = false;
+        }
+    }
+}
+
+void
+Handleset_addSocket(HandleSet self, const Socket sock)
+{
+    if (self != NULL && sock != NULL && sock->fd != -1) {
+        LinkedList_add(self->sockets, sock);
+        self->pollfdIsUpdated = false;
+    }
+}
+
+void
+Handleset_removeSocket(HandleSet self, const Socket sock)
+{
+    if (self && self->sockets && sock) {
+        LinkedList_remove(self->sockets, sock);
+        self->pollfdIsUpdated = false;
+    }
+}
+
+int
+Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
+{
+    /* check if pollfd array is updated */
+    if (self->pollfdIsUpdated == false) {
+        if (self->fds) {
+            GLOBAL_FREEMEM(self->fds);
+            self->fds = NULL;
+        }
+
+        self->nfds = LinkedList_size(self->sockets);
+
+        self->fds = GLOBAL_CALLOC(self->nfds, sizeof(struct pollfd));
+
+        int i;
+
+        for (i = 0; i < self->nfds; i++) {
+            LinkedList sockElem = LinkedList_get(self->sockets, i);
+
+            if (sockElem) {
+                Socket sock = (Socket) LinkedList_getData(sockElem);
+
+                if (sock) {
+                    self->fds[i].fd = sock->fd;
+                    self->fds[i].events = POLL_IN;
+                }
+            }
+        }
+
+        self->pollfdIsUpdated = true;
+    }
+
+    if (self->fds && self->nfds > 0) {
+        int result = poll(self->fds, self->nfds, timeoutMs);
+
+        if (result == -1) {
+            if (DEBUG_SOCKET)
+                printf("SOCKET: poll error (errno: %i)\n", errno);
+        }
+
+        return result;
+    }
+    else {
+        /* there is no socket to wait for */
+        return 0;
+    }
+}
+
+void
+Handleset_destroy(HandleSet self)
+{
+    if (self) {
+        if (self->sockets)
+            LinkedList_destroyStatic(self->sockets);
+
+        if (self->fds)
+            GLOBAL_FREEMEM(self->fds);
+
+        GLOBAL_FREEMEM(self);
+    }
+}
+
+void
+Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count)
+{
+#if defined SO_KEEPALIVE
+    int optval;
+    socklen_t optlen = sizeof(optval);
+
+    optval = idleTime;
+    setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
+    optval = 1;
+    setsockopt(self->fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, optlen);
+
+#if defined TCP_KEEPCNT
+    optval = interval;
+    setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen);
+
+    optval = count;
+    setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen);
+#endif /* TCP_KEEPCNT */
+
+#endif /* SO_KEEPALIVE */
+}
+
+static bool
+prepareAddress(const char* address, int port, struct sockaddr_in* sockaddr)
+{
+
+	memset((char *) sockaddr , 0, sizeof(struct sockaddr_in));
+
+	if (address != NULL) {
+		struct hostent *server;
+		server = gethostbyname(address);
+
+		if (server == NULL) return false;
+
+		memcpy((char *) &sockaddr->sin_addr.s_addr, (char *) server->h_addr, server->h_length);
+	}
+	else
+		sockaddr->sin_addr.s_addr = htonl(INADDR_ANY);
+
+    sockaddr->sin_family = AF_INET;
+    sockaddr->sin_port = htons(port);
+
+    return true;
+}
+
+static void
+setSocketNonBlocking(Socket self)
+{
+    int flags = fcntl(self->fd, F_GETFL, 0);
+    fcntl(self->fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+static void
+activateTcpNoDelay(Socket self)
+{
+    /* activate TCP_NODELAY option - packets will be sent immediately */
+    int flag = 1;
+    setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
+}
+
+ServerSocket
+TcpServerSocket_create(const char* address, int port)
+{
+    ServerSocket serverSocket = NULL;
+
+    int fd;
+
+    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) {
+        struct sockaddr_in serverAddress;
+
+        if (!prepareAddress(address, port, &serverAddress)) {
+            close(fd);
+            return NULL;
+        }
+
+        int optionReuseAddr = 1;
+        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int));
+
+        if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) {
+            serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket));
+            serverSocket->fd = fd;
+            serverSocket->backLog = 2;
+
+            setSocketNonBlocking((Socket) serverSocket);
+        }
+        else {
+            close(fd);
+            return NULL ;
+        }
+    }
+
+    return serverSocket;
+}
+
+void
+ServerSocket_listen(ServerSocket self)
+{
+    listen(self->fd, self->backLog);
+}
+
+
+/* CHANGED TO MAKE NON-BLOCKING --> RETURNS NULL IF NO CONNECTION IS PENDING */
+Socket
+ServerSocket_accept(ServerSocket self)
+{
+    int fd;
+
+    Socket conSocket = NULL;
+
+    fd = accept(self->fd, NULL, NULL );
+
+    if (fd >= 0) {
+        conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket));
+
+        if (conSocket) {
+            conSocket->fd = fd;
+
+            setSocketNonBlocking(conSocket);
+
+            activateTcpNoDelay(conSocket);
+        }
+        else {
+            /* out of memory */
+            close(fd);
+
+            if (DEBUG_SOCKET)
+                printf("SOCKET: out of memory\n");
+        }
+    }
+
+    return conSocket;
+}
+
+void
+ServerSocket_setBacklog(ServerSocket self, int backlog)
+{
+    self->backLog = backlog;
+}
+
+static void
+closeAndShutdownSocket(int socketFd)
+{
+    if (socketFd != -1) {
+
+        if (DEBUG_SOCKET)
+            printf("socket_linux.c: call shutdown for %i!\n", socketFd);
+
+        /* shutdown is required to unblock read or accept in another thread! */
+        shutdown(socketFd, SHUT_RDWR);
+
+        close(socketFd);
+    }
+}
+
+void
+ServerSocket_destroy(ServerSocket self)
+{
+    int fd = self->fd;
+
+    self->fd = -1;
+
+    closeAndShutdownSocket(fd);
+
+    Thread_sleep(10);
+
+    GLOBAL_FREEMEM(self);
+}
+
+Socket
+TcpSocket_create()
+{
+    Socket self = (Socket)NULL;
+
+    int sock = socket(AF_INET, SOCK_STREAM, 0);
+
+    if (sock != -1) {
+        self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
+
+        if (self) {
+            self->fd = sock;
+            self->connectTimeout = 5000;
+
+#if 0
+            int tcpUserTimeout = 10000;
+            int result = setsockopt(sock, SOL_TCP,  TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
+#endif
+        }
+        else {
+            /* out of memory */
+            close(sock);
+
+            if (DEBUG_SOCKET)
+                printf("SOCKET: out of memory\n");
+        }
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to create socket (errno=%i)\n", errno);
+    }
+
+    return self;
+}
+
+void
+Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
+{
+    self->connectTimeout = timeoutInMs;
+}
+
+bool
+Socket_bind(Socket self, const char* srcAddress, int srcPort)
+{
+    struct sockaddr_in localAddress;
+
+    if (!prepareAddress(srcAddress, srcPort, &localAddress))
+        return false;
+
+    int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
+
+    if (result == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to bind TCP socket (errno=%i)\n", errno);
+
+        close(self->fd);
+        self->fd = -1;
+
+        return false;
+    }    
+
+    return true;
+}
+
+bool
+Socket_connectAsync(Socket self, const char* address, int port)
+{
+    struct sockaddr_in serverAddress;
+
+    if (DEBUG_SOCKET)
+        printf("SOCKET: connect: %s:%i\n", address, port);
+
+    if (!prepareAddress(address, port, &serverAddress))
+        return false;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    activateTcpNoDelay(self);
+
+    fcntl(self->fd, F_SETFL, O_NONBLOCK);
+
+    if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {
+
+        if (errno != EINPROGRESS) {
+            self->fd = -1;
+            return false;
+        }
+    }
+
+    return true; /* is connecting or already connected */
+}
+
+SocketState
+Socket_checkAsyncConnectState(Socket self)
+{
+    struct timeval timeout;
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    int selectVal = select(self->fd + 1, NULL, &fdSet , NULL, &timeout);
+
+    if (selectVal == 1) {
+
+        /* Check if connection is established */
+
+        int so_error;
+        socklen_t len = sizeof so_error;
+
+        if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) {
+
+            if (so_error == 0)
+                return SOCKET_STATE_CONNECTED;
+        }
+
+        return SOCKET_STATE_FAILED;
+    }
+    else if (selectVal == 0) {
+        return SOCKET_STATE_CONNECTING;
+    }
+    else {
+        return SOCKET_STATE_FAILED;
+    }
+}
+
+bool
+Socket_connect(Socket self, const char* address, int port)
+{
+    if (Socket_connectAsync(self, address, port) == false)
+        return false;
+
+    struct timeval timeout;
+    timeout.tv_sec = self->connectTimeout / 1000;
+    timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) {
+
+        /* Check if connection is established */
+
+        int so_error;
+        socklen_t len = sizeof so_error;
+
+        if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) {
+
+            if (so_error == 0)
+                return true;
+        }
+    }
+
+    close (self->fd);
+    self->fd = -1;
+
+    return false;
+}
+
+static char*
+convertAddressToStr(struct sockaddr_storage* addr)
+{
+    char addrString[INET6_ADDRSTRLEN + 7];
+    int port;
+
+    bool isIPv6;
+
+    if (addr->ss_family == AF_INET) {
+        struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) addr;
+        port = ntohs(ipv4Addr->sin_port);
+        inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN);
+        isIPv6 = false;
+    }
+    else if (addr->ss_family == AF_INET6) {
+        struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) addr;
+        port = ntohs(ipv6Addr->sin6_port);
+        inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN);
+        isIPv6 = true;
+    }
+    else
+        return NULL ;
+
+    char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9);
+
+    if (isIPv6)
+        sprintf(clientConnection, "[%s]:%i", addrString, port);
+    else
+        sprintf(clientConnection, "%s:%i", addrString, port);
+
+    return clientConnection;
+}
+
+char*
+Socket_getPeerAddress(Socket self)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+
+    if (getpeername(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) {
+        return convertAddressToStr(&addr);
+    }
+    else
+        return NULL;
+}
+
+char*
+Socket_getLocalAddress(Socket self)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+
+    if (getsockname(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) {
+        return convertAddressToStr(&addr);
+    }
+    else
+        return NULL;
+}
+
+char*
+Socket_getPeerAddressStatic(Socket self, char* peerAddressString)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+
+    getpeername(self->fd, (struct sockaddr*) &addr, &addrLen);
+
+    char addrString[INET6_ADDRSTRLEN + 7];
+    int port;
+
+    bool isIPv6;
+
+    if (addr.ss_family == AF_INET) {
+        struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr;
+        port = ntohs(ipv4Addr->sin_port);
+        inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN);
+        isIPv6 = false;
+    }
+    else if (addr.ss_family == AF_INET6) {
+        struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr;
+        port = ntohs(ipv6Addr->sin6_port);
+        inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN);
+        isIPv6 = true;
+    }
+    else
+        return NULL ;
+
+    if (isIPv6)
+        sprintf(peerAddressString, "[%s]:%i", addrString, port);
+    else
+        sprintf(peerAddressString, "%s:%i", addrString, port);
+
+    return peerAddressString;
+}
+
+int
+Socket_read(Socket self, uint8_t* buf, int size)
+{
+    if (self->fd == -1)
+        return -1;
+
+    int read_bytes = recv(self->fd, buf, size, MSG_DONTWAIT);
+
+    if (read_bytes == 0)
+        return -1;
+
+    if (read_bytes == -1) {
+        int error = errno;
+
+        switch (error) {
+
+            case EAGAIN:
+                return 0;
+            case EBADF:
+                return -1;
+
+            default:
+                return -1;
+        }
+    }
+
+    return read_bytes;
+}
+
+int
+Socket_write(Socket self, uint8_t* buf, int size)
+{
+    if (self->fd == -1)
+        return -1;
+
+    /* MSG_NOSIGNAL - prevent send to signal SIGPIPE when peer unexpectedly closed the socket */
+    int retVal = send(self->fd, buf, size, MSG_NOSIGNAL | MSG_DONTWAIT);
+
+    if ((retVal == -1) && (errno == EAGAIN))
+        return 0;
+    else
+        return retVal;
+}
+
+void
+Socket_destroy(Socket self)
+{
+    int fd = self->fd;
+
+    self->fd = -1;
+
+    closeAndShutdownSocket(fd);
+
+    Thread_sleep(10);
+
+    GLOBAL_FREEMEM(self);
+}

+ 862 - 0
app/IEC_SERVER/lib60870-C/src/hal/socket/linux/socket_linux.c

@@ -0,0 +1,862 @@
+/*
+ *  socket_linux.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include "hal_socket.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <netinet/tcp.h> /* required for TCP keepalive */
+#include <linux/version.h>
+
+#define _GNU_SOURCE
+#include <signal.h>
+#include <poll.h>
+
+
+#include "linked_list.h"
+#include "hal_thread.h"
+#include "lib_memory.h"
+
+#ifndef DEBUG_SOCKET
+#define DEBUG_SOCKET 0
+#endif
+
+struct sSocket {
+    int fd;
+    uint32_t connectTimeout;
+};
+
+struct sServerSocket {
+    int fd;
+    int backLog;
+};
+
+struct sUdpSocket {
+    int fd;
+};
+
+struct sHandleSet {
+    LinkedList sockets;
+    bool pollfdIsUpdated;
+    struct pollfd* fds;
+    int nfds;
+};
+
+HandleSet
+Handleset_new(void)
+{
+   HandleSet self = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet));
+
+   if (self) {
+       self->sockets = LinkedList_create();
+       self->pollfdIsUpdated = false;
+       self->fds = NULL;
+       self->nfds = 0;
+   }
+
+   return self;
+}
+
+void
+Handleset_reset(HandleSet self)
+{
+    if (self) {
+        if (self->sockets) {
+            LinkedList_destroyStatic(self->sockets);
+            self->sockets = LinkedList_create();
+            self->pollfdIsUpdated = false;
+        }
+    }
+}
+
+void
+Handleset_addSocket(HandleSet self, const Socket sock)
+{
+   if (self != NULL && sock != NULL && sock->fd != -1) {
+
+       LinkedList_add(self->sockets, sock);
+       self->pollfdIsUpdated = false;
+   }
+}
+
+void
+Handleset_removeSocket(HandleSet self, const Socket sock)
+{
+    if (self && self->sockets && sock) {
+        LinkedList_remove(self->sockets, sock);
+        self->pollfdIsUpdated = false;
+    }
+}
+
+int
+Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
+{
+    /* check if pollfd array is updated */
+    if (self->pollfdIsUpdated == false) {
+        if (self->fds) {
+            GLOBAL_FREEMEM(self->fds);
+            self->fds = NULL;
+        }
+
+        self->nfds = LinkedList_size(self->sockets);
+
+        self->fds = GLOBAL_CALLOC(self->nfds, sizeof(struct pollfd));
+
+        int i;
+
+        for (i = 0; i < self->nfds; i++) {
+            LinkedList sockElem = LinkedList_get(self->sockets, i);
+
+            if (sockElem) {
+                Socket sock = (Socket) LinkedList_getData(sockElem);
+
+                if (sock) {
+                    self->fds[i].fd = sock->fd;
+                    self->fds[i].events = POLL_IN;
+                }
+            }
+        }
+
+        self->pollfdIsUpdated = true;
+    }
+
+    if (self->fds && self->nfds > 0) {
+        int result = poll(self->fds, self->nfds, timeoutMs);
+
+        if (result == -1 && errno == EINTR) {
+            result = 0;
+        }
+
+        if (result == -1) {
+            if (DEBUG_SOCKET)
+                printf("SOCKET: poll error (errno: %i)\n", errno);
+        }
+
+        return result;
+    }
+    else {
+        /* there is no socket to wait for */
+        return 0;
+    }
+}
+
+void
+Handleset_destroy(HandleSet self)
+{
+    if (self) {
+        if (self->sockets)
+            LinkedList_destroyStatic(self->sockets);
+
+        if (self->fds)
+            GLOBAL_FREEMEM(self->fds);
+
+        GLOBAL_FREEMEM(self);
+    }
+}
+
+void
+Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count)
+{
+#if defined SO_KEEPALIVE
+    int optval;
+    socklen_t optlen = sizeof(optval);
+
+    optval = 1;
+
+    if (setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen)) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: Failed to enable TCP keepalive\n");
+    }
+
+#if defined TCP_KEEPCNT
+    optval = idleTime;
+    if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen)) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: Failed to set TCP keepalive TCP_KEEPIDLE parameter\n");
+    }
+
+    optval = interval;
+    if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen)) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: Failed to set TCP keepalive TCP_KEEPINTVL parameter\n");
+    }
+
+    optval = count;
+    if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen)) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: Failed to set TCP keepalive TCP_KEEPCNT parameter\n");
+    }
+#endif /* TCP_KEEPCNT */
+
+#endif /* SO_KEEPALIVE */
+}
+
+static bool
+prepareAddress(const char* address, int port, struct sockaddr_in* sockaddr)
+{
+    bool retVal = true;
+
+    memset((char *) sockaddr, 0, sizeof(struct sockaddr_in));
+
+    if (address != NULL) {
+        struct addrinfo addressHints;
+        struct addrinfo *lookupResult;
+        int result;
+
+        memset(&addressHints, 0, sizeof(struct addrinfo));
+        addressHints.ai_family = AF_INET;
+        result = getaddrinfo(address, NULL, &addressHints, &lookupResult);
+
+        if (result != 0) {
+
+            if (DEBUG_SOCKET)
+                printf("SOCKET: getaddrinfo failed (code=%i)\n", result);
+
+            retVal = false;
+            goto exit_function;
+        }
+
+        memcpy(sockaddr, lookupResult->ai_addr, sizeof(struct sockaddr_in));
+        freeaddrinfo(lookupResult);
+    }
+    else
+        sockaddr->sin_addr.s_addr = htonl(INADDR_ANY);
+
+    sockaddr->sin_family = AF_INET;
+
+    if (port < 0)
+        port = 0;
+
+    sockaddr->sin_port = htons(port);
+
+exit_function:
+    return retVal;
+}
+
+static void
+setSocketNonBlocking(Socket self)
+{
+    int flags = fcntl(self->fd, F_GETFL, 0);
+    fcntl(self->fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+static void
+activateTcpNoDelay(Socket self)
+{
+    /* activate TCP_NODELAY option - packets will be sent immediately */
+    int flag = 1;
+    setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
+}
+
+ServerSocket
+TcpServerSocket_create(const char* address, int port)
+{
+    ServerSocket serverSocket = NULL;
+
+    int fd;
+
+    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) {
+        struct sockaddr_in serverAddress;
+
+        if (!prepareAddress(address, port, &serverAddress)) {
+            close(fd);
+            return NULL;
+        }
+
+        int optionReuseAddr = 1;
+        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+        int tcpUserTimeout = 10000;
+        int result = setsockopt(fd, SOL_TCP,  TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
+
+        if (result < 0) {
+            if (DEBUG_SOCKET)
+                printf("SOCKET: failed to set TCP_USER_TIMEOUT\n");
+        }
+#else
+#warning "TCP_USER_TIMEOUT not supported by linux kernel"
+#endif
+
+        if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) {
+            serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket));
+            serverSocket->fd = fd;
+            serverSocket->backLog = 2;
+
+            setSocketNonBlocking((Socket) serverSocket);
+        }
+        else {
+            close(fd);
+            return NULL ;
+        }
+    }
+
+    return serverSocket;
+}
+
+void
+ServerSocket_listen(ServerSocket self)
+{
+    if (listen(self->fd, self->backLog) == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: listen failed (errno: %i)\n", errno);
+    }
+}
+
+/* CHANGED TO MAKE NON-BLOCKING --> RETURNS NULL IF NO CONNECTION IS PENDING */
+Socket
+ServerSocket_accept(ServerSocket self)
+{
+    int fd;
+
+    Socket conSocket = NULL;
+
+    fd = accept(self->fd, NULL, NULL );
+
+    if (fd >= 0) {
+        conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket));
+
+        if (conSocket) {
+            conSocket->fd = fd;
+
+            setSocketNonBlocking(conSocket);
+
+            activateTcpNoDelay(conSocket);
+        }
+        else {
+            /* out of memory */
+            close(fd);
+
+            if (DEBUG_SOCKET)
+                printf("SOCKET: out of memory\n");
+        }
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: accept failed (errno=%i)\n", errno);
+    }
+
+    return conSocket;
+}
+
+void
+ServerSocket_setBacklog(ServerSocket self, int backlog)
+{
+    self->backLog = backlog;
+}
+
+static void
+closeAndShutdownSocket(int socketFd)
+{
+    if (socketFd != -1) {
+
+        if (DEBUG_SOCKET)
+            printf("SOCKET: call shutdown for %i!\n", socketFd);
+
+        /* shutdown is required to unblock read or accept in another thread! */
+        int result = shutdown(socketFd, SHUT_RDWR);
+
+        if (result == -1) {
+            if (DEBUG_SOCKET)
+                printf("SOCKET: shutdown error: %i\n", errno);
+        }
+
+        result = close(socketFd);
+
+        if (result == -1) {
+            if (DEBUG_SOCKET)
+                printf("SOCKET: close error: %i\n", errno);
+        }
+    }
+}
+
+void
+ServerSocket_destroy(ServerSocket self)
+{
+    int fd = self->fd;
+
+    self->fd = -1;
+
+    closeAndShutdownSocket(fd);
+
+    Thread_sleep(10);
+
+    GLOBAL_FREEMEM(self);
+}
+
+Socket
+TcpSocket_create()
+{
+    Socket self = (Socket)NULL;
+
+    int sock = socket(AF_INET, SOCK_STREAM, 0);
+
+    if (sock != -1) {
+        self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
+
+        if (self) {
+            self->fd = sock;
+            self->connectTimeout = 5000;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+            int tcpUserTimeout = 10000;
+            int result = setsockopt(sock, SOL_TCP,  TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
+
+            if (result == -1) {
+                if (DEBUG_SOCKET)
+                    printf("SOCKET: failed to set TCP_USER_TIMEOUT (errno=%i)\n", errno);
+            }
+#endif
+        }
+        else {
+            /* out of memory */
+            close(sock);
+
+            if (DEBUG_SOCKET)
+                printf("SOCKET: out of memory\n");
+        }
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to create socket (errno=%i)\n", errno);
+    }
+
+    return self;
+}
+
+void
+Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
+{
+    self->connectTimeout = timeoutInMs;
+}
+
+bool
+Socket_bind(Socket self, const char* srcAddress, int srcPort)
+{
+    struct sockaddr_in localAddress;
+
+    if (!prepareAddress(srcAddress, srcPort, &localAddress))
+        return false;
+
+    int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
+
+    if (result == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to bind TCP socket (errno=%i)\n", errno);
+
+        close(self->fd);
+        self->fd = -1;
+
+        return false;
+    }    
+
+    return true;
+}
+
+bool
+Socket_connectAsync(Socket self, const char* address, int port)
+{
+    struct sockaddr_in serverAddress;
+
+    if (DEBUG_SOCKET)
+        printf("SOCKET: connect: %s:%i\n", address, port);
+
+    if (!prepareAddress(address, port, &serverAddress))
+        return false;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    activateTcpNoDelay(self);
+
+    fcntl(self->fd, F_SETFL, O_NONBLOCK);
+
+    if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {
+
+        if (errno != EINPROGRESS) {
+            if (close(self->fd) == -1) {
+                if (DEBUG_SOCKET)
+                    printf("SOCKET: failed to close socket (errno: %i)\n", errno);
+            }
+
+            self->fd = -1;
+            return false;
+        }
+    }
+
+    return true; /* is connecting or already connected */
+}
+
+SocketState
+Socket_checkAsyncConnectState(Socket self)
+{
+    struct timeval timeout;
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    int selectVal = select(self->fd + 1, NULL, &fdSet , NULL, &timeout);
+
+    if (selectVal == 1) {
+
+        /* Check if connection is established */
+
+        int so_error;
+        socklen_t len = sizeof so_error;
+
+        if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) {
+
+            if (so_error == 0)
+                return SOCKET_STATE_CONNECTED;
+        }
+
+        return SOCKET_STATE_FAILED;
+    }
+    else if (selectVal == 0) {
+        return SOCKET_STATE_CONNECTING;
+    }
+    else {
+        return SOCKET_STATE_FAILED;
+    }
+}
+
+bool
+Socket_connect(Socket self, const char* address, int port)
+{
+    if (Socket_connectAsync(self, address, port) == false)
+        return false;
+
+    struct timeval timeout;
+    timeout.tv_sec = self->connectTimeout / 1000;
+    timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) {
+
+        /* Check if connection is established */
+
+        int so_error;
+        socklen_t len = sizeof so_error;
+
+        if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) {
+
+            if (so_error == 0)
+                return true;
+        }
+    }
+
+    close (self->fd);
+    self->fd = -1;
+
+    return false;
+}
+
+static char*
+convertAddressToStr(struct sockaddr_storage* addr)
+{
+    char addrString[INET6_ADDRSTRLEN + 7];
+    int port;
+
+    bool isIPv6;
+
+    if (addr->ss_family == AF_INET) {
+        struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) addr;
+        port = ntohs(ipv4Addr->sin_port);
+        inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN);
+        isIPv6 = false;
+    }
+    else if (addr->ss_family == AF_INET6) {
+        struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) addr;
+        port = ntohs(ipv6Addr->sin6_port);
+        inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN);
+        isIPv6 = true;
+    }
+    else
+        return NULL ;
+
+    char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9);
+
+    if (isIPv6)
+        sprintf(clientConnection, "[%s]:%i", addrString, port);
+    else
+        sprintf(clientConnection, "%s:%i", addrString, port);
+
+    return clientConnection;
+}
+
+char*
+Socket_getPeerAddress(Socket self)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+
+    if (getpeername(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) {
+        return convertAddressToStr(&addr);
+    }
+    else
+        return NULL;
+}
+
+char*
+Socket_getLocalAddress(Socket self)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+
+    if (getsockname(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) {
+        return convertAddressToStr(&addr);
+    }
+    else
+        return NULL;
+}
+
+char*
+Socket_getPeerAddressStatic(Socket self, char* peerAddressString)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+    memset(&addr, 0, sizeof(addr));
+
+    if (getpeername(self->fd, (struct sockaddr*) &addr, &addrLen) == -1)
+    {
+        if (DEBUG_SOCKET)
+            printf("DEBUG_SOCKET: getpeername -> errno: %i\n", errno);
+
+        return NULL;
+    }
+
+    char addrString[INET6_ADDRSTRLEN + 7];
+    int port;
+
+    bool isIPv6;
+
+    if (addr.ss_family == AF_INET) {
+        struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr;
+        port = ntohs(ipv4Addr->sin_port);
+        inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN);
+        isIPv6 = false;
+    }
+    else if (addr.ss_family == AF_INET6) {
+        struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr;
+        port = ntohs(ipv6Addr->sin6_port);
+        inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN);
+        isIPv6 = true;
+    }
+    else
+        return NULL;
+
+    if (isIPv6)
+        sprintf(peerAddressString, "[%s]:%i", addrString, port);
+    else
+        sprintf(peerAddressString, "%s:%i", addrString, port);
+
+    return peerAddressString;
+}
+
+int
+Socket_read(Socket self, uint8_t* buf, int size)
+{
+    if (self->fd == -1)
+        return -1;
+
+    int read_bytes = recv(self->fd, buf, size, MSG_DONTWAIT);
+
+    if (read_bytes == 0)
+        return -1;
+
+    if (read_bytes == -1) {
+        int error = errno;
+
+        switch (error) {
+
+            case EAGAIN:
+                return 0;
+            case EBADF:
+                return -1;
+
+            default:
+
+                if (DEBUG_SOCKET)
+                    printf("DEBUG_SOCKET: recv returned error (errno=%i)\n", error);
+
+                return -1;
+        }
+    }
+
+    return read_bytes;
+}
+
+int
+Socket_write(Socket self, uint8_t* buf, int size)
+{
+    if (self->fd == -1)
+        return -1;
+
+    /* MSG_NOSIGNAL - prevent send to signal SIGPIPE when peer unexpectedly closed the socket */
+    int retVal = send(self->fd, buf, size, MSG_NOSIGNAL | MSG_DONTWAIT);
+
+    if (retVal == -1) {
+        if (errno == EAGAIN) {
+            return 0;
+        }
+        else {
+            if (DEBUG_SOCKET)
+                printf("DEBUG_SOCKET: send returned error (errno=%i)\n", errno);
+        }
+    }
+
+    return retVal;
+}
+
+void
+Socket_destroy(Socket self)
+{
+    int fd = self->fd;
+
+    self->fd = -1;
+
+    closeAndShutdownSocket(fd);
+
+    Thread_sleep(10);
+
+    GLOBAL_FREEMEM(self);
+}
+
+UdpSocket
+UdpSocket_create()
+{
+    UdpSocket self = NULL;
+
+    int sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+    if (sock != -1) {
+        self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket));
+
+        self->fd = sock;
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to create UDP socket (errno=%i)\n", errno);
+    }
+
+    return self;
+}
+
+bool
+UdpSocket_bind(UdpSocket self, const char* address, int port)
+{
+    struct sockaddr_in localAddress;
+
+    if (!prepareAddress(address, port, &localAddress)) {
+        close(self->fd);
+        self->fd = 0;
+        return false;
+    }
+
+    int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
+
+    if (result == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to bind UDP socket (errno=%i)\n", errno);
+
+        close(self->fd);
+        self->fd = 0;
+
+        return false;
+    }
+
+    return true;
+}
+
+bool
+UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize)
+{
+    struct sockaddr_in remoteAddress;
+
+    if (!prepareAddress(address, port, &remoteAddress)) {
+
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to lookup remote address %s\n", address);
+
+        return false;
+    }
+
+    int result = sendto(self->fd, msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress));
+
+    if (result == msgSize) {
+        return true;
+    }
+    else if (result == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to send UDP message (errno=%i)\n", errno);
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to send UDP message (insufficient data sent)\n");
+    }
+
+    return false;
+}
+
+int
+UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize)
+{
+    struct sockaddr_storage remoteAddress;
+    socklen_t structSize = sizeof(struct sockaddr_storage);
+
+    int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, (struct sockaddr*)&remoteAddress, &structSize);
+
+    if (result == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to receive UDP message (errno=%i)\n", errno);
+    }
+
+    if (address) {
+        bool isIPv6;
+        char addrString[INET6_ADDRSTRLEN + 7];
+        int port;
+
+        if (remoteAddress.ss_family == AF_INET) {
+            struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress;
+            port = ntohs(ipv4Addr->sin_port);
+            inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN);
+            isIPv6 = false;
+        }
+        else if (remoteAddress.ss_family == AF_INET6) {
+            struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress;
+            port = ntohs(ipv6Addr->sin6_port);
+            inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN);
+            isIPv6 = true;
+        }
+        else
+            return result ;
+
+        if (isIPv6)
+            snprintf(address, maxAddrSize, "[%s]:%i", addrString, port);
+        else
+            snprintf(address, maxAddrSize, "%s:%i", addrString, port);
+    }
+
+    return result;
+}

+ 796 - 0
app/IEC_SERVER/lib60870-C/src/hal/socket/win32/socket_win32.c

@@ -0,0 +1,796 @@
+/*
+ *  socket_win32.c
+ *
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#pragma comment (lib, "Ws2_32.lib")
+
+#include "lib_memory.h"
+#include "hal_socket.h"
+
+#ifndef DEBUG_SOCKET
+#define DEBUG_SOCKET 0
+#endif
+
+#ifndef __MINGW64_VERSION_MAJOR
+struct tcp_keepalive {
+    u_long  onoff;
+    u_long  keepalivetime;
+    u_long  keepaliveinterval;
+};
+#endif
+
+#define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
+
+struct sSocket {
+    SOCKET fd;
+    uint32_t connectTimeout;
+};
+
+struct sServerSocket {
+    SOCKET fd;
+    int backLog;
+};
+
+struct sHandleSet {
+   fd_set handles;
+   SOCKET maxHandle;
+};
+
+struct sUdpSocket {
+	SOCKET fd;
+};
+
+HandleSet
+Handleset_new(void)
+{
+    HandleSet result = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet));
+
+    if (result != NULL) {
+        FD_ZERO(&result->handles);
+        result->maxHandle = INVALID_SOCKET;
+    }
+
+    return result;
+}
+
+void
+Handleset_reset(HandleSet self)
+{
+    FD_ZERO(&self->handles);
+    self->maxHandle = INVALID_SOCKET;
+}
+
+void
+Handleset_addSocket(HandleSet self, const Socket sock)
+{
+   if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) {
+
+       FD_SET(sock->fd, &self->handles);
+
+       if ((sock->fd > self->maxHandle) || (self->maxHandle == INVALID_SOCKET))
+           self->maxHandle = sock->fd;
+   }
+}
+
+void
+Handleset_removeSocket(HandleSet self, const Socket sock)
+{
+    if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) {
+        FD_CLR(sock->fd, &self->handles);
+    }
+}
+
+int
+Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
+{
+    int result;
+
+    if ((self != NULL) && (self->maxHandle != INVALID_SOCKET)) {
+        struct timeval timeout;
+
+        timeout.tv_sec = timeoutMs / 1000;
+        timeout.tv_usec = (timeoutMs % 1000) * 1000;
+
+        fd_set handles;
+
+        memcpy((void*)&handles, &(self->handles), sizeof(fd_set));
+
+        result = select(0, &handles, NULL, NULL, &timeout);
+    } else {
+        result = -1;
+    }
+
+    return result;
+}
+
+void
+Handleset_destroy(HandleSet self)
+{
+    GLOBAL_FREEMEM(self);
+}
+
+static bool wsaStartupCalled = false;
+static int socketCount = 0;
+
+void
+Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count)
+{
+    (void)count; /* not supported in windows socket API */
+
+    struct tcp_keepalive keepalive;
+    DWORD retVal=0;
+
+    keepalive.onoff = 1;
+    keepalive.keepalivetime = idleTime * 1000;
+    keepalive.keepaliveinterval = interval * 1000;
+
+     if (WSAIoctl(self->fd, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive),
+                NULL, 0, &retVal, NULL, NULL) == SOCKET_ERROR)
+     {
+         if (DEBUG_SOCKET)
+                printf("WIN32_SOCKET: WSAIotcl(SIO_KEEPALIVE_VALS) failed: %d\n",
+                    WSAGetLastError());
+     }
+}
+
+
+static void
+setSocketNonBlocking(Socket self)
+{
+    unsigned long mode = 1;
+    if (ioctlsocket(self->fd, FIONBIO, &mode) != 0) {
+        if (DEBUG_SOCKET)
+            printf("WIN32_SOCKET: failed to set socket non-blocking!\n");
+    }
+
+    /* activate TCP_NODELAY */
+
+    int tcpNoDelay = 1;
+
+    setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&tcpNoDelay, sizeof(int));
+}
+
+static bool
+prepareAddress(const char *address, int port, struct sockaddr_in *sockaddr)
+{
+    memset((char *)sockaddr, 0, sizeof(struct sockaddr_in));
+
+    if (address != NULL) {
+        struct hostent *server;
+        server = gethostbyname(address);
+
+        if (server == NULL)
+            return false;
+
+        memcpy((char *)&sockaddr->sin_addr.s_addr, (char *)server->h_addr, server->h_length);
+    }
+    else
+        sockaddr->sin_addr.s_addr = htonl(INADDR_ANY);
+
+    sockaddr->sin_family = AF_INET;
+    sockaddr->sin_port = htons(port);
+
+    return true;
+}
+
+static bool
+wsaStartUp(void)
+{
+    if (wsaStartupCalled == false) {
+        int ec;
+        WSADATA wsa;
+
+        if ((ec = WSAStartup(MAKEWORD(2, 0), &wsa)) != 0) {
+            if (DEBUG_SOCKET)
+                printf("WIN32_SOCKET: winsock error: code %i\n", ec);
+            return false;
+        }
+        else {
+            wsaStartupCalled = true;
+            return true;
+        }
+            
+    }
+    else
+        return true;
+}
+
+static void
+wsaShutdown(void)
+{
+    if (wsaStartupCalled) {
+        if (socketCount == 0) {
+            WSACleanup();
+            wsaStartupCalled = false;
+        }
+
+    }
+}
+
+ServerSocket
+TcpServerSocket_create(const char* address, int port)
+{
+    ServerSocket serverSocket = NULL;
+    int ec;
+    SOCKET listen_socket = INVALID_SOCKET;
+
+    if (wsaStartUp() == false)
+        return NULL;
+
+    struct sockaddr_in server_addr;
+
+    if (!prepareAddress(address, port, &server_addr))
+        return NULL;
+
+    listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+    if (listen_socket == INVALID_SOCKET) {
+        if (DEBUG_SOCKET)
+            printf("WIN32_SOCKET: socket failed with error: %i\n", WSAGetLastError());
+
+        wsaShutdown();
+
+        return NULL;
+    }
+
+    int optionReuseAddr = 1;
+    setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&optionReuseAddr, sizeof(int));
+
+    ec = bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
+
+    if (ec == SOCKET_ERROR) {
+        if (DEBUG_SOCKET)
+            printf("WIN32_SOCKET: bind failed with error:%i\n", WSAGetLastError());
+        closesocket(listen_socket);
+
+        wsaShutdown();
+
+        return NULL;
+    }
+
+    serverSocket = (ServerSocket)GLOBAL_MALLOC(sizeof(struct sServerSocket));
+
+    if (serverSocket) {
+        serverSocket->fd = listen_socket;
+        serverSocket->backLog = 10;
+
+        setSocketNonBlocking((Socket)serverSocket);
+
+        socketCount++;
+    }
+    else {
+        closesocket(listen_socket);
+        wsaShutdown();
+    }
+
+    return serverSocket;
+}
+
+void
+ServerSocket_listen(ServerSocket self)
+{
+    listen(self->fd, self->backLog);
+}
+
+Socket
+ServerSocket_accept(ServerSocket self)
+{
+    Socket conSocket = NULL;
+
+    SOCKET fd = accept(self->fd, NULL, NULL);
+
+    if (fd != INVALID_SOCKET) {
+        conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket));
+        conSocket->fd = fd;
+
+        socketCount++;
+
+        setSocketNonBlocking(conSocket);
+
+        if (DEBUG_SOCKET)
+            printf("WIN32_SOCKET: connection accepted\n");
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("WIN32_SOCKET: accept failed\n");
+    }
+
+    return conSocket;
+}
+
+void
+ServerSocket_setBacklog(ServerSocket self, int backlog)
+{
+    self->backLog = backlog;
+}
+
+void
+ServerSocket_destroy(ServerSocket self)
+{
+    if (self->fd != INVALID_SOCKET) {
+        shutdown(self->fd, 2);
+        closesocket(self->fd);
+        socketCount--;
+        self->fd = INVALID_SOCKET;
+    }
+
+    wsaShutdown();
+    GLOBAL_FREEMEM(self);
+}
+
+Socket
+TcpSocket_create()
+{
+    Socket self = NULL;
+
+    if (wsaStartUp() == false)
+        return NULL;
+
+    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
+
+    if (sock != INVALID_SOCKET) {
+        self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket));
+
+        if (self) {
+            self->fd = sock;
+            self->connectTimeout = 5000;
+
+            socketCount++;
+        }
+        else {
+            if (DEBUG_SOCKET)
+                printf("SOCKET: failed to create socket - cannot allocate memory\n");
+
+            closesocket(sock);
+            wsaShutdown();
+        }
+
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to create socket (error code=%i)\n", WSAGetLastError());
+    }
+
+    return self;
+}
+
+void
+Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs)
+{
+    self->connectTimeout = timeoutInMs;
+}
+
+bool
+Socket_bind(Socket self, const char* srcAddress, int srcPort)
+{
+    struct sockaddr_in localAddress;
+
+    if (!prepareAddress(srcAddress, srcPort, &localAddress))
+        return false;
+
+    int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
+
+    if (result == SOCKET_ERROR) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to bind TCP socket (errno=%i)\n", WSAGetLastError());
+
+        closesocket(self->fd);
+        self->fd = -1;
+
+        return false;
+    }
+
+    return true;
+}
+
+bool
+Socket_connectAsync(Socket self, const char* address, int port)
+{
+    if (DEBUG_SOCKET)
+        printf("WIN32_SOCKET: Socket_connect: %s:%i\n", address, port);
+
+    struct sockaddr_in serverAddress;
+    WSADATA wsa;
+    int ec;
+
+    if ((ec = WSAStartup(MAKEWORD(2,0), &wsa)) != 0) {
+        if (DEBUG_SOCKET)
+            printf("WIN32_SOCKET: winsock error: code %i\n", ec);
+        return false;
+    }
+
+    if (!prepareAddress(address, port, &serverAddress))
+        return false;
+
+    setSocketNonBlocking(self);
+
+    if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
+        if (WSAGetLastError() != WSAEWOULDBLOCK) {
+            closesocket(self->fd);
+            self->fd = INVALID_SOCKET;
+            return false;
+        }
+    }
+
+    return true; /* is connecting or already connected */
+}
+
+SocketState
+Socket_checkAsyncConnectState(Socket self)
+{
+    struct timeval timeout;
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    int selectVal = select(0, NULL, &fdSet , NULL, &timeout);
+
+    if (selectVal == 1) {
+
+        /* Check if connection is established */
+
+        int so_error;
+        int len = sizeof so_error;
+
+        if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*) (&so_error), &len) != SOCKET_ERROR) {
+            if (so_error == 0) {
+
+                int recvRes = recv(self->fd, NULL, 0, 0);
+
+                if (recvRes == SOCKET_ERROR) {
+                    int wsaError = WSAGetLastError();
+
+                    if (wsaError == WSAECONNRESET)
+                        return SOCKET_STATE_FAILED;
+
+                    if (wsaError == WSAECONNABORTED)
+                        return SOCKET_STATE_FAILED;
+                }
+
+
+                return SOCKET_STATE_CONNECTED;
+            }
+        }
+
+        return SOCKET_STATE_FAILED;
+    }
+    else if (selectVal == 0) {
+        return SOCKET_STATE_CONNECTING;
+    }
+    else {
+        return SOCKET_STATE_FAILED;
+    }
+}
+
+bool
+Socket_connect(Socket self, const char* address, int port)
+{
+    if (Socket_connectAsync(self, address, port) == false)
+        return false;
+
+    struct timeval timeout;
+    timeout.tv_sec = self->connectTimeout / 1000;
+    timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
+
+    fd_set fdSet;
+    FD_ZERO(&fdSet);
+    FD_SET(self->fd, &fdSet);
+
+    if (select(0, NULL, &fdSet , NULL, &timeout) == 1) {
+
+        /* Check if connection is established */
+
+        int so_error;
+        socklen_t len = sizeof so_error;
+
+        if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*)&so_error, &len) >= 0) {
+
+            if (so_error == 0)
+                return true;
+        }
+    }
+
+    closesocket (self->fd);
+    self->fd = INVALID_SOCKET;
+
+    return false;
+}
+
+static char*
+convertAddressToStr(struct sockaddr_storage* addr)
+{
+    char addrString[INET6_ADDRSTRLEN + 7];
+    int addrStringLen = INET6_ADDRSTRLEN + 7;
+    int port;
+
+    bool isIPv6;
+
+    if (addr->ss_family == AF_INET)  {
+        struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) addr;
+        port = ntohs(ipv4Addr->sin_port);
+        ipv4Addr->sin_port = 0;
+        WSAAddressToString((LPSOCKADDR) ipv4Addr, sizeof(struct sockaddr_storage), NULL,
+            (LPSTR) addrString, (LPDWORD) &addrStringLen);
+        isIPv6 = false;
+    }
+    else if (addr->ss_family == AF_INET6){
+        struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) addr;
+        port = ntohs(ipv6Addr->sin6_port);
+        ipv6Addr->sin6_port = 0;
+        WSAAddressToString((LPSOCKADDR) ipv6Addr, sizeof(struct sockaddr_storage), NULL,
+            (LPSTR) addrString, (LPDWORD) &addrStringLen);
+        isIPv6 = true;
+    }
+    else
+        return NULL;
+
+    char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9);
+
+    if (isIPv6)
+        sprintf(clientConnection, "[%s]:%i", addrString, port);
+    else
+        sprintf(clientConnection, "%s:%i", addrString, port);
+
+    return clientConnection;
+}
+
+
+char*
+Socket_getPeerAddress(Socket self)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+
+    if (getpeername(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) {
+        return convertAddressToStr(&addr);
+    }
+    else
+        return NULL;
+}
+
+char*
+Socket_getLocalAddress(Socket self)
+{
+    struct sockaddr_storage addr;
+    socklen_t addrLen = sizeof(addr);
+
+    if (getsockname(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) {
+        return convertAddressToStr(&addr);
+    }
+    else
+        return NULL;
+}
+
+char*
+Socket_getPeerAddressStatic(Socket self, char* peerAddressString)
+{
+    struct sockaddr_storage addr;
+    int addrLen = sizeof(addr);
+
+    getpeername(self->fd, (struct sockaddr*) &addr, &addrLen);
+
+    char addrString[INET6_ADDRSTRLEN + 7];
+    int addrStringLen = INET6_ADDRSTRLEN + 7;
+    int port;
+
+    bool isIPv6;
+
+    if (addr.ss_family == AF_INET) {
+        struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr;
+        port = ntohs(ipv4Addr->sin_port);
+        ipv4Addr->sin_port = 0;
+        WSAAddressToString((LPSOCKADDR) ipv4Addr, sizeof(struct sockaddr_storage), NULL,
+                (LPSTR) addrString, (LPDWORD) & addrStringLen);
+        isIPv6 = false;
+    }
+    else if (addr.ss_family == AF_INET6) {
+        struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr;
+        port = ntohs(ipv6Addr->sin6_port);
+        ipv6Addr->sin6_port = 0;
+        WSAAddressToString((LPSOCKADDR) ipv6Addr, sizeof(struct sockaddr_storage), NULL,
+                (LPSTR) addrString, (LPDWORD) & addrStringLen);
+        isIPv6 = true;
+    }
+    else
+        return NULL;
+
+    if (isIPv6)
+        sprintf(peerAddressString, "[%s]:%i", addrString, port);
+    else
+        sprintf(peerAddressString, "%s:%i", addrString, port);
+
+    return peerAddressString;
+}
+
+int
+Socket_read(Socket self, uint8_t* buf, int size)
+{
+    int bytes_read = recv(self->fd, (char*) buf, size, 0);
+
+    if (bytes_read == 0) /* peer has closed socket */
+        return -1;
+
+    if (bytes_read == SOCKET_ERROR) {
+        if (WSAGetLastError() == WSAEWOULDBLOCK)
+            return 0;
+        else
+            return -1;
+    }
+
+    return bytes_read;
+}
+
+int
+Socket_write(Socket self, uint8_t* buf, int size)
+{
+    int bytes_sent = send(self->fd, (char*) buf, size, 0);
+
+    if (bytes_sent == SOCKET_ERROR) {
+        int errorCode = WSAGetLastError();
+
+        if (errorCode == WSAEWOULDBLOCK)
+            bytes_sent = 0;
+        else
+            bytes_sent = -1;
+    }
+
+    return bytes_sent;
+}
+
+void
+Socket_destroy(Socket self)
+{
+    if (self->fd != INVALID_SOCKET) {
+        shutdown(self->fd, 2);
+        closesocket(self->fd);
+
+        self->fd = INVALID_SOCKET;
+
+        socketCount--;
+    }
+
+    wsaShutdown();
+
+    GLOBAL_FREEMEM(self);
+}
+
+UdpSocket
+UdpSocket_create()
+{
+    UdpSocket self = NULL;
+
+    SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+    if (sock != INVALID_SOCKET) {
+        self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket));
+
+        self->fd = sock;
+
+        setSocketNonBlocking((Socket)self);
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to create UDP socket (errno=%i)\n", errno);
+    }
+
+    return self;
+}
+
+bool
+UdpSocket_bind(UdpSocket self, const char* address, int port)
+{
+    struct sockaddr_in localAddress;
+
+    if (!prepareAddress(address, port, &localAddress)) {
+		closesocket(self->fd);
+        self->fd = 0;
+        return false;
+    }
+
+    int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress));
+
+    if (result == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to bind UDP socket (errno=%i)\n", errno);
+
+		closesocket(self->fd);
+        self->fd = 0;
+
+        return false;
+    }
+
+    return true;
+}
+
+bool
+UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize)
+{
+    struct sockaddr_in remoteAddress;
+
+    if (!prepareAddress(address, port, &remoteAddress)) {
+
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to lookup remote address %s\n", address);
+
+        return false;
+    }
+
+    int result = sendto(self->fd, (const char*) msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress));
+
+    if (result == msgSize) {
+        return true;
+    }
+    else if (result == -1) {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to send UDP message (errno=%i)\n", errno);
+    }
+    else {
+        if (DEBUG_SOCKET)
+            printf("SOCKET: failed to send UDP message (insufficient data sent)\n");
+    }
+
+    return false;
+}
+
+int
+UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize)
+{
+    struct sockaddr_storage remoteAddress;
+    socklen_t structSize = sizeof(struct sockaddr_storage);
+
+    int result = recvfrom(self->fd, (char*) msg, msgSize, 0, (struct sockaddr*)&remoteAddress, &structSize);
+
+    if (result == 0) /* peer has closed socket */
+        return -1;
+
+    if (result == SOCKET_ERROR) {
+        if (WSAGetLastError() == WSAEWOULDBLOCK)
+            return 0;
+        else
+            return -1;
+    }
+
+    if (address) {
+        bool isIPv6;
+        char addrString[INET6_ADDRSTRLEN + 7];
+        int port;
+
+        if (remoteAddress.ss_family == AF_INET) {
+            struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress;
+            port = ntohs(ipv4Addr->sin_port);
+            inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN);
+            isIPv6 = false;
+        }
+        else if (remoteAddress.ss_family == AF_INET6) {
+            struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress;
+            port = ntohs(ipv6Addr->sin6_port);
+            inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN);
+            isIPv6 = true;
+        }
+        else
+            return result ;
+
+        if (isIPv6)
+            snprintf(address, maxAddrSize, "[%s]:%i", addrString, port);
+        else
+            snprintf(address, maxAddrSize, "%s:%i", addrString, port);
+    }
+
+    return result;
+}

+ 109 - 0
app/IEC_SERVER/lib60870-C/src/hal/thread/bsd/thread_bsd.c

@@ -0,0 +1,109 @@
+/**
+ * thread_bsd.c
+ *
+ * Copyright 2013-2021 Michael Zillgith
+ *
+ * This file is part of Platform Abstraction Layer (libpal)
+ * for libiec61850, libmms, and lib60870.
+ */
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <unistd.h>
+#include "hal_thread.h"
+#include "lib_memory.h"
+
+struct sThread {
+    ThreadExecutionFunction function;
+    void* parameter;
+    pthread_t pthread;
+    int state;
+    bool autodestroy;
+};
+
+Semaphore
+Semaphore_create(int initialValue)
+{
+    Semaphore self = GLOBAL_MALLOC(sizeof(sem_t));
+
+    sem_init((sem_t*) self, 0, initialValue);
+
+    return self;
+}
+
+/* Wait until semaphore value is more than zero. Then decrease the semaphore value. */
+void
+Semaphore_wait(Semaphore self)
+{
+    sem_wait((sem_t*) self);
+}
+
+void
+Semaphore_post(Semaphore self)
+{
+    sem_post((sem_t*) self);
+}
+
+void
+Semaphore_destroy(Semaphore self)
+{
+    sem_destroy((sem_t*) self);
+    GLOBAL_FREEMEM(self);
+}
+
+Thread
+Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
+{
+   Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));
+
+   if (thread != NULL) {
+        thread->parameter = parameter;
+        thread->function = function;
+        thread->state = 0;
+        thread->autodestroy = autodestroy;
+   }
+
+   return thread;
+}
+
+static void*
+destroyAutomaticThread(void* parameter)
+{
+    Thread thread = (Thread) parameter;
+
+    thread->function(thread->parameter);
+
+    GLOBAL_FREEMEM(thread);
+
+    pthread_exit(NULL);
+}
+
+void
+Thread_start(Thread thread)
+{
+    if (thread->autodestroy == true) {
+        pthread_create(&thread->pthread, NULL, destroyAutomaticThread, thread);
+        pthread_detach(thread->pthread);
+    }
+    else
+        pthread_create(&thread->pthread, NULL, thread->function, thread->parameter);
+
+    thread->state = 1;
+}
+
+void
+Thread_destroy(Thread thread)
+{
+    if (thread->state == 1) {
+        pthread_join(thread->pthread, NULL);
+    }
+
+    GLOBAL_FREEMEM(thread);
+}
+
+void
+Thread_sleep(int millies)
+{
+    usleep(millies * 1000);
+}
+

+ 108 - 0
app/IEC_SERVER/lib60870-C/src/hal/thread/linux/thread_linux.c

@@ -0,0 +1,108 @@
+/*
+ *  thread_linux.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include "hal_thread.h"
+#include "lib_memory.h"
+
+#include "FreeRTOS.h"
+#include "main.h"
+#include "semphr.h"
+#include "platform_thread.h"
+#include "platform_memory.h"
+
+
+struct sThread {
+    ThreadExecutionFunction function;
+    void* parameter;
+    platform_thread_t * pthread;
+    int state;
+    bool autodestroy;
+};
+
+SemaphoreHandle_t
+Semaphore_create(void)
+{
+    return xSemaphoreCreateMutex();;
+}
+
+/* Wait until semaphore value is more than zero. Then decrease the semaphore value. */
+void
+Semaphore_wait(Semaphore queueMutex)
+{
+    xSemaphoreTake(queueMutex, 1000);
+}
+
+void
+Semaphore_post(Semaphore queueMutex)
+{
+    xSemaphoreGive(queueMutex);
+}
+
+void
+Semaphore_destroy(SemaphoreHandle_t logMutex)
+{
+    vSemaphoreDelete(logMutex)
+}
+
+Thread
+Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
+{
+    Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));
+
+    if (thread != NULL) {
+        thread->parameter = parameter;
+        thread->function = function;
+        thread->state = 0;
+        thread->autodestroy = autodestroy;
+    }
+
+    return thread;
+}
+
+static void*
+destroyAutomaticThread(void* parameter)
+{
+    Thread thread = (Thread) parameter;
+
+    thread->function(thread->parameter);
+
+    GLOBAL_FREEMEM(thread);
+
+    pthread_exit(NULL);
+}
+
+void
+Thread_start(Thread thread)
+{
+    if (thread->autodestroy == true) {
+        pthread_create(&thread->pthread, NULL, destroyAutomaticThread, thread);
+        pthread_detach(thread->pthread);
+    }
+    else
+        pthread_create(&thread->pthread, NULL, thread->function, thread->parameter);
+
+    thread->state = 1;
+}
+
+void
+Thread_destroy(Thread thread)
+{
+    if (thread->state == 1) {
+        pthread_join(thread->pthread, NULL);
+    }
+
+    GLOBAL_FREEMEM(thread);
+}
+
+void
+Thread_sleep(int millies)
+{
+    usleep(millies * 1000);
+}
+

+ 149 - 0
app/IEC_SERVER/lib60870-C/src/hal/thread/macos/thread_macos.c

@@ -0,0 +1,149 @@
+/**
+ * thread_macos.c
+ *
+ * Copyright 2013-2021 Michael Zillgith
+ *
+ * This file is part of Platform Abstraction Layer (libpal)
+ * for libiec61850, libmms, and lib60870.
+ */
+
+/*
+ * NOTE: MacOS needs own thread layer because it doesn't support unnamed semaphores!
+ * NOTE: named semaphores were replaced by POSIX mutex
+ */
+
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include "hal_thread.h"
+#include "lib_memory.h"
+
+struct sThread {
+   ThreadExecutionFunction function;
+   void* parameter;
+   pthread_t pthread;
+   int state;
+   bool autodestroy;
+};
+
+typedef struct sSemaphore* mSemaphore;
+
+struct sSemaphore
+{
+    pthread_mutex_t mutex;
+};
+
+/*
+ * NOTE: initialValue is ignored because semaphore was replaced by mutex
+ */
+Semaphore
+Semaphore_create(int initialValue)
+{
+    mSemaphore self = NULL;
+
+    self = (mSemaphore) GLOBAL_CALLOC(1, sizeof(struct sSemaphore));
+
+    if (self) {
+        pthread_mutex_init(&(self->mutex), NULL);
+    }
+
+    return (Semaphore)self;
+}
+
+/* lock mutex */
+void
+Semaphore_wait(Semaphore self)
+{
+    mSemaphore mSelf = (mSemaphore) self;
+
+    int retVal = pthread_mutex_lock(&(mSelf->mutex));
+
+    if (retVal) {
+       printf("FATAL ERROR: pthread_mutex_lock failed (err=%i)\n", retVal);
+       exit(-1);
+    }
+}
+
+/* unlock mutex */
+void
+Semaphore_post(Semaphore self)
+{
+    mSemaphore mSelf = (mSemaphore) self;
+
+    int retVal = pthread_mutex_unlock(&(mSelf->mutex));
+
+    if (retVal) {
+        printf("FATAL ERROR: pthread_mutex_unlock failed (err=%i)\n", retVal);
+        exit(-1);
+    }
+}
+
+void
+Semaphore_destroy(Semaphore self)
+{
+    if (self) {
+        mSemaphore mSelf = (mSemaphore) self;
+
+        pthread_mutex_destroy(&(mSelf->mutex));
+
+        GLOBAL_FREEMEM(mSelf);
+    }    
+}
+
+Thread
+Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
+{
+   Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));
+
+   if (thread != NULL) {
+        thread->parameter = parameter;
+        thread->function = function;
+        thread->state = 0;
+        thread->autodestroy = autodestroy;
+   }
+
+   return thread;
+}
+
+static void*
+destroyAutomaticThread(void* parameter)
+{
+    Thread thread = (Thread) parameter;
+
+    thread->function(thread->parameter);
+
+    GLOBAL_FREEMEM(thread);
+
+    pthread_exit(NULL);
+}
+
+void
+Thread_start(Thread thread)
+{
+   if (thread->autodestroy == true) {
+       pthread_create(&thread->pthread, NULL, destroyAutomaticThread, thread);
+       pthread_detach(thread->pthread);
+   }
+   else
+       pthread_create(&thread->pthread, NULL, thread->function, thread->parameter);
+
+   thread->state = 1;
+}
+
+void
+Thread_destroy(Thread thread)
+{
+   if (thread->state == 1) {
+       pthread_join(thread->pthread, NULL);
+   }
+
+   GLOBAL_FREEMEM(thread);
+}
+
+void
+Thread_sleep(int millies)
+{
+   usleep(millies * 1000);
+}

+ 114 - 0
app/IEC_SERVER/lib60870-C/src/hal/thread/win32/thread_win32.c

@@ -0,0 +1,114 @@
+/*
+ *  thread_win32.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include <windows.h>
+#include "lib_memory.h"
+#include "hal_thread.h"
+
+struct sThread {
+	ThreadExecutionFunction function;
+	void* parameter;
+	HANDLE handle;
+	int state;
+	bool autodestroy;
+};
+
+static DWORD WINAPI
+destroyAutomaticThreadRunner(LPVOID parameter)
+{
+	Thread thread = (Thread) parameter;
+
+	thread->function(thread->parameter);
+
+	thread->state = 0;
+
+	Thread_destroy(thread);
+
+	return 0;
+}
+
+static DWORD WINAPI
+threadRunner(LPVOID parameter)
+{
+	Thread thread = (Thread) parameter;
+
+	thread->function(thread->parameter);
+
+	return (DWORD)0;
+}
+
+Thread
+Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
+{
+	DWORD threadId;
+	Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));
+
+	thread->parameter = parameter;
+	thread->function = function;
+	thread->state = 0;
+	thread->autodestroy = autodestroy;
+
+	if (autodestroy == true)
+		thread->handle = CreateThread(0, 0, destroyAutomaticThreadRunner, thread, CREATE_SUSPENDED, &threadId);
+	else
+		thread->handle = CreateThread(0, 0, threadRunner, thread, CREATE_SUSPENDED, &threadId);
+
+	return thread;
+}
+
+void
+Thread_start(Thread thread)
+{
+	thread->state = 1;
+	ResumeThread(thread->handle);
+}
+
+void
+Thread_destroy(Thread thread)
+{
+	if (thread->state == 1)
+		WaitForSingleObject(thread->handle, INFINITE);
+
+	CloseHandle(thread->handle);
+
+	GLOBAL_FREEMEM(thread);
+}
+
+void
+Thread_sleep(int millies)
+{
+	Sleep(millies);
+}
+
+Semaphore
+Semaphore_create(int initialValue)
+{
+    HANDLE self = CreateSemaphore(NULL, 1, 1, NULL);
+
+    return self;
+}
+
+/* Wait until semaphore value is greater than zero. Then decrease the semaphore value. */
+void
+Semaphore_wait(Semaphore self)
+{
+    WaitForSingleObject((HANDLE) self, INFINITE);
+}
+
+void
+Semaphore_post(Semaphore self)
+{
+    ReleaseSemaphore((HANDLE) self, 1, NULL);
+}
+
+void
+Semaphore_destroy(Semaphore self)
+{
+    CloseHandle((HANDLE) self);
+}

+ 67 - 0
app/IEC_SERVER/lib60870-C/src/hal/time/unix/time.c

@@ -0,0 +1,67 @@
+/*
+ *  time.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include "hal_time.h"
+#include <time.h>
+
+#ifdef CONFIG_SYSTEM_HAS_CLOCK_GETTIME
+uint64_t
+Hal_getTimeInMs()
+{
+	struct timespec tp;
+
+	clock_gettime(CLOCK_REALTIME, &tp);
+
+	return ((uint64_t) tp.tv_sec) * 1000LL + (tp.tv_nsec / 1000000);
+}
+#else
+
+#include <sys/time.h>
+
+msSinceEpoch
+Hal_getTimeInMs()
+{
+    struct timeval now;
+
+    gettimeofday(&now, NULL);
+
+    return ((uint64_t) now.tv_sec * 1000LL) + (now.tv_usec / 1000);
+}
+
+nsSinceEpoch
+Hal_getTimeInNs()
+{
+    struct timespec now;
+
+    clock_gettime(CLOCK_REALTIME, &now);
+
+    nsSinceEpoch nsTime = now.tv_sec * 1000000000UL;
+    nsTime += now.tv_nsec;
+
+    return nsTime;
+}
+
+bool
+Hal_setTimeInNs(nsSinceEpoch nsTime)
+{
+    struct timespec tv;
+
+    tv.tv_sec = nsTime / 1000000000UL;
+    tv.tv_nsec = nsTime % 1000000000UL;
+
+    if (clock_settime(CLOCK_REALTIME, &tv) < 0) {
+        return false;
+    }
+
+    return true;
+}
+
+
+#endif
+

+ 61 - 0
app/IEC_SERVER/lib60870-C/src/hal/time/win32/time.c

@@ -0,0 +1,61 @@
+/*
+ *  time.c
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#include "hal_time.h"
+#include <time.h>
+#include <windows.h>
+
+uint64_t
+Hal_getTimeInMs()
+{
+   FILETIME ft;
+   uint64_t now;
+
+   static const uint64_t DIFF_TO_UNIXTIME = 11644473600000ULL;
+
+   GetSystemTimeAsFileTime(&ft);
+
+   now = (LONGLONG)ft.dwLowDateTime + ((LONGLONG)(ft.dwHighDateTime) << 32LL);
+
+   return (now / 10000LL) - DIFF_TO_UNIXTIME;
+}
+
+nsSinceEpoch
+Hal_getTimeInNs()
+{
+   FILETIME ft;
+
+   static const uint64_t DIFF_TO_UNIXTIME = 11644473600000000000ULL;
+
+   GetSystemTimeAsFileTime(&ft);
+
+   nsSinceEpoch nsTime = (LONGLONG)ft.dwLowDateTime + ((LONGLONG)(ft.dwHighDateTime) << 32LL);
+
+   nsTime = nsTime * 100LL - DIFF_TO_UNIXTIME;
+
+   return nsTime;
+}
+
+bool
+Hal_setTimeInNs(nsSinceEpoch nsTime)
+{
+    uint64_t t = (nsTime / 100ULL) + 116444736000000000ULL;                               
+
+    FILETIME ft;
+
+    ft.dwLowDateTime = (uint32_t)(t & 0xffffffff);
+    ft.dwHighDateTime = (uint32_t)(t >> 32);
+
+    SYSTEMTIME st;
+
+    FileTimeToSystemTime(&ft, &st);
+
+    return SetSystemTime(&st);
+}
+

+ 65 - 0
app/IEC_SERVER/lib60870-C/src/hal/tls/mbedtls/mbedtls_config.h

@@ -0,0 +1,65 @@
+#ifndef MBEDTLS_CONFIG_H
+#define MBEDTLS_CONFIG_H
+
+/* System support */
+#define MBEDTLS_HAVE_ASM
+#define MBEDTLS_HAVE_TIME
+#define MBEDTLS_HAVE_TIME_DATE
+#define MBEDTLS_NO_UDBL_DIVISION
+#define MBEDTLS_PLATFORM_C
+#define MBEDTLS_DEBUG_C
+
+/* mbed TLS feature support */
+#define MBEDTLS_CIPHER_MODE_CBC
+#define MBEDTLS_PKCS1_V15
+#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
+#define MBEDTLS_SSL_PROTO_TLS1_2
+#define MBEDTLS_SSL_PROTO_TLS1_1
+#define MBEDTLS_SSL_PROTO_TLS1
+#define MBEDTLS_SSL_RENEGOTIATION
+
+#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES
+
+/* mbed TLS modules */
+#define MBEDTLS_AES_C
+#define MBEDTLS_ASN1_PARSE_C
+#define MBEDTLS_ASN1_WRITE_C
+#define MBEDTLS_BIGNUM_C
+#define MBEDTLS_CIPHER_C
+#define MBEDTLS_CTR_DRBG_C
+/* #define MBEDTLS_DES_C */
+#define MBEDTLS_ENTROPY_C
+#define MBEDTLS_MD_C
+#define MBEDTLS_MD5_C
+#define MBEDTLS_NET_C
+#define MBEDTLS_OID_C
+#define MBEDTLS_PK_C
+#define MBEDTLS_PK_PARSE_C
+#define MBEDTLS_RSA_C
+#define MBEDTLS_SHA1_C
+#define MBEDTLS_SHA256_C
+#define MBEDTLS_SSL_CLI_C
+#define MBEDTLS_SSL_SRV_C
+#define MBEDTLS_SSL_TLS_C
+#define MBEDTLS_X509_CRT_PARSE_C
+#define MBEDTLS_X509_CRL_PARSE_C
+#define MBEDTLS_X509_USE_C
+#define MBEDTLS_SSL_CACHE_C
+
+/* For test certificates */
+#define MBEDTLS_BASE64_C
+#define MBEDTLS_CERTS_C
+#define MBEDTLS_PEM_PARSE_C
+
+#define MBEDTLS_PKCS12_C
+#define MBEDTLS_PKCS5_C
+
+/* For testing with compat.sh */
+#define MBEDTLS_FS_IO
+
+#define MBEDTLS_X509_CHECK_KEY_USAGE
+#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
+
+#include "mbedtls/check_config.h"
+
+#endif /* MBEDTLS_CONFIG_H */

Datei-Diff unterdrückt, da er zu groß ist
+ 1060 - 0
app/IEC_SERVER/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c


+ 691 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/apl/cpXXtime2a.c

@@ -0,0 +1,691 @@
+/*
+ *  cpXXtime2a.c
+ *
+ *  Implementation of the types CP16Time2a, CP24Time2a and CP56Time2a
+ *
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <time.h>
+
+#include "lib_memory.h"
+
+#include "iec60870_common.h"
+#include "apl_types_internal.h"
+
+/**********************************
+ *  CP16Time2a type
+ **********************************/
+
+bool
+CP16Time2a_getFromBuffer (CP16Time2a self, const uint8_t* msg, int msgSize, int startIndex)
+{
+    if (msgSize < startIndex + 2)
+        return false;
+
+    int i;
+
+    for (i = 0; i < 2; i++)
+        self->encodedValue[i] = msg[startIndex + i];
+
+    return true;
+}
+
+int
+CP16Time2a_getEplapsedTimeInMs(const CP16Time2a self)
+{
+    return (self->encodedValue[0] + (self->encodedValue[1] * 0x100));
+}
+
+void
+CP16Time2a_setEplapsedTimeInMs(CP16Time2a self, int value)
+{
+    self->encodedValue[0] = (uint8_t) (value % 0x100);
+    self->encodedValue[1] = (uint8_t) (value / 0x100);
+}
+
+uint8_t*
+CP16Time2a_getEncodedValue(CP16Time2a self)
+{
+    return self->encodedValue;
+}
+
+/************************************
+ *  CP24Time2a and CP26Time2a common
+ ************************************/
+
+static int
+getMillisecond(const uint8_t* encodedValue)
+{
+    return (encodedValue[0] + (encodedValue[1] * 0x100)) % 1000;
+}
+
+static void
+setMillisecond(uint8_t* encodedValue, int value)
+{
+    int millies = encodedValue[0] + (encodedValue[1] * 0x100);
+
+    /* erase sub-second part */
+    millies = millies - (millies % 1000);
+
+    millies = millies + value;
+
+    encodedValue[0] = (uint8_t) (millies & 0xff);
+    encodedValue[1] = (uint8_t) ((millies / 0x100) & 0xff);
+}
+
+static int
+getSecond(const uint8_t* encodedValue)
+{
+    return  (encodedValue[0] + (encodedValue[1] * 0x100)) / 1000;
+}
+
+static void
+setSecond(uint8_t* encodedValue, int value)
+{
+    int millies = encodedValue[0] + (encodedValue[1] * 0x100);
+
+    int msPart = millies % 1000;
+
+    millies = (value * 1000) + msPart;
+
+    encodedValue[0] = (uint8_t) (millies & 0xff);
+    encodedValue[1] = (uint8_t) ((millies / 0x100) & 0xff);
+}
+
+static int
+getMinute(const uint8_t* encodedValue)
+{
+    return (encodedValue[2] & 0x3f);
+}
+
+static void
+setMinute(uint8_t* encodedValue, int value)
+{
+    encodedValue[2] = (uint8_t) ((encodedValue[2] & 0xc0) | (value & 0x3f));
+}
+
+static bool
+isInvalid(const uint8_t* encodedValue)
+{
+    return ((encodedValue[2] & 0x80) != 0);
+}
+
+static void
+setInvalid(uint8_t* encodedValue, bool value)
+{
+    if (value)
+        encodedValue[2] |= 0x80;
+    else
+        encodedValue[2] &= 0x7f;
+}
+
+static bool
+isSubstituted(const uint8_t* encodedValue)
+{
+    return ((encodedValue[2] & 0x40) == 0x40);
+}
+
+static void
+setSubstituted(uint8_t* encodedValue, bool value)
+{
+    if (value)
+        encodedValue[2] |= 0x40;
+    else
+        encodedValue[2] &= 0xbf;
+}
+
+/**********************************
+ *  CP24Time2a type
+ **********************************/
+
+bool
+CP24Time2a_getFromBuffer (CP24Time2a self, const uint8_t* msg, int msgSize, int startIndex)
+{
+    if (msgSize < startIndex + 3)
+        return false;
+
+    int i;
+
+    for (i = 0; i < 3; i++)
+        self->encodedValue[i] = msg[startIndex + i];
+
+    return true;
+}
+
+int
+CP24Time2a_getMillisecond(const CP24Time2a self)
+{
+    return getMillisecond(self->encodedValue);
+}
+
+void
+CP24Time2a_setMillisecond(CP24Time2a self, int value)
+{
+    setMillisecond(self->encodedValue, value);
+}
+
+int
+CP24Time2a_getSecond(const CP24Time2a self)
+{
+    return getSecond(self->encodedValue);
+}
+
+void
+CP24Time2a_setSecond(CP24Time2a self, int value)
+{
+    setSecond(self->encodedValue, value);
+}
+
+int
+CP24Time2a_getMinute(const CP24Time2a self)
+{
+    return getMinute(self->encodedValue);
+}
+
+
+void
+CP24Time2a_setMinute(CP24Time2a self, int value)
+{
+    setMinute(self->encodedValue, value);
+}
+
+bool
+CP24Time2a_isInvalid(const CP24Time2a self)
+{
+    return isInvalid(self->encodedValue);
+}
+
+void
+CP24Time2a_setInvalid(CP24Time2a self, bool value)
+{
+    setInvalid(self->encodedValue, value);
+}
+
+bool
+CP24Time2a_isSubstituted(const CP24Time2a self)
+{
+    return isSubstituted(self->encodedValue);
+}
+
+void
+CP24Time2a_setSubstituted(CP24Time2a self, bool value)
+{
+    setSubstituted(self->encodedValue, value);
+}
+
+#if 0
+/* function found here: http://stackoverflow.com/questions/530519/stdmktime-and-timezone-info */
+time_t my_timegm(register struct tm * t)
+/* struct tm to seconds since Unix epoch */
+{
+    register long year;
+    register time_t result;
+#define MONTHSPERYEAR   12      /* months per calendar year */
+    static const int cumdays[MONTHSPERYEAR] =
+        { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+    /*@ +matchanyintegral @*/
+    year = 1900 + t->tm_year + t->tm_mon / MONTHSPERYEAR;
+    result = (year - 1970) * 365 + cumdays[t->tm_mon % MONTHSPERYEAR];
+    result += (year - 1968) / 4;
+    result -= (year - 1900) / 100;
+    result += (year - 1600) / 400;
+    if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) &&
+        (t->tm_mon % MONTHSPERYEAR) < 2)
+        result--;
+    result += t->tm_mday - 1;
+    result *= 24;
+    result += t->tm_hour;
+    result *= 60;
+    result += t->tm_min;
+    result *= 60;
+    result += t->tm_sec;
+    if (t->tm_isdst == 1)
+        result -= 3600;
+    /*@ -matchanyintegral @*/
+    return (result);
+}
+#endif
+
+/* Conversion from UTC date to second, unsigned 32-bit Unix epoch version.
+ * Written by François Grieu, 2015-07-21; public domain.
+ *
+ * my_mktime  converts from  struct tm  UTC to non-leap seconds since
+ * 00:00:00 on the first UTC day of year 1970 (fixed).
+ * It works from 1970 to 2105 inclusive. It strives to be compatible
+ * with C compilers supporting // comments and claiming C89 conformance.
+ *
+ * input:   Pointer to a  struct tm  with field tm_year, tm_mon, tm_mday,
+ *          tm_hour, tm_min, tm_sec set per  mktime  convention; thus
+ *          - tm_year is year minus 1900
+ *          - tm_mon is [0..11] for January to December, but [-2..14]
+ *            works for November of previous year to February of next year
+ *          - tm_mday, tm_hour, tm_min, tm_sec similarly can be offset to
+ *            the full range [-32767 to 32768], as long as the combination
+ *            with tm_year gives a result within years [1970..2105], and
+ *            tm_year>0.
+ * output:  Number of non-leap seconds since beginning of the first UTC
+ *          day of year 1970, as an unsigned at-least-32-bit integer.
+ *          The input is not changed (in particular, fields tm_wday,
+ *          tm_yday, and tm_isdst are unchanged and ignored).
+ */
+static time_t
+my_mktime(const struct tm * ptm)
+{
+    int m, y = ptm->tm_year;
+
+    if ((m = ptm->tm_mon) < 2) {
+        m += 12;
+        --y;
+    }
+
+    return ((((time_t) (y - 69) * 365u + y / 4 - y / 100 * 3 / 4 + (m + 2) * 153 / 5 - 446 +
+            ptm->tm_mday) * 24u + ptm->tm_hour) * 60u + ptm->tm_min) * 60u + ptm->tm_sec;
+}
+
+
+/**********************************
+ *  CP32Time2a type
+ **********************************/
+
+CP32Time2a
+CP32Time2a_create(CP32Time2a self)
+{
+    if (self == NULL)
+        self = (CP32Time2a) GLOBAL_CALLOC(1, sizeof(struct sCP32Time2a));
+    else
+        memset (self, 0, sizeof(struct sCP32Time2a));
+
+    return self;
+}
+
+bool
+CP32Time2a_getFromBuffer (CP32Time2a self, const uint8_t* msg, int msgSize, int startIndex)
+{
+    if (msgSize < startIndex + 4)
+        return false;
+
+    int i;
+
+    for (i = 0; i < 4; i++)
+        self->encodedValue[i] = msg[startIndex + i];
+
+    return true;
+}
+
+int
+CP32Time2a_getMillisecond(const CP32Time2a self)
+{
+    return (self->encodedValue[0] + (self->encodedValue[1] * 0x100)) % 1000;
+}
+
+void
+CP32Time2a_setMillisecond(CP32Time2a self, int value)
+{
+    int millies = (CP32Time2a_getSecond(self) * 1000) + value;
+
+    self->encodedValue[0] = (uint8_t) (millies & 0xff);
+    self->encodedValue[1] = (uint8_t) ((millies / 0x100) & 0xff);
+}
+
+
+int
+CP32Time2a_getSecond(const CP32Time2a self)
+{
+    return getSecond(self->encodedValue);
+}
+
+void
+CP32Time2a_setSecond(CP32Time2a self, int value)
+{
+    setSecond(self->encodedValue, value);
+}
+
+int
+CP32Time2a_getMinute(const CP32Time2a self)
+{
+    return getMinute(self->encodedValue);
+}
+
+
+void
+CP32Time2a_setMinute(CP32Time2a self, int value)
+{
+    setMinute(self->encodedValue, value);
+}
+
+bool
+CP32Time2a_isInvalid(const CP32Time2a self)
+{
+    return isInvalid(self->encodedValue);
+}
+
+void
+CP32Time2a_setInvalid(CP32Time2a self, bool value)
+{
+    setInvalid(self->encodedValue, value);
+}
+
+bool
+CP32Time2a_isSubstituted(const CP32Time2a self)
+{
+    return isSubstituted(self->encodedValue);
+}
+
+void
+CP32Time2a_setSubstituted(CP32Time2a self, bool value)
+{
+    setSubstituted(self->encodedValue, value);
+}
+
+int
+CP32Time2a_getHour(const CP32Time2a self)
+{
+    return (self->encodedValue[3] & 0x1f);
+}
+
+void
+CP32Time2a_setHour(CP32Time2a self, int value)
+{
+    self->encodedValue[3] = (uint8_t) ((self->encodedValue[3] & 0xe0) | (value & 0x1f));
+}
+
+bool
+CP32Time2a_isSummerTime(const CP32Time2a self)
+{
+    return ((self->encodedValue[3] & 0x80) != 0);
+}
+
+void
+CP32Time2a_setSummerTime(CP32Time2a self, bool value)
+{
+    if (value)
+        self->encodedValue[3] |= 0x80;
+    else
+        self->encodedValue[3] &= 0x7f;
+}
+
+void
+CP32Time2a_setFromMsTimestamp(CP32Time2a self, uint64_t timestamp)
+{
+    memset(self->encodedValue, 0, 4);
+
+    time_t timeVal = timestamp / 1000;
+
+    int msPart = timestamp % 1000;
+
+    struct tm tmTime;
+
+#ifdef _WIN32
+    gmtime_s(&tmTime, &timeVal);
+#else
+    gmtime_r(&timeVal, &tmTime);
+#endif
+
+    CP32Time2a_setSecond(self, tmTime.tm_sec);
+
+    CP32Time2a_setMillisecond(self, msPart);
+
+    CP32Time2a_setMinute(self, tmTime.tm_min);
+
+    CP32Time2a_setHour(self, tmTime.tm_hour);
+}
+
+uint8_t*
+CP32Time2a_getEncodedValue(CP32Time2a self)
+{
+    return self->encodedValue;
+}
+
+/**********************************
+ *  CP56Time2a type
+ **********************************/
+
+CP56Time2a
+CP56Time2a_createFromMsTimestamp(CP56Time2a self, uint64_t timestamp)
+{
+    if (self == NULL)
+        self = (CP56Time2a) GLOBAL_CALLOC(1, sizeof(struct sCP56Time2a));
+    else
+        memset (self, 0, sizeof(struct sCP56Time2a));
+
+    if (self != NULL)
+        CP56Time2a_setFromMsTimestamp(self, timestamp);
+
+    return self;
+}
+
+
+void
+CP56Time2a_setFromMsTimestamp(CP56Time2a self, uint64_t timestamp)
+{
+    memset(self->encodedValue, 0, 7);
+
+    time_t timeVal = timestamp / 1000;
+    int msPart = timestamp % 1000;
+
+    struct tm tmTime;
+
+    /* TODO replace with portable implementation */
+#ifdef _WIN32
+	gmtime_s(&tmTime, &timeVal);
+#else
+    gmtime_r(&timeVal, &tmTime);
+#endif
+
+    CP56Time2a_setMillisecond(self, msPart);
+
+    CP56Time2a_setSecond(self, tmTime.tm_sec);
+
+    CP56Time2a_setMinute(self, tmTime.tm_min);
+
+    CP56Time2a_setHour(self, tmTime.tm_hour);
+
+    CP56Time2a_setDayOfMonth(self, tmTime.tm_mday);
+
+    /* set day of week to 0 = not present */
+    CP56Time2a_setDayOfWeek(self, 0);
+
+    CP56Time2a_setMonth(self, tmTime.tm_mon + 1);
+
+    CP56Time2a_setYear(self, tmTime.tm_year);
+}
+
+
+uint64_t
+CP56Time2a_toMsTimestamp(const CP56Time2a self)
+{
+    struct tm tmTime;
+
+    tmTime.tm_sec = CP56Time2a_getSecond(self);
+    tmTime.tm_min = CP56Time2a_getMinute(self);
+    tmTime.tm_hour = CP56Time2a_getHour(self);
+    tmTime.tm_mday = CP56Time2a_getDayOfMonth(self);
+    tmTime.tm_mon = CP56Time2a_getMonth(self) - 1;
+    tmTime.tm_year = CP56Time2a_getYear(self) + 100;
+
+    time_t timestamp = my_mktime(&tmTime);
+
+    uint64_t msTimestamp = ((uint64_t) (timestamp * (uint64_t) 1000)) + CP56Time2a_getMillisecond(self);
+
+    return msTimestamp;
+}
+
+/* private */ bool
+CP56Time2a_getFromBuffer(CP56Time2a self, const uint8_t* msg, int msgSize, int startIndex)
+{
+    if (msgSize < startIndex + 7)
+        return false;
+
+    int i;
+
+    for (i = 0; i < 7; i++)
+        self->encodedValue[i] = msg[startIndex + i];
+
+    return true;
+}
+
+int
+CP56Time2a_getMillisecond(const CP56Time2a self)
+{
+    return getMillisecond(self->encodedValue);
+}
+
+void
+CP56Time2a_setMillisecond(CP56Time2a self, int value)
+{
+    setMillisecond(self->encodedValue, value);
+}
+
+int
+CP56Time2a_getSecond(const CP56Time2a self)
+{
+    return getSecond(self->encodedValue);
+}
+
+void
+CP56Time2a_setSecond(CP56Time2a self, int value)
+{
+    setSecond(self->encodedValue, value);
+}
+
+int
+CP56Time2a_getMinute(const CP56Time2a self)
+{
+    return getMinute(self->encodedValue);
+}
+
+void
+CP56Time2a_setMinute(CP56Time2a self, int value)
+{
+    setMinute(self->encodedValue, value);
+}
+
+int
+CP56Time2a_getHour(const CP56Time2a self)
+{
+    return (self->encodedValue[3] & 0x1f);
+}
+
+void
+CP56Time2a_setHour(CP56Time2a self, int value)
+{
+    self->encodedValue[3] = (uint8_t) ((self->encodedValue[3] & 0xe0) | (value & 0x1f));
+}
+
+int
+CP56Time2a_getDayOfWeek(const CP56Time2a self)
+{
+    return ((self->encodedValue[4] & 0xe0) >> 5);
+}
+
+void
+CP56Time2a_setDayOfWeek(CP56Time2a self, int value)
+{
+    self->encodedValue[4] = (uint8_t) ((self->encodedValue[4] & 0x1f) | ((value & 0x07) << 5));
+}
+
+int
+CP56Time2a_getDayOfMonth(const CP56Time2a self)
+{
+    return (self->encodedValue[4] & 0x1f);
+}
+
+void
+CP56Time2a_setDayOfMonth(CP56Time2a self, int value)
+{
+    self->encodedValue[4] = (uint8_t) ((self->encodedValue[4] & 0xe0) + (value & 0x1f));
+}
+
+int
+CP56Time2a_getMonth(const CP56Time2a self)
+{
+    return (self->encodedValue[5] & 0x0f);
+}
+
+void
+CP56Time2a_setMonth(CP56Time2a self, int value)
+{
+    self->encodedValue[5] = (uint8_t) ((self->encodedValue[5] & 0xf0) + (value & 0x0f));
+}
+
+int
+CP56Time2a_getYear(const CP56Time2a self)
+{
+    return (self->encodedValue[6] & 0x7f);
+}
+
+void
+CP56Time2a_setYear(CP56Time2a self, int value)
+{
+    value = value % 100;
+
+    self->encodedValue[6] = (uint8_t) ((self->encodedValue[6] & 0x80) + (value & 0x7f));
+}
+
+bool
+CP56Time2a_isSummerTime(const CP56Time2a self)
+{
+    return ((self->encodedValue[3] & 0x80) != 0);
+}
+
+void
+CP56Time2a_setSummerTime(CP56Time2a self, bool value)
+{
+    if (value)
+        self->encodedValue[3] |= 0x80;
+    else
+        self->encodedValue[3] &= 0x7f;
+}
+
+bool
+CP56Time2a_isInvalid(const CP56Time2a self)
+{
+    return isInvalid(self->encodedValue);
+}
+
+void
+CP56Time2a_setInvalid(CP56Time2a self, bool value)
+{
+    setInvalid(self->encodedValue, value);
+}
+
+bool
+CP56Time2a_isSubstituted(const CP56Time2a self)
+{
+    return isSubstituted(self->encodedValue);
+}
+
+void
+CP56Time2a_setSubstituted(CP56Time2a self, bool value)
+{
+    setSubstituted(self->encodedValue, value);
+}
+
+uint8_t*
+CP56Time2a_getEncodedValue(CP56Time2a self)
+{
+    return self->encodedValue;
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 1545 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_asdu.c


+ 163 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_bcr.c

@@ -0,0 +1,163 @@
+/*
+ *  bcr.c
+ *
+ *  Implementation of Binary Counter Reading (BCR) data type.
+ *
+ *  Copyright 2016 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "iec60870_common.h"
+
+#include <stdlib.h>
+
+#include "platform_endian.h"
+#include "apl_types_internal.h"
+#include "lib_memory.h"
+
+
+BinaryCounterReading
+BinaryCounterReading_create(BinaryCounterReading self, int32_t value, int seqNumber,
+        bool hasCarry, bool isAdjusted, bool isInvalid)
+{
+    if (self == NULL)
+        self = (BinaryCounterReading) GLOBAL_MALLOC(sizeof(struct sBinaryCounterReading));
+
+    if (self != NULL) {
+        BinaryCounterReading_setValue(self, value);
+        BinaryCounterReading_setSequenceNumber(self, seqNumber);
+        BinaryCounterReading_setCarry(self, hasCarry);
+        BinaryCounterReading_setAdjusted(self, isAdjusted);
+        BinaryCounterReading_setInvalid(self, isInvalid);
+    }
+
+    return self;
+}
+
+void
+BinaryCounterReading_destroy(BinaryCounterReading self)
+{
+    GLOBAL_FREEMEM(self);
+}
+
+int32_t
+BinaryCounterReading_getValue(BinaryCounterReading self)
+{
+    int32_t value;
+
+    uint8_t* valueBytes = (uint8_t*) &(value);
+    uint8_t* encodedValue = self->encodedValue;
+
+#if (ORDER_LITTLE_ENDIAN == 1)
+    valueBytes[0] = encodedValue[0];
+    valueBytes[1] = encodedValue[1];
+    valueBytes[2] = encodedValue[2];
+    valueBytes[3] = encodedValue[3];
+#else
+    valueBytes[0] = encodedValue[3];
+    valueBytes[1] = encodedValue[2];
+    valueBytes[2] = encodedValue[1];
+    valueBytes[3] = encodedValue[0];
+#endif
+
+    return value;
+}
+
+void
+BinaryCounterReading_setValue(BinaryCounterReading self, int32_t value)
+{
+    uint8_t* valueBytes = (uint8_t*) &(value);
+    uint8_t* encodedValue = self->encodedValue;
+
+#if (ORDER_LITTLE_ENDIAN == 1)
+    encodedValue[0] = valueBytes[0];
+    encodedValue[1] = valueBytes[1];
+    encodedValue[2] = valueBytes[2];
+    encodedValue[3] = valueBytes[3];
+#else
+    encodedValue[0] = valueBytes[3];
+    encodedValue[1] = valueBytes[2];
+    encodedValue[2] = valueBytes[1];
+    encodedValue[3] = valueBytes[0];
+#endif
+
+}
+
+
+int
+BinaryCounterReading_getSequenceNumber(BinaryCounterReading self)
+{
+    return (self->encodedValue[4] & 0x1f);
+}
+
+void
+BinaryCounterReading_setSequenceNumber(BinaryCounterReading self, int value)
+{
+    int seqNumber = value & 0x1f;
+    int flags = self->encodedValue[4] & 0xe0;
+
+    self->encodedValue[4] = flags | seqNumber;
+}
+
+bool
+BinaryCounterReading_hasCarry(BinaryCounterReading self)
+{
+    return ((self->encodedValue[4] & 0x20) == 0x20);
+}
+
+void
+BinaryCounterReading_setCarry(BinaryCounterReading self, bool value)
+{
+    if (value)
+        self->encodedValue[4] |= 0x20;
+    else
+        self->encodedValue[4] &= 0xdf;
+}
+
+bool
+BinaryCounterReading_isAdjusted(BinaryCounterReading self)
+{
+    return ((self->encodedValue[4] & 0x40) == 0x40);
+}
+
+void
+BinaryCounterReading_setAdjusted(BinaryCounterReading self, bool value)
+{
+    if (value)
+        self->encodedValue[4] |= 0x40;
+    else
+        self->encodedValue[4] &= 0xbf;
+}
+
+
+bool
+BinaryCounterReading_isInvalid(BinaryCounterReading self)
+{
+    return ((self->encodedValue[4] & 0x80) == 0x80);
+}
+
+void
+BinaryCounterReading_setInvalid(BinaryCounterReading self, bool value)
+{
+    if (value)
+        self->encodedValue[4] |= 0x80;
+    else
+        self->encodedValue[4] &= 0x7f;
+}
+

Datei-Diff unterdrückt, da er zu groß ist
+ 7707 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_information_objects.c


+ 531 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_master.c

@@ -0,0 +1,531 @@
+/*
+ *  cs101_master.c
+ *
+ *  Copyright 2017 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "buffer_frame.h"
+#include "lib_memory.h"
+#include "apl_types_internal.h"
+#include "lib60870_config.h"
+#include "lib60870_internal.h"
+#include "serial_transceiver_ft_1_2.h"
+#include "link_layer.h"
+#include "cs101_master.h"
+#include "cs101_queue.h"
+#include "cs101_asdu_internal.h"
+
+
+struct sCS101_Master
+{
+    SerialPort serialPort;
+
+    struct sLinkLayerParameters linkLayerParameters;
+    struct sCS101_AppLayerParameters alParameters;
+
+    SerialTransceiverFT12 transceiver;
+
+    IEC60870_LinkLayerMode linkLayerMode;
+
+    LinkLayerBalanced balancedLinkLayer;
+
+    LinkLayerPrimaryUnbalanced unbalancedLinkLayer;
+    int slaveAddress; /* address of the currently selected slave (unbalanced link layer only) */
+
+    CS101_ASDUReceivedHandler asduReceivedHandler;
+    void* asduReceivedHandlerParameter;
+
+    struct sCS101_Queue userDataQueue;
+
+#if (CONFIG_USE_THREADS == 1)
+    bool isRunning;
+    Thread workerThread;
+#endif
+};
+
+
+static struct sCS101_AppLayerParameters defaultAppLayerParameters = {
+    /* .sizeOfTypeId =  */ 1,
+    /* .sizeOfVSQ = */ 1,
+    /* .sizeOfCOT = */ 2,
+    /* .originatorAddress = */ 0,
+    /* .sizeOfCA = */ 2,
+    /* .sizeOfIOA = */ 3,
+    /* .maxSizeOfASDU = */ 249
+};
+
+/********************************************
+ * IBalancedApplicationLayer
+ ********************************************/
+static Frame
+IBalancedApplicationLayer_GetUserData (void* parameter, Frame frame)
+{
+    CS101_Master self = (CS101_Master) parameter;
+
+    Frame ret = NULL;
+
+    CS101_Queue_lock(&(self->userDataQueue));
+
+    ret = CS101_Queue_dequeue(&(self->userDataQueue), frame);
+
+    CS101_Queue_unlock(&(self->userDataQueue));
+
+    return ret;
+}
+
+static bool
+IBalancedApplicationLayer_HandleReceivedData (void* parameter, uint8_t* msg, bool isBroadcast, int userDataStart, int userDataLength)
+{
+    UNUSED_PARAMETER(isBroadcast);
+
+    CS101_Master self = (CS101_Master) parameter;
+
+    CS101_ASDU asdu = CS101_ASDU_createFromBuffer(&(self->alParameters), msg + userDataStart, userDataLength);
+
+    if (self->asduReceivedHandler)
+        self->asduReceivedHandler(self->asduReceivedHandlerParameter, 0, asdu);
+
+    CS101_ASDU_destroy(asdu);
+
+    return true;
+}
+
+static struct sIBalancedApplicationLayer cs101BalancedAppLayerInterface = {
+    IBalancedApplicationLayer_GetUserData,
+    IBalancedApplicationLayer_HandleReceivedData
+};
+
+/********************************************
+ * END IBalancedApplicationLayer
+ ********************************************/
+
+/********************************************
+ * IPrimaryApplicationLayer
+ ********************************************/
+
+static void
+IPrimaryApplicationLayer_AccessDemand(void* parameter, int slaveAddress)
+{
+    CS101_Master self = (CS101_Master) parameter;
+
+    DEBUG_PRINT ("MASTER: Access demand for slave %i\n", slaveAddress);
+
+    LinkLayerPrimaryUnbalanced_requestClass1Data(self->unbalancedLinkLayer, slaveAddress);
+}
+
+static void
+IPrimaryApplicationLayer_UserData(void* parameter, int slaveAddress, uint8_t* msg, int start, int length)
+{
+    CS101_Master self = (CS101_Master) parameter;
+
+    CS101_ASDU asdu = CS101_ASDU_createFromBuffer(&(self->alParameters), msg + start, length);
+
+    if (self->asduReceivedHandler)
+        self->asduReceivedHandler(self->asduReceivedHandlerParameter, slaveAddress, asdu);
+
+    CS101_ASDU_destroy(asdu);
+
+}
+
+static void
+IPrimaryApplicationLayer_Timeout (void* parameter, int slaveAddress)
+{
+    UNUSED_PARAMETER(parameter);
+    UNUSED_PARAMETER(slaveAddress);
+}
+
+static struct sIPrimaryApplicationLayer cs101UnbalancedAppLayerInterface = {
+    IPrimaryApplicationLayer_AccessDemand,
+    IPrimaryApplicationLayer_UserData,
+    IPrimaryApplicationLayer_Timeout
+};
+
+/********************************************
+ * END IPrimaryApplicationLayer
+ ********************************************/
+
+CS101_Master
+CS101_Master_createEx(SerialPort serialPort, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode,
+        int queueSize)
+{
+    CS101_Master self = (CS101_Master) GLOBAL_MALLOC(sizeof(struct sCS101_Master));
+
+    if (self != NULL) {
+
+        if (llParameters)
+            self->linkLayerParameters = *llParameters;
+        else {
+            self->linkLayerParameters.addressLength = 1;
+            self->linkLayerParameters.timeoutForAck = 200;
+            self->linkLayerParameters.timeoutRepeat = 1000;
+            self->linkLayerParameters.timeoutLinkState = 5000;
+            self->linkLayerParameters.useSingleCharACK = true;
+        }
+
+        if (alParameters)
+            self->alParameters = *alParameters;
+        else
+            self->alParameters = defaultAppLayerParameters;
+
+        self->transceiver = SerialTransceiverFT12_create(serialPort,  &(self->linkLayerParameters));
+
+        self->linkLayerMode = linkLayerMode;
+
+        if (linkLayerMode == IEC60870_LINK_LAYER_UNBALANCED) {
+
+            self->balancedLinkLayer = NULL;
+
+            self->unbalancedLinkLayer = LinkLayerPrimaryUnbalanced_create(self->transceiver,
+                    &(self->linkLayerParameters), &cs101UnbalancedAppLayerInterface, self);
+        }
+        else {
+            CS101_Queue_initialize(&(self->userDataQueue), queueSize);
+
+            self->unbalancedLinkLayer = NULL;
+
+            self->balancedLinkLayer = LinkLayerBalanced_create(0, self->transceiver,
+                    &(self->linkLayerParameters),
+                    &cs101BalancedAppLayerInterface, self);
+
+            LinkLayerBalanced_setDIR(self->balancedLinkLayer, true);
+        }
+
+        self->asduReceivedHandler = NULL;
+
+#if (CONFIG_USE_THREADS == 1)
+        self->isRunning = false;
+        self->workerThread = NULL;
+#endif
+
+    }
+
+    return self;
+}
+
+CS101_Master
+CS101_Master_create(SerialPort serialPort, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode)
+{
+    return CS101_Master_createEx(serialPort, llParameters, alParameters, linkLayerMode, CS101_MAX_QUEUE_SIZE);
+}
+
+void
+CS101_Master_run(CS101_Master self)
+{
+    if (self->unbalancedLinkLayer) {
+        LinkLayerPrimaryUnbalanced_run(self->unbalancedLinkLayer);
+    }
+    else {
+        LinkLayerBalanced_run(self->balancedLinkLayer);
+    }
+}
+
+#if (CONFIG_USE_THREADS == 1)
+static void*
+masterMainThread(void* parameter)
+{
+    CS101_Master self = (CS101_Master) parameter;
+
+    self->isRunning = true;
+
+    while (self->isRunning) {
+        CS101_Master_run(self);
+    }
+
+    return NULL;
+}
+#endif /* (CONFIG_USE_THREADS == 1) */
+
+void
+CS101_Master_start(CS101_Master self)
+{
+#if (CONFIG_USE_THREADS == 1)
+    if (self->workerThread == NULL) {
+        self->workerThread = Thread_create(masterMainThread, self, false);
+        Thread_start(self->workerThread);
+    }
+#endif /* (CONFIG_USE_THREADS == 1) */
+}
+
+void
+CS101_Master_stop(CS101_Master self)
+{
+#if (CONFIG_USE_THREADS == 1)
+    if (self->isRunning) {
+        self->isRunning = false;
+        Thread_destroy(self->workerThread);
+        self->workerThread = NULL;
+    }
+#endif /* (CONFIG_USE_THREADS == 1) */
+}
+
+
+void
+CS101_Master_destroy(CS101_Master self)
+{
+    if (self) {
+
+        if (self->balancedLinkLayer) {
+            LinkLayerBalanced_destroy(self->balancedLinkLayer);
+            CS101_Queue_dispose(&(self->userDataQueue));
+        }
+
+        if (self->unbalancedLinkLayer) {
+            LinkLayerPrimaryUnbalanced_destroy(self->unbalancedLinkLayer);
+        }
+
+        SerialTransceiverFT12_destroy(self->transceiver);
+
+        GLOBAL_FREEMEM(self);
+    }
+}
+
+void
+CS101_Master_setDIR(CS101_Master self, bool dir)
+{
+    if (self->balancedLinkLayer)
+        LinkLayerBalanced_setDIR(self->balancedLinkLayer, dir);
+}
+
+void
+CS101_Master_setOwnAddress(CS101_Master self, int address)
+{
+    if (self->balancedLinkLayer)
+        LinkLayerBalanced_setAddress(self->balancedLinkLayer, address);
+}
+
+void
+CS101_Master_useSlaveAddress(CS101_Master self, int address)
+{
+    self->slaveAddress = address;
+
+    if (self->balancedLinkLayer) {
+        LinkLayerBalanced_setOtherStationAddress(self->balancedLinkLayer, address);
+    }
+}
+
+LinkLayerParameters
+CS101_Master_getLinkLayerParameters(CS101_Master self)
+{
+    return &(self->linkLayerParameters);
+}
+
+CS101_AppLayerParameters
+CS101_Master_getAppLayerParameters(CS101_Master self)
+{
+    return &(self->alParameters);
+}
+
+
+bool
+CS101_Master_isChannelReady(CS101_Master self, int address)
+{
+    if (self->unbalancedLinkLayer)
+        return LinkLayerPrimaryUnbalanced_isChannelAvailable(self->unbalancedLinkLayer, address);
+
+    return false;
+}
+
+void
+CS101_Master_sendLinkLayerTestFunction(CS101_Master self)
+{
+    if (self->unbalancedLinkLayer)
+        LinkLayerPrimaryUnbalanced_sendLinkLayerTestFunction(self->unbalancedLinkLayer,
+                self->slaveAddress);
+    else if (self->balancedLinkLayer)
+        LinkLayerBalanced_sendLinkLayerTestFunction(self->balancedLinkLayer);
+}
+
+void
+CS101_Master_sendInterrogationCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, QualifierOfInterrogation qoi)
+{
+    sCS101_StaticASDU _asdu;
+
+    CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, cot, self->alParameters.originatorAddress, ca, false, false);
+
+    struct sInterrogationCommand _io;
+
+    InformationObject io = (InformationObject) InterrogationCommand_create(&_io, 0, qoi);
+
+    CS101_ASDU_addInformationObject(asdu, io);
+
+    CS101_Master_sendASDU(self, asdu);
+}
+
+void
+CS101_Master_sendCounterInterrogationCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, uint8_t qcc)
+{
+    sCS101_StaticASDU _asdu;
+
+    CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, cot, self->alParameters.originatorAddress, ca, false, false);
+
+    struct sCounterInterrogationCommand _io;
+
+    InformationObject io = (InformationObject) CounterInterrogationCommand_create(&_io, 0, qcc);
+
+    CS101_ASDU_addInformationObject(asdu, io);
+
+    CS101_Master_sendASDU(self, asdu);
+}
+
+void
+CS101_Master_sendReadCommand(CS101_Master self, int ca, int ioa)
+{
+    sCS101_StaticASDU _asdu;
+
+    CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, CS101_COT_REQUEST, self->alParameters.originatorAddress, ca, false, false);
+
+    struct sReadCommand _io;
+
+    InformationObject io = (InformationObject) ReadCommand_create(&_io, ioa);
+
+    CS101_ASDU_addInformationObject(asdu, io);
+
+    CS101_Master_sendASDU(self, asdu);
+}
+
+void
+CS101_Master_sendClockSyncCommand(CS101_Master self, int ca, CP56Time2a time)
+{
+    sCS101_StaticASDU _asdu;
+
+    CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, CS101_COT_ACTIVATION, self->alParameters.originatorAddress, ca, false, false);
+
+    struct sClockSynchronizationCommand _io;
+
+    InformationObject io = (InformationObject) ClockSynchronizationCommand_create(&_io, 0, time);
+
+    CS101_ASDU_addInformationObject(asdu, io);
+
+    CS101_Master_sendASDU(self, asdu);
+}
+
+void
+CS101_Master_sendTestCommand(CS101_Master self, int ca)
+{
+    sCS101_StaticASDU _asdu;
+
+    CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, CS101_COT_ACTIVATION, self->alParameters.originatorAddress, ca, false, false);
+
+    struct sTestCommand _io;
+
+    InformationObject io = (InformationObject) TestCommand_create(&_io);
+
+    CS101_ASDU_addInformationObject(asdu, io);
+
+    CS101_Master_sendASDU(self, asdu);
+}
+
+void
+CS101_Master_sendProcessCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, InformationObject command)
+{
+    sCS101_StaticASDU _asdu;
+
+    CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, cot, self->alParameters.originatorAddress, ca, false, false);
+
+    CS101_ASDU_addInformationObject(asdu, command);
+
+    CS101_Master_sendASDU(self, asdu);
+}
+
+static bool
+isBroadcastAddress(CS101_Master self, int address)
+{
+    if (self->linkLayerParameters.addressLength == 1) {
+        return (address == 255);
+    }
+    else if (self->linkLayerParameters.addressLength == 2) {
+        return (address == 65535);
+    }
+
+    return 0;
+}
+
+void
+CS101_Master_sendASDU(CS101_Master self, CS101_ASDU asdu)
+{
+    if (self->unbalancedLinkLayer) {
+
+        struct sBufferFrame bufferFrame;
+        uint8_t buffer[256];
+        BufferFrame_initialize(&bufferFrame, buffer, 0);
+
+        CS101_ASDU_encode(asdu, (Frame) &bufferFrame);
+
+        if (isBroadcastAddress(self, self->slaveAddress))
+            LinkLayerPrimaryUnbalanced_sendNoReply(self->unbalancedLinkLayer, self->slaveAddress, &bufferFrame);
+        else
+            LinkLayerPrimaryUnbalanced_sendConfirmed(self->unbalancedLinkLayer, self->slaveAddress, &bufferFrame);
+    }
+    else
+        CS101_Queue_enqueue(&(self->userDataQueue), asdu);
+}
+
+void
+CS101_Master_addSlave(CS101_Master self, int address)
+{
+    if (self->unbalancedLinkLayer)
+        LinkLayerPrimaryUnbalanced_addSlaveConnection(self->unbalancedLinkLayer, address);
+}
+
+void
+CS101_Master_pollSingleSlave(CS101_Master self, int address)
+{
+    if (self->unbalancedLinkLayer) {
+        LinkLayerPrimaryUnbalanced_requestClass2Data(self->unbalancedLinkLayer, address);
+    }
+}
+
+void
+CS101_Master_setASDUReceivedHandler(CS101_Master self, CS101_ASDUReceivedHandler handler, void* parameter)
+{
+    self->asduReceivedHandler = handler;
+    self->asduReceivedHandlerParameter = parameter;
+}
+
+void
+CS101_Master_setLinkLayerStateChanged(CS101_Master self, IEC60870_LinkLayerStateChangedHandler handler, void* parameter)
+{
+    if (self->linkLayerMode == IEC60870_LINK_LAYER_BALANCED) {
+        LinkLayerBalanced_setStateChangeHandler(self->balancedLinkLayer, handler, parameter);
+    }
+    else {
+        LinkLayerPrimaryUnbalanced_setStateChangeHandler(self->unbalancedLinkLayer, handler, parameter);
+    }
+}
+
+void
+CS101_Master_setRawMessageHandler(CS101_Master self, IEC60870_RawMessageHandler handler, void* parameter)
+{
+    SerialTransceiverFT12_setRawMessageHandler(self->transceiver, handler, parameter);
+}
+
+void
+CS101_Master_setIdleTimeout(CS101_Master self, int timeoutInMs)
+{
+    if (self->linkLayerMode == IEC60870_LINK_LAYER_BALANCED)
+        LinkLayerBalanced_setIdleTimeout(self->balancedLinkLayer, timeoutInMs);
+
+    /* unbalanced primary layer does not support automatic idle detection */
+}
+

+ 70 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_master_connection.c

@@ -0,0 +1,70 @@
+/*
+ *  cs101_master_connection.c
+ *
+ *  Copyright 2017 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "iec60870_slave.h"
+
+bool
+IMasterConnection_isReady(IMasterConnection self)
+{
+    return self->isReady(self);
+}
+
+bool
+IMasterConnection_sendASDU(IMasterConnection self, CS101_ASDU asdu)
+{
+    return self->sendASDU(self, asdu);
+}
+
+bool
+IMasterConnection_sendACT_CON(IMasterConnection self, CS101_ASDU asdu, bool negative)
+{
+    return self->sendACT_CON(self, asdu, negative);
+}
+
+bool
+IMasterConnection_sendACT_TERM(IMasterConnection self, CS101_ASDU asdu)
+{
+    return self->sendACT_TERM(self, asdu);
+}
+
+CS101_AppLayerParameters
+IMasterConnection_getApplicationLayerParameters(IMasterConnection self)
+{
+    return self->getApplicationLayerParameters(self);
+}
+
+void
+IMasterConnection_close(IMasterConnection self)
+{
+    if (self->close)
+        self->close(self);
+}
+
+int
+IMasterConnection_getPeerAddress(IMasterConnection self, char* addrBuf, int addrBufSize)
+{
+    if (self->getPeerAddress)
+        return self->getPeerAddress(self, addrBuf, addrBufSize);
+    else
+        return 0;
+}

+ 203 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_queue.c

@@ -0,0 +1,203 @@
+/*
+ *  cs101_queue.c
+ *
+ *  Copyright 2017-2018 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "buffer_frame.h"
+#include "lib_memory.h"
+#include "lib60870_config.h"
+#include "lib60870_internal.h"
+#include "apl_types_internal.h"
+#include "cs101_queue.h"
+
+/********************************************
+ * CS101_Queue
+ ********************************************/
+
+void
+CS101_Queue_initialize(CS101_Queue self, int maxQueueSize)
+{
+    self->entryCounter = 0;
+    self->firstMsgIndex = 0;
+    self->lastMsgIndex = 0;
+    self->size = maxQueueSize;
+
+    BufferFrame_initialize(&(self->encodeFrame), NULL, 0);
+
+#if (CS101_MAX_QUEUE_SIZE == -1)
+    int queueSize = maxQueueSize;
+
+    if (maxQueueSize == -1)
+        queueSize = 100;
+
+    self->elements = (CS101_QueueElement) GLOBAL_CALLOC(queueSize, sizeof(struct sCS101_QueueElement));
+
+    self->size = queueSize;
+#else
+    self->size = CS101_MAX_QUEUE_SIZE;
+#endif
+
+
+#if (CONFIG_USE_SEMAPHORES == 1)
+    self->queueLock = Semaphore_create();
+#endif
+}
+
+void
+CS101_Queue_dispose(CS101_Queue self)
+{
+#if (CONFIG_USE_SEMAPHORES == 1)
+    Semaphore_destroy(self->queueLock);
+#endif
+
+#if (CS101_MAX_QUEUE_SIZE == -1)
+    GLOBAL_FREEMEM(self->elements);
+#endif
+}
+
+void
+CS101_Queue_lock(CS101_Queue self)
+{
+#if (CONFIG_USE_SEMAPHORES == 1)
+    Semaphore_wait(self->queueLock);
+#endif
+}
+
+void
+CS101_Queue_unlock(CS101_Queue self)
+{
+#if (CONFIG_USE_SEMAPHORES == 1)
+    Semaphore_post(self->queueLock);
+#endif
+}
+
+void
+CS101_Queue_enqueue(CS101_Queue self, CS101_ASDU asdu)
+{
+    CS101_Queue_lock(self);
+
+    int nextIndex;
+    bool removeEntry = false;
+
+    if (self->entryCounter == 0) {
+        self->firstMsgIndex = 0;
+        nextIndex = 0;
+    }
+    else
+        nextIndex = self->lastMsgIndex + 1;
+
+    if (nextIndex == self->size)
+        nextIndex = 0;
+
+    if (self->entryCounter == self->size)
+        removeEntry = true;
+
+    if (removeEntry == false) {
+        DEBUG_PRINT("add entry (nextIndex:%i)\n", nextIndex);
+        self->lastMsgIndex = nextIndex;
+        self->entryCounter++;
+    }
+    else {
+        DEBUG_PRINT("add entry (nextIndex:%i) -> remove oldest\n", nextIndex);
+
+        /* remove oldest entry */
+        self->lastMsgIndex = nextIndex;
+
+        int firstIndex = nextIndex + 1;
+
+        if (firstIndex == self->size)
+            firstIndex = 0;
+
+        self->firstMsgIndex = firstIndex;
+    }
+
+    self->encodeFrame.buffer = self->elements[nextIndex].buffer;
+    self->encodeFrame.startSize = 0;
+    self->encodeFrame.msgSize = 0;
+
+    CS101_ASDU_encode(asdu, (Frame)&(self->encodeFrame));
+
+    int srcSize = self->encodeFrame.msgSize;
+    self->elements[nextIndex].size = srcSize;
+
+    DEBUG_PRINT("Events in FIFO: %i (first: %i, last: %i)\n", self->entryCounter,
+            self->firstMsgIndex, self->lastMsgIndex);
+
+    CS101_Queue_unlock(self);
+}
+
+
+/*
+ * NOTE: Locking has to be done by caller!
+ */
+Frame
+CS101_Queue_dequeue(CS101_Queue self, Frame resultStorage)
+{
+    Frame frame = NULL;
+
+    if (self->entryCounter != 0) {
+
+        if (resultStorage) {
+            frame = resultStorage;
+
+            int currentIndex = self->firstMsgIndex;
+
+            Frame_appendBytes(frame, self->elements[currentIndex].buffer, self->elements[currentIndex].size);
+
+            self->firstMsgIndex = (currentIndex + 1) % self->size;
+            self->entryCounter--;
+        }
+    }
+
+    return frame;
+}
+
+bool
+CS101_Queue_isFull(CS101_Queue self)
+{
+   return (self->entryCounter == self->size);
+}
+
+bool
+CS101_Queue_isEmpty(CS101_Queue self)
+{
+   return (self->entryCounter == 0);
+}
+
+
+void
+CS101_Queue_flush(CS101_Queue self)
+{
+    CS101_Queue_lock(self);
+    self->entryCounter = 0;
+    self->firstMsgIndex = 0;
+    self->lastMsgIndex = 0;
+    CS101_Queue_unlock(self);
+}
+
+
+/********************************************
+ * END CS101_Queue
+ ********************************************/

+ 874 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs101/cs101_slave.c

@@ -0,0 +1,874 @@
+/*
+ *  cs101_slave.c
+ *
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "cs101_slave.h"
+#include "buffer_frame.h"
+#include "lib_memory.h"
+#include "apl_types_internal.h"
+#include "iec60870_slave.h"
+#include "lib60870_config.h"
+#include "lib60870_internal.h"
+#include "serial_transceiver_ft_1_2.h"
+#include "link_layer.h"
+#include "cs101_queue.h"
+#include "cs101_asdu_internal.h"
+#include "linked_list.h"
+
+#if ((CONFIG_USE_THREADS == 1) || (CONFIG_USE_SEMAPHORES == 1))
+#include "hal_thread.h"
+#endif
+
+struct sCS101_Slave
+{
+    CS101_InterrogationHandler interrogationHandler;
+    void* interrogationHandlerParameter;
+
+    CS101_CounterInterrogationHandler counterInterrogationHandler;
+    void* counterInterrogationHandlerParameter;
+
+    CS101_ReadHandler readHandler;
+    void* readHandlerParameter;
+
+    CS101_ClockSynchronizationHandler clockSyncHandler;
+    void* clockSyncHandlerParameter;
+
+    CS101_ResetProcessHandler resetProcessHandler;
+    void* resetProcessHandlerParameter;
+
+    CS101_DelayAcquisitionHandler delayAcquisitionHandler;
+    void* delayAcquisitionHandlerParameter;
+
+    CS101_ASDUHandler asduHandler;
+    void* asduHandlerParameter;
+
+    CS101_ResetCUHandler resetCUHandler;
+    void* resetCUHandlerParameter;
+
+    SerialTransceiverFT12 transceiver;
+
+    LinkLayerSecondaryUnbalanced unbalancedLinkLayer;
+    LinkLayerBalanced balancedLinkLayer;
+
+    struct sLinkLayerParameters linkLayerParameters;
+
+    struct sCS101_AppLayerParameters alParameters;
+
+    struct sCS101_Queue userDataClass1Queue;
+
+    struct sCS101_Queue userDataClass2Queue;
+
+    struct sIMasterConnection iMasterConnection;
+
+    IEC60870_LinkLayerMode linkLayerMode;
+
+#if (CONFIG_USE_THREADS == 1)
+    bool isRunning;
+    Thread workerThread;
+#endif
+
+    LinkedList plugins;
+};
+
+static void
+handleASDU(CS101_Slave self, CS101_ASDU asdu);
+
+/********************************************
+ * ISecondaryApplicationLayer
+ ********************************************/
+
+static bool
+IsClass1DataAvailable(void* parameter)
+{
+    CS101_Slave self = (CS101_Slave) parameter;
+
+    return (CS101_Queue_isEmpty(&(self->userDataClass1Queue)) == false);
+}
+
+static Frame
+GetClass1Data(void* parameter, Frame frame)
+{
+    CS101_Slave self = (CS101_Slave) parameter;
+
+    CS101_Queue_lock(&(self->userDataClass1Queue));
+
+    Frame userData = CS101_Queue_dequeue(&(self->userDataClass1Queue), frame);
+
+    CS101_Queue_unlock(&(self->userDataClass1Queue));
+
+    return userData;
+}
+
+static Frame
+GetClass2Data(void* parameter, Frame frame)
+{
+    CS101_Slave self = (CS101_Slave) parameter;
+
+    CS101_Queue_lock(&(self->userDataClass2Queue));
+
+    Frame userData = CS101_Queue_dequeue(&(self->userDataClass2Queue), frame);
+
+    CS101_Queue_unlock(&(self->userDataClass2Queue));
+
+    return userData;
+}
+
+static bool
+HandleReceivedData(void* parameter, uint8_t* msg, bool isBroadcast, int userDataStart, int userDataLength)
+{
+    UNUSED_PARAMETER(isBroadcast);
+
+    CS101_Slave self = (CS101_Slave) parameter;
+
+    CS101_ASDU asdu = CS101_ASDU_createFromBuffer(&(self->alParameters), msg + userDataStart, userDataLength);
+
+    if (asdu) {
+        handleASDU(self, asdu);
+
+        CS101_ASDU_destroy(asdu);
+    }
+    else {
+        DEBUG_PRINT("CS101 slave: Failed to parse ASDU\n");
+    }
+
+    return true;
+}
+
+static void
+ResetCUReceived(void* parameter, bool onlyFCB)
+{
+    CS101_Slave self = (CS101_Slave) parameter;
+
+    if (onlyFCB) {
+        DEBUG_PRINT("CS101 slave: Reset FCB received\n");
+
+    }
+    else {
+        DEBUG_PRINT("CS101 slave: Reset CU received\n");
+
+        if (self->resetCUHandler)
+            self->resetCUHandler(self->resetCUHandlerParameter);
+
+    }
+}
+
+static struct sISecondaryApplicationLayer cs101UnbalancedAppLayerInterface = {
+        IsClass1DataAvailable,
+        GetClass1Data,
+        GetClass2Data,
+        HandleReceivedData,
+        ResetCUReceived
+};
+
+/********************************************
+ * END ISecondaryApplicationLayer
+ ********************************************/
+
+/********************************************
+ * IBalancedApplicationLayer
+ ********************************************/
+static bool
+IsClass2DataAvailable(void* parameter)
+{
+    CS101_Slave self = (CS101_Slave) parameter;
+
+    return (CS101_Queue_isEmpty(&(self->userDataClass2Queue)) == false);
+}
+
+static Frame
+IBalancedApplicationLayer_GetUserData(void* parameter, Frame frame)
+{
+    if (IsClass1DataAvailable(parameter))
+        return GetClass1Data(parameter, frame);
+    else if (IsClass2DataAvailable(parameter))
+        return GetClass2Data(parameter, frame);
+    else
+        return NULL;
+}
+
+static bool
+IBalancedApplicationLayer_HandleReceivedData(void* parameter, uint8_t* msg, bool isBroadcast, int userDataStart, int userDataLength)
+{
+    return HandleReceivedData(parameter, msg, isBroadcast, userDataStart, userDataLength);
+}
+
+static struct sIBalancedApplicationLayer cs101BalancedAppLayerInterface = {
+    IBalancedApplicationLayer_GetUserData,
+    IBalancedApplicationLayer_HandleReceivedData
+};
+
+/********************************************
+ * END IBalancedApplicationLayer
+ ********************************************/
+
+/********************************************
+ * IMasterConnection
+ *******************************************/
+
+static bool
+isReady(IMasterConnection self)
+{
+    CS101_Slave slave = (CS101_Slave) self->object;
+
+    if (CS101_Slave_isClass1QueueFull(slave))
+        return false;
+    else
+        return true;
+}
+
+static bool
+sendASDU(IMasterConnection self, CS101_ASDU asdu)
+{
+    CS101_Slave slave = (CS101_Slave) self->object;
+
+    CS101_Slave_enqueueUserDataClass1(slave, asdu);
+
+    return true;
+}
+
+static bool
+sendACT_CON(IMasterConnection self, CS101_ASDU asdu, bool negative)
+{
+    CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
+    CS101_ASDU_setNegative(asdu, negative);
+
+    return sendASDU(self, asdu);
+}
+
+static bool
+sendACT_TERM(IMasterConnection self, CS101_ASDU asdu)
+{
+    CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_TERMINATION);
+    CS101_ASDU_setNegative(asdu, false);
+
+    return sendASDU(self, asdu);
+}
+
+static CS101_AppLayerParameters
+getApplicationLayerParameters(IMasterConnection self)
+{
+    CS101_Slave slave = (CS101_Slave) self->object;
+
+    return &(slave->alParameters);
+}
+
+/********************************************
+ * END IMasterConnection
+ *******************************************/
+
+static struct sCS101_AppLayerParameters defaultAppLayerParameters = {
+    /* .sizeOfTypeId =  */ 1,
+    /* .sizeOfVSQ = */ 1,
+    /* .sizeOfCOT = */ 2,
+    /* .originatorAddress = */ 0,
+    /* .sizeOfCA = */ 2,
+    /* .sizeOfIOA = */ 3,
+    /* .maxSizeOfASDU = */ 249
+};
+
+CS101_Slave
+CS101_Slave_createEx(SerialPort serialPort, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode,
+        int class1QueueSize, int class2QueueSize)
+{
+    CS101_Slave self = (CS101_Slave) GLOBAL_MALLOC(sizeof(struct sCS101_Slave));
+
+    if (self != NULL) {
+
+        self->asduHandler = NULL;
+        self->interrogationHandler = NULL;
+        self->counterInterrogationHandler = NULL;
+        self->readHandler = NULL;
+        self->clockSyncHandler = NULL;
+        self->resetProcessHandler = NULL;
+        self->delayAcquisitionHandler = NULL;
+        self->resetCUHandler = NULL;
+
+#if (CONFIG_USE_THREADS == 1)
+        self->isRunning = false;
+        self->workerThread = NULL;
+#endif
+
+        if (llParameters)
+            self->linkLayerParameters = *llParameters;
+        else {
+            self->linkLayerParameters.addressLength = 1;
+            self->linkLayerParameters.timeoutForAck = 200;
+            self->linkLayerParameters.timeoutRepeat = 1000;
+            self->linkLayerParameters.useSingleCharACK = true;
+        }
+
+        if (alParameters)
+            self->alParameters = *alParameters;
+        else {
+            self->alParameters = defaultAppLayerParameters;
+        }
+
+        self->transceiver = SerialTransceiverFT12_create(serialPort,  &(self->linkLayerParameters));
+
+        self->linkLayerMode = linkLayerMode;
+
+        if (linkLayerMode == IEC60870_LINK_LAYER_UNBALANCED) {
+
+            self->balancedLinkLayer = NULL;
+
+            self->unbalancedLinkLayer = LinkLayerSecondaryUnbalanced_create(0, self->transceiver,
+                    &(self->linkLayerParameters),
+                    &cs101UnbalancedAppLayerInterface, self);
+
+        }
+        else {
+
+            self->unbalancedLinkLayer = NULL;
+
+            self->balancedLinkLayer = LinkLayerBalanced_create(0, self->transceiver,
+                    &(self->linkLayerParameters),
+                    &cs101BalancedAppLayerInterface, self);
+
+        }
+
+        self->iMasterConnection.isReady = isReady;
+        self->iMasterConnection.sendASDU = sendASDU;
+        self->iMasterConnection.sendACT_CON = sendACT_CON;
+        self->iMasterConnection.sendACT_TERM = sendACT_TERM;
+        self->iMasterConnection.getApplicationLayerParameters = getApplicationLayerParameters;
+        self->iMasterConnection.close = NULL;
+        self->iMasterConnection.getPeerAddress = NULL;
+        self->iMasterConnection.object = self;
+
+        CS101_Queue_initialize(&(self->userDataClass1Queue), class1QueueSize);
+        CS101_Queue_initialize(&(self->userDataClass2Queue), class2QueueSize);
+
+        self->plugins = NULL;
+    }
+
+    return self;
+}
+
+CS101_Slave
+CS101_Slave_create(SerialPort serialPort, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode)
+{
+    return CS101_Slave_createEx(serialPort, llParameters, alParameters, linkLayerMode, CS101_MAX_QUEUE_SIZE, CS101_MAX_QUEUE_SIZE);
+}
+
+void
+CS101_Slave_destroy(CS101_Slave self)
+{
+    if (self != NULL) {
+
+        if (self->unbalancedLinkLayer)
+            LinkLayerSecondaryUnbalanced_destroy(self->unbalancedLinkLayer);
+
+        if (self->balancedLinkLayer)
+            LinkLayerBalanced_destroy(self->balancedLinkLayer);
+
+        SerialTransceiverFT12_destroy(self->transceiver);
+
+        CS101_Queue_dispose(&(self->userDataClass1Queue));
+        CS101_Queue_dispose(&(self->userDataClass2Queue));
+
+        if (self->plugins) {
+            LinkedList_destroyStatic(self->plugins);
+        }
+
+        GLOBAL_FREEMEM(self);
+    }
+}
+
+void
+CS101_Slave_addPlugin(CS101_Slave self, CS101_SlavePlugin plugin)
+{
+    if (self->plugins == NULL)
+        self->plugins = LinkedList_create();
+
+    if (self->plugins)
+        LinkedList_add(self->plugins, plugin);
+}
+
+void
+CS101_Slave_setDIR(CS101_Slave self, bool dir)
+{
+    if (self->linkLayerMode == IEC60870_LINK_LAYER_BALANCED) {
+        LinkLayerBalanced_setDIR(self->balancedLinkLayer, dir);
+    }
+}
+
+void
+CS101_Slave_setIdleTimeout(CS101_Slave self, int timeoutInMs)
+{
+    if (self->linkLayerMode == IEC60870_LINK_LAYER_UNBALANCED)
+        LinkLayerSecondaryUnbalanced_setIdleTimeout(self->unbalancedLinkLayer, timeoutInMs);
+    else
+        LinkLayerBalanced_setIdleTimeout(self->balancedLinkLayer, timeoutInMs);
+}
+
+void
+CS101_Slave_setLinkLayerStateChanged(CS101_Slave self, IEC60870_LinkLayerStateChangedHandler handler, void* parameter)
+{
+    if (self->linkLayerMode == IEC60870_LINK_LAYER_UNBALANCED) {
+        LinkLayerSecondaryUnbalanced_setStateChangeHandler(self->unbalancedLinkLayer, handler, parameter);
+    }
+    else {
+        LinkLayerBalanced_setStateChangeHandler(self->balancedLinkLayer, handler, parameter);
+    }
+}
+
+void
+CS101_Slave_setLinkLayerAddress(CS101_Slave self, int address)
+{
+    if (self->linkLayerMode == IEC60870_LINK_LAYER_UNBALANCED)
+        LinkLayerSecondaryUnbalanced_setAddress(self->unbalancedLinkLayer, address);
+    else
+        LinkLayerBalanced_setAddress(self->balancedLinkLayer, address);
+}
+
+void
+CS101_Slave_setLinkLayerAddressOtherStation(CS101_Slave self, int address)
+{
+    if (self->balancedLinkLayer)
+        LinkLayerBalanced_setOtherStationAddress(self->balancedLinkLayer, address);
+}
+
+bool
+CS101_Slave_isClass1QueueFull(CS101_Slave self)
+{
+    return CS101_Queue_isFull(&(self->userDataClass1Queue));
+}
+
+void
+CS101_Slave_enqueueUserDataClass1(CS101_Slave self, CS101_ASDU asdu)
+{
+    CS101_Queue_enqueue(&(self->userDataClass1Queue), asdu);
+}
+
+
+bool
+CS101_Slave_isClass2QueueFull(CS101_Slave self)
+{
+    return CS101_Queue_isFull(&(self->userDataClass2Queue));
+}
+
+void
+CS101_Slave_enqueueUserDataClass2(CS101_Slave self, CS101_ASDU asdu)
+{
+    CS101_Queue_enqueue(&(self->userDataClass2Queue), asdu);
+}
+
+void
+CS101_Slave_flushQueues(CS101_Slave self)
+{
+    CS101_Queue_flush(&(self->userDataClass1Queue));
+    CS101_Queue_flush(&(self->userDataClass2Queue));
+}
+
+void
+CS101_Slave_run(CS101_Slave self)
+{
+
+    if (self->unbalancedLinkLayer)
+        LinkLayerSecondaryUnbalanced_run(self->unbalancedLinkLayer);
+    else
+        LinkLayerBalanced_run(self->balancedLinkLayer);
+
+    /* call plugins */
+    if (self->plugins) {
+
+        LinkedList pluginElem = LinkedList_getNext(self->plugins);
+
+        while (pluginElem) {
+
+            CS101_SlavePlugin plugin = (CS101_SlavePlugin) LinkedList_getData(pluginElem);
+
+            plugin->runTask(plugin->parameter, &(self->iMasterConnection));
+
+            pluginElem = LinkedList_getNext(pluginElem);
+        }
+    }
+}
+
+#if (CONFIG_USE_THREADS == 1)
+static void*
+slaveMainThread(void* parameter)
+{
+    CS101_Slave self = (CS101_Slave) parameter;
+
+    self->isRunning = true;
+
+    while (self->isRunning) {
+        CS101_Slave_run(self);
+    }
+
+    return NULL;
+}
+#endif /* (CONFIG_USE_THREADS == 1) */
+
+void
+CS101_Slave_start(CS101_Slave self)
+{
+#if (CONFIG_USE_THREADS == 1)
+    if (self->workerThread == NULL) {
+        self->workerThread = Thread_create(slaveMainThread, self, false);
+        Thread_start(self->workerThread);
+    }
+#endif /* (CONFIG_USE_THREADS == 1) */
+}
+
+void
+CS101_Slave_stop(CS101_Slave self)
+{
+#if (CONFIG_USE_THREADS == 1)
+    if (self->isRunning) {
+        self->isRunning = false;
+        Thread_destroy(self->workerThread);
+        self->workerThread = NULL;
+    }
+#endif /* (CONFIG_USE_THREADS == 1) */
+}
+
+
+
+CS101_AppLayerParameters
+CS101_Slave_getAppLayerParameters(CS101_Slave self)
+{
+    return &(self->alParameters);
+}
+
+LinkLayerParameters
+CS101_Slave_getLinkLayerParameters(CS101_Slave self)
+{
+    return &(self->linkLayerParameters);
+}
+
+void
+CS101_Slave_setResetCUHandler(CS101_Slave self, CS101_ResetCUHandler handler, void* parameter)
+{
+    self->resetCUHandler = handler;
+    self->resetCUHandlerParameter = parameter;
+}
+
+void
+CS101_Slave_setInterrogationHandler(CS101_Slave self, CS101_InterrogationHandler handler, void*  parameter)
+{
+    self->interrogationHandler = handler;
+    self->interrogationHandlerParameter = parameter;
+}
+
+void
+CS101_Slave_setCounterInterrogationHandler(CS101_Slave self, CS101_CounterInterrogationHandler handler, void*  parameter)
+{
+    self->counterInterrogationHandler = handler;
+    self->counterInterrogationHandlerParameter = parameter;
+}
+
+void
+CS101_Slave_setReadHandler(CS101_Slave self, CS101_ReadHandler handler, void* parameter)
+{
+    self->readHandler = handler;
+    self->readHandlerParameter = parameter;
+}
+
+void
+CS101_Slave_setASDUHandler(CS101_Slave self, CS101_ASDUHandler handler, void* parameter)
+{
+    self->asduHandler = handler;
+    self->asduHandlerParameter = parameter;
+}
+
+void
+CS101_Slave_setClockSyncHandler(CS101_Slave self, CS101_ClockSynchronizationHandler handler, void* parameter)
+{
+    self->clockSyncHandler = handler;
+    self->clockSyncHandlerParameter = parameter;
+}
+
+void
+CS101_Slave_setResetProcessHandler(CS101_Slave self, CS101_ResetProcessHandler handler, void* parameter)
+{
+    self->resetProcessHandler = handler;
+    self->resetProcessHandlerParameter = parameter;
+}
+
+void
+CS101_Slave_setDelayAcquisitionHandler(CS101_Slave self, CS101_DelayAcquisitionHandler handler, void* parameter)
+{
+    self->delayAcquisitionHandler = handler;
+    self->delayAcquisitionHandlerParameter = parameter;
+}
+
+static void
+responseCOTUnknown(CS101_ASDU asdu, IMasterConnection self)
+{
+    DEBUG_PRINT("  with unknown COT\n");
+    CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);
+    CS101_ASDU_setNegative(asdu, true);
+    sendASDU(self, asdu);
+}
+
+
+/*
+ * Handle received ASDUs
+ *
+ * Call the appropriate callbacks according to ASDU type and CoT
+ */
+static void
+handleASDU(CS101_Slave self, CS101_ASDU asdu)
+{
+    bool messageHandled = false;
+
+    /* call plugins */
+    if (self->plugins) {
+        LinkedList pluginElem = LinkedList_getNext(self->plugins);
+
+        while (pluginElem) {
+
+            CS101_SlavePlugin plugin = (CS101_SlavePlugin) LinkedList_getData(pluginElem);
+
+            CS101_SlavePlugin_Result result = plugin->handleAsdu(plugin->parameter, &(self->iMasterConnection), asdu);
+
+            if (result == CS101_PLUGIN_RESULT_HANDLED) {
+                return;
+            }
+            else if (result == CS101_PLUGIN_RESULT_INVALID_ASDU) {
+                DEBUG_PRINT("Invalid message");
+            }
+
+            pluginElem = LinkedList_getNext(pluginElem);
+        }
+    }
+
+    uint8_t cot = CS101_ASDU_getCOT(asdu);
+
+    switch (CS101_ASDU_getTypeID(asdu)) {
+
+    case C_IC_NA_1: /* 100 - interrogation command */
+
+        DEBUG_PRINT("Rcvd interrogation command C_IC_NA_1\n");
+
+        if ((cot == CS101_COT_ACTIVATION) || (cot == CS101_COT_DEACTIVATION)) {
+            if (self->interrogationHandler != NULL) {
+
+                union uInformationObject _io;
+
+                InterrogationCommand irc = (InterrogationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0);
+
+                if (irc) {
+                    if (self->interrogationHandler(self->interrogationHandlerParameter,
+                            &(self->iMasterConnection), asdu, InterrogationCommand_getQOI(irc)))
+                        messageHandled = true;
+                }
+                else {
+                    DEBUG_PRINT("Invalid message");
+                }
+            }
+        }
+        else
+            responseCOTUnknown(asdu, &(self->iMasterConnection));
+
+        break;
+
+    case C_CI_NA_1: /* 101 - counter interrogation command */
+
+        DEBUG_PRINT("Rcvd counter interrogation command C_CI_NA_1\n");
+
+        if ((cot == CS101_COT_ACTIVATION) || (cot == CS101_COT_DEACTIVATION)) {
+
+            if (self->counterInterrogationHandler != NULL) {
+
+                union uInformationObject _io;
+
+                CounterInterrogationCommand cic = (CounterInterrogationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0);
+
+                if (cic) {
+                    if (self->counterInterrogationHandler(self->counterInterrogationHandlerParameter,
+                            &(self->iMasterConnection), asdu, CounterInterrogationCommand_getQCC(cic)))
+                        messageHandled = true;
+                }
+                else {
+                    DEBUG_PRINT("Invalid message");
+                    return;
+                }
+            }
+        }
+        else
+            responseCOTUnknown(asdu, &(self->iMasterConnection));
+
+        break;
+
+    case C_RD_NA_1: /* 102 - read command */
+
+        DEBUG_PRINT("Rcvd read command C_RD_NA_1\n");
+
+        if (cot == CS101_COT_REQUEST) {
+            if (self->readHandler != NULL) {
+
+                union uInformationObject _io;
+
+                ReadCommand rc = (ReadCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0);
+
+                if (rc) {
+                    if (self->readHandler(self->readHandlerParameter,
+                            &(self->iMasterConnection), asdu, InformationObject_getObjectAddress((InformationObject) rc)))
+                        messageHandled = true;
+                }
+                else {
+                    DEBUG_PRINT("Invalid message");
+                }
+            }
+        }
+        else
+            responseCOTUnknown(asdu, &(self->iMasterConnection));
+
+        break;
+
+    case C_CS_NA_1: /* 103 - Clock synchronization command */
+
+        DEBUG_PRINT("Rcvd clock sync command C_CS_NA_1\n");
+
+        if (cot == CS101_COT_ACTIVATION) {
+
+            if (self->clockSyncHandler != NULL) {
+
+                union uInformationObject _io;
+
+                ClockSynchronizationCommand csc = (ClockSynchronizationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0);
+
+                if (csc) {
+                    if (self->clockSyncHandler(self->clockSyncHandlerParameter,
+                            &(self->iMasterConnection), asdu, ClockSynchronizationCommand_getTime(csc)))
+                        messageHandled = true;
+
+                    if (messageHandled) {
+                        /* send ACT-CON message */
+                        CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
+
+                        CS101_Slave_enqueueUserDataClass1(self, asdu);
+                    }
+                }
+                else {
+                    DEBUG_PRINT("Invalid message");
+                }
+            }
+        }
+        else
+            responseCOTUnknown(asdu, &(self->iMasterConnection));
+
+        break;
+
+    case C_TS_NA_1: /* 104 - test command */
+
+        DEBUG_PRINT("Rcvd test command C_TS_NA_1\n");
+
+        if (cot != CS101_COT_ACTIVATION) {
+            CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);
+            CS101_ASDU_setNegative(asdu, true);
+        }
+        else
+            CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
+
+        CS101_Slave_enqueueUserDataClass1(self, asdu);
+
+        messageHandled = true;
+
+        break;
+
+    case C_RP_NA_1: /* 105 - Reset process command */
+
+        DEBUG_PRINT("Rcvd reset process command C_RP_NA_1\n");
+
+        if (cot == CS101_COT_ACTIVATION) {
+
+            if (self->resetProcessHandler != NULL) {
+
+                union uInformationObject _io;
+
+                ResetProcessCommand rpc = (ResetProcessCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0);
+
+                if (rpc) {
+
+                    if (self->resetProcessHandler(self->resetProcessHandlerParameter,
+                            &(self->iMasterConnection), asdu, ResetProcessCommand_getQRP(rpc)))
+                        messageHandled = true;
+                }
+                else {
+                    DEBUG_PRINT("Invalid reset-process-command message");
+                }
+            }
+
+        }
+        else
+            responseCOTUnknown(asdu, &(self->iMasterConnection));
+
+        break;
+
+    case C_CD_NA_1: /* 106 - Delay acquisition command */
+
+        DEBUG_PRINT("Rcvd delay acquisition command C_CD_NA_1\n");
+
+        if ((cot == CS101_COT_ACTIVATION) || (cot == CS101_COT_SPONTANEOUS)) {
+
+            if (self->delayAcquisitionHandler != NULL) {
+
+                union uInformationObject _io;
+
+                DelayAcquisitionCommand dac = (DelayAcquisitionCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0);
+
+                if (dac) {
+                    if (self->delayAcquisitionHandler(self->delayAcquisitionHandlerParameter,
+                            &(self->iMasterConnection), asdu, DelayAcquisitionCommand_getDelay(dac)))
+                        messageHandled = true;
+                }
+                else {
+                    DEBUG_PRINT("Invalid message");
+                }
+            }
+        }
+        else
+            responseCOTUnknown(asdu, &(self->iMasterConnection));
+
+        break;
+
+
+    default: /* no special handler available -> use default handler */
+        break;
+    }
+
+    if ((messageHandled == false) && (self->asduHandler != NULL))
+        if (self->asduHandler(self->asduHandlerParameter, &(self->iMasterConnection), asdu))
+            messageHandled = true;
+
+    if (messageHandled == false) {
+        /* send error response */
+        CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_TYPE_ID);
+        CS101_ASDU_setNegative(asdu, true);
+        CS101_Slave_enqueueUserDataClass1(self, asdu);
+    }
+}
+
+void
+CS101_Slave_setRawMessageHandler(CS101_Slave self, IEC60870_RawMessageHandler handler, void* parameter)
+{
+    SerialTransceiverFT12_setRawMessageHandler(self->transceiver, handler, parameter);
+}
+

Datei-Diff unterdrückt, da er zu groß ist
+ 1365 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs104/cs104_connection.c


+ 210 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs104/cs104_frame.c

@@ -0,0 +1,210 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "cs104_frame.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "frame.h"
+#include "lib60870_internal.h"
+#include "lib_memory.h"
+
+#ifndef CONFIG_LIB60870_STATIC_FRAMES
+#define CONFIG_LIB60870_STATIC_FRAMES 0
+#endif
+
+
+struct sT104Frame {
+    FrameVFT virtualFunctionTable;
+
+    uint8_t buffer[256];
+    int msgSize;
+
+#if (CONFIG_LIB60870_STATIC_FRAMES == 1)
+    /* TODO move to base class? */
+    uint8_t allocated;
+#endif
+};
+
+static struct sFrameVFT t104FrameVFT = {
+        T104Frame_destroy,
+        T104Frame_resetFrame,
+        T104Frame_setNextByte,
+        T104Frame_appendBytes,
+        T104Frame_getMsgSize,
+        T104Frame_getBuffer,
+        T104Frame_getSpaceLeft
+};
+
+#if (CONFIG_LIB60870_STATIC_FRAMES == 1)
+
+static int staticFramesInitialized = 0;
+
+#ifndef CONFIG_LIB60870_MAX_FRAMES
+#define CONFIG_LIB60870_MAX_FRAMES 10
+#endif
+
+struct sT104Frame staticFrames[CONFIG_LIB60870_MAX_FRAMES];
+
+static void
+initializeFrames(void)
+{
+    int i;
+
+    for (i = 0; i < CONFIG_LIB60870_MAX_FRAMES; i++) {
+        staticFrames[i].virtualFunctionTable = &t104FrameVFT;
+        staticFrames[i].allocated = 0;
+        staticFrames[i].buffer[0] = 0x68;
+    }
+}
+
+static T104Frame
+getNextFreeFrame(void)
+{
+    int i;
+
+    for (i = 0; i < CONFIG_LIB60870_MAX_FRAMES; i++) {
+
+        if (staticFrames[i].allocated == 0) {
+            staticFrames[i].msgSize = 6;
+            staticFrames[i].allocated = 1;
+
+            return &staticFrames[i];
+        }
+    }
+
+    return NULL;
+}
+
+#endif /* (CONFIG_LIB60870_STATIC_FRAMES == 1) */
+
+
+T104Frame
+T104Frame_create()
+{
+#if (CONFIG_LIB60870_STATIC_FRAMES == 1)
+
+    if (staticFramesInitialized == 0) {
+        initializeFrames();
+        staticFramesInitialized = 1;
+    }
+
+    T104Frame self = getNextFreeFrame();
+
+#else
+    T104Frame self = (T104Frame) GLOBAL_MALLOC(sizeof(struct sT104Frame));
+
+    if (self != NULL) {
+
+        int i;
+        for (i = 0; i < 256; i++)
+            self->buffer[i] = 0;
+
+        self->virtualFunctionTable = &t104FrameVFT;
+        self->buffer[0] = 0x68;
+        self->msgSize = 6;
+    }
+#endif
+
+    return self;
+}
+
+void
+T104Frame_destroy(Frame super)
+{
+    T104Frame self = (T104Frame) super;
+
+#if (CONFIG_LIB60870_STATIC_FRAMES == 1)
+    self->allocated = 0;
+#else
+    GLOBAL_FREEMEM(self);
+#endif
+}
+
+void
+T104Frame_resetFrame(Frame super)
+{
+    T104Frame self = (T104Frame) super;
+
+    self->msgSize = 6;
+}
+
+void
+T104Frame_prepareToSend(T104Frame self, int sendCounter, int receiveCounter)
+{
+    uint8_t* buffer = self->buffer;
+
+    buffer[1] = (uint8_t) (self->msgSize - 2);
+
+    buffer[2] = (uint8_t) ((sendCounter % 128) * 2);
+    buffer[3] = (uint8_t) (sendCounter / 128);
+
+    buffer[4] = (uint8_t) ((receiveCounter % 128) * 2);
+    buffer[5] = (uint8_t) (receiveCounter / 128);
+}
+
+void
+T104Frame_setNextByte(Frame super, uint8_t byte)
+{
+    T104Frame self = (T104Frame) super;
+
+    self->buffer[self->msgSize++] = byte;
+}
+
+void
+T104Frame_appendBytes(Frame super, const uint8_t* bytes, int numberOfBytes)
+{
+    T104Frame self = (T104Frame) super;
+
+    int i;
+
+    uint8_t* target = self->buffer + self->msgSize;
+
+    for (i = 0; i < numberOfBytes; i++)
+        target[i] = bytes[i];
+
+    self->msgSize += numberOfBytes;
+}
+
+int
+T104Frame_getMsgSize(Frame super)
+{
+    T104Frame self = (T104Frame) super;
+
+    return self->msgSize;
+}
+
+uint8_t*
+T104Frame_getBuffer(Frame super)
+{
+    T104Frame self = (T104Frame) super;
+
+    return self->buffer;
+}
+
+int
+T104Frame_getSpaceLeft(Frame super)
+{
+    T104Frame self = (T104Frame) super;
+
+    return (IEC60870_5_104_MAX_ASDU_LENGTH + IEC60870_5_104_APCI_LENGTH - self->msgSize);
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 4485 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/cs104/cs104_slave.c


+ 71 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/frame.c

@@ -0,0 +1,71 @@
+/*
+ *  Copyright 2016 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "frame.h"
+#include "iec60870_common.h"
+
+struct sFrame {
+    FrameVFT virtualFunctionTable;
+};
+
+
+void
+Frame_destroy(Frame self)
+{
+    self->virtualFunctionTable->destroy(self);
+}
+
+void
+Frame_resetFrame(Frame self)
+{
+    self->virtualFunctionTable->resetFrame(self);
+}
+
+void
+Frame_setNextByte(Frame self, uint8_t byte)
+{
+    self->virtualFunctionTable->setNextByte(self, byte);
+}
+
+void
+Frame_appendBytes(Frame self, uint8_t* bytes, int numberOfBytes)
+{
+    self->virtualFunctionTable->appendBytes(self, bytes, numberOfBytes);
+}
+
+int
+Frame_getMsgSize(Frame self)
+{
+    return self->virtualFunctionTable->getMsgSize(self);
+}
+
+uint8_t*
+Frame_getBuffer(Frame self)
+{
+    return self->virtualFunctionTable->getBuffer(self);
+}
+
+
+int
+Frame_getSpaceLeft(Frame self)
+{
+    return self->virtualFunctionTable->getSpaceLeft(self);
+}

+ 69 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/lib60870_common.c

@@ -0,0 +1,69 @@
+/*
+ *  Copyright 2016 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "iec60870_common.h"
+#include "lib60870_internal.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#if (CONFIG_DEBUG_OUTPUT == 1)
+static bool debugOutputEnabled = 1;
+#endif
+
+void
+lib60870_debug_print(const char *format, ...)
+{
+#if (CONFIG_DEBUG_OUTPUT == 1)
+    if (debugOutputEnabled) {
+        printf("DEBUG_LIB60870: ");
+        va_list ap;
+        va_start(ap, format);
+        vprintf(format, ap);
+        va_end(ap);
+    }
+#else
+    UNUSED_PARAMETER(format);
+#endif
+}
+
+void
+Lib60870_enableDebugOutput(bool value)
+{
+#if (CONFIG_DEBUG_OUTPUT == 1)
+    debugOutputEnabled = value;
+#else
+    UNUSED_PARAMETER(value);
+#endif
+}
+
+
+Lib60870VersionInfo
+Lib60870_getLibraryVersionInfo()
+{
+    Lib60870VersionInfo versionInfo;
+
+    versionInfo.major = LIB60870_VERSION_MAJOR;
+    versionInfo.minor = LIB60870_VERSION_MINOR;
+    versionInfo.patch = LIB60870_VERSION_PATCH;
+
+    return versionInfo;
+}

+ 124 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/link_layer/buffer_frame.c

@@ -0,0 +1,124 @@
+/*
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "frame.h"
+#include "buffer_frame.h"
+
+static struct sFrameVFT bufferFrameVFT = {
+        BufferFrame_destroy,
+        BufferFrame_resetFrame,
+        BufferFrame_setNextByte,
+        BufferFrame_appendBytes,
+        BufferFrame_getMsgSize,
+        BufferFrame_getBuffer,
+        BufferFrame_getSpaceLeft
+};
+
+Frame
+BufferFrame_initialize(BufferFrame self, uint8_t* buffer, int startSize)
+{
+    self->virtualFunctionTable = &bufferFrameVFT;
+    self->buffer = buffer;
+
+    self->startSize = startSize;
+    self->msgSize = startSize;
+    self->isUsed = false;
+
+    return (Frame) self;
+}
+
+void
+BufferFrame_destroy(Frame super)
+{
+    BufferFrame self = (BufferFrame) super;
+
+    self->isUsed = false;
+}
+
+void
+BufferFrame_resetFrame(Frame super)
+{
+    BufferFrame self = (BufferFrame) super;
+
+    self->msgSize = self->startSize;
+}
+
+void
+BufferFrame_setNextByte(Frame super, uint8_t byte)
+{
+    BufferFrame self = (BufferFrame) super;
+
+    self->buffer[self->msgSize++] = byte;
+}
+
+void
+BufferFrame_appendBytes(Frame super, const uint8_t* bytes, int numberOfBytes)
+{
+    BufferFrame self = (BufferFrame) super;
+
+    int i;
+
+    uint8_t* target = self->buffer + self->msgSize;
+
+    for (i = 0; i < numberOfBytes; i++)
+        target[i] = bytes[i];
+
+    self->msgSize += numberOfBytes;
+}
+
+int
+BufferFrame_getMsgSize(Frame super)
+{
+    BufferFrame self = (BufferFrame) super;
+
+    return self->msgSize;
+}
+
+uint8_t*
+BufferFrame_getBuffer(Frame super)
+{
+    BufferFrame self = (BufferFrame) super;
+
+    return self->buffer;
+}
+
+int
+BufferFrame_getSpaceLeft(Frame super)
+{
+    BufferFrame self = (BufferFrame) super;
+
+    return ((self->startSize) - self->msgSize);
+}
+
+bool
+BufferFrame_isUsed(BufferFrame self)
+{
+    return self->isUsed;
+}
+
+void
+BufferFrame_markAsUsed(BufferFrame self)
+{
+    self->isUsed = true;
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 2162 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/link_layer/link_layer.c


+ 196 - 0
app/IEC_SERVER/lib60870-C/src/iec60870/link_layer/serial_transceiver_ft_1_2.c

@@ -0,0 +1,196 @@
+/*
+ *  serial_transceiver_ft_1_2.c
+ *
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "hal_serial.h"
+#include "serial_transceiver_ft_1_2.h"
+#include "lib_memory.h"
+#include <stdlib.h>
+#include <stdbool.h>
+#include "lib60870_internal.h"
+
+struct sSerialTransceiverFT12 {
+    int messageTimeout;
+    int characterTimeout;
+    LinkLayerParameters linkLayerParameters;
+    SerialPort serialPort;
+    IEC60870_RawMessageHandler rawMessageHandler;
+    void* rawMessageHandlerParameter;
+};
+
+SerialTransceiverFT12
+SerialTransceiverFT12_create(SerialPort serialPort, LinkLayerParameters linkLayerParameters)
+{
+    SerialTransceiverFT12 self = (SerialTransceiverFT12) GLOBAL_MALLOC(sizeof(struct sSerialTransceiverFT12));
+
+    if (self != NULL) {
+        self->messageTimeout = 10;
+        self->characterTimeout = 300;
+        self->linkLayerParameters = linkLayerParameters;
+        self->serialPort = serialPort;
+        self->rawMessageHandler = NULL;
+    }
+
+    return self;
+}
+
+void
+SerialTransceiverFT12_destroy(SerialTransceiverFT12 self)
+{
+    if (self != NULL)
+        GLOBAL_FREEMEM(self);
+}
+
+void
+SerialTransceiverFT12_setTimeouts(SerialTransceiverFT12 self, int messageTimeout, int characterTimeout)
+{
+    self->messageTimeout = messageTimeout;
+    self->characterTimeout = characterTimeout;
+}
+
+void
+SerialTransceiverFT12_setRawMessageHandler(SerialTransceiverFT12 self, IEC60870_RawMessageHandler handler, void* parameter)
+{
+    self->rawMessageHandler = handler;
+    self->rawMessageHandlerParameter = parameter;
+}
+
+int
+SerialTransceiverFT12_getBaudRate(SerialTransceiverFT12 self)
+{
+    return SerialPort_getBaudRate(self->serialPort);
+}
+
+void
+SerialTransceiverFT12_sendMessage(SerialTransceiverFT12 self, uint8_t* msg, int msgSize)
+{
+    if (self->rawMessageHandler)
+        self->rawMessageHandler(self->rawMessageHandlerParameter, msg, msgSize, true);
+
+    SerialPort_write(self->serialPort, msg, 0, msgSize);
+}
+
+static int
+readBytesWithTimeout(SerialTransceiverFT12 self, uint8_t* buffer, int startIndex, int count)
+{
+    int readByte;
+    int readBytes = 0;
+
+    while ((readByte = SerialPort_readByte(self->serialPort)) != -1) {
+        buffer[startIndex++] = (uint8_t) readByte;
+
+        readBytes++;
+
+        if (readBytes >= count)
+            break;
+    }
+
+    return readBytes;
+}
+
+void
+SerialTransceiverFT12_readNextMessage(SerialTransceiverFT12 self, uint8_t* buffer,
+        SerialTXMessageHandler messageHandler, void* parameter)
+{
+    SerialPort_setTimeout(self->serialPort, self->messageTimeout);
+
+    int read = SerialPort_readByte(self->serialPort);
+
+    if (read != -1) {
+
+        if (read == 0x68) {
+
+            SerialPort_setTimeout(self->serialPort, self->characterTimeout);
+
+            int msgSize = SerialPort_readByte(self->serialPort);
+
+            if (msgSize == -1)
+                goto sync_error;
+
+            buffer[0] = (uint8_t) 0x68;
+            buffer[1] = (uint8_t) msgSize;
+
+            msgSize += 4;
+
+            int readBytes = readBytesWithTimeout(self, buffer, 2, msgSize);
+
+            if (readBytes == msgSize) {
+                msgSize += 2;
+
+                if (self->rawMessageHandler)
+                    self->rawMessageHandler(self->rawMessageHandlerParameter, buffer, msgSize, false);
+
+                messageHandler(parameter, buffer, msgSize);
+            }
+            else {
+                DEBUG_PRINT("RECV: Timeout reading variable length frame size = %i (expected = %i)\n", readBytes, msgSize);
+            }
+
+        }
+        else if (read == 0x10) {
+
+            SerialPort_setTimeout(self->serialPort, self->characterTimeout);
+
+            buffer[0] = 0x10;
+
+            int msgSize = 3 + self->linkLayerParameters->addressLength;
+
+            int readBytes = readBytesWithTimeout(self, buffer, 1, msgSize);
+
+            if (readBytes == msgSize) {
+                msgSize += 1;
+
+                if (self->rawMessageHandler)
+                    self->rawMessageHandler(self->rawMessageHandlerParameter, buffer, msgSize, false);
+
+                messageHandler(parameter, buffer, msgSize);
+            }
+            else {
+                DEBUG_PRINT("RECV: Timeout reading fixed length frame size = %i (expected = %i)\n", readBytes, msgSize);
+            }
+
+        }
+        else if (read == 0xe5) {
+            int msgSize = 1;
+            buffer[0] = (uint8_t) read;
+
+            if (self->rawMessageHandler)
+                self->rawMessageHandler(self->rawMessageHandlerParameter, buffer, msgSize, false);
+
+            messageHandler(parameter, buffer, msgSize);
+        }
+        else {
+            goto sync_error;
+        }
+
+    }
+
+    return;
+
+sync_error:
+
+    DEBUG_PRINT("RECV: SYNC ERROR\n");
+
+    SerialPort_discardInBuffer(self->serialPort);
+
+    return;
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 2109 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/cs101_information_objects.h


+ 342 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/cs101_master.h

@@ -0,0 +1,342 @@
+/*
+ *  cs101_master.h
+ *
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+/**
+ * \file cs101_master.h
+ * \brief Functions for CS101_Master ADT.
+ * Can be used to implement a balanced or unbalanced CS 101 master.
+ */
+
+#ifndef SRC_INC_API_CS101_MASTER_H_
+#define SRC_INC_API_CS101_MASTER_H_
+
+#include "iec60870_master.h"
+#include "link_layer_parameters.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup MASTER Master related functions
+ *
+ * @{
+ */
+
+/**
+ * @defgroup CS101_MASTER CS 101 master related functions
+ *
+ * @{
+ */
+
+/**
+ * \brief CS101_Master type
+ */
+typedef struct sCS101_Master* CS101_Master;
+
+/**
+ * \brief Create a new master instance
+ *
+ * \param port the serial port to use
+ * \param llParameters the link layer parameters to use
+ * \param alParameters the application layer parameters to use
+ * \param mode the link layer mode (either IEC60870_LINK_LAYER_BALANCED or IEC60870_LINK_LAYER_UNBALANCED)
+ *
+ * \return the new CS101_Master instance
+ */
+CS101_Master
+CS101_Master_create(SerialPort port, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode mode);
+
+/**
+ * \brief Create a new master instance and specify message queue size (for balanced mode)
+ *
+ * \param port the serial port to use
+ * \param llParameters the link layer parameters to use
+ * \param alParameters the application layer parameters to use
+ * \param mode the link layer mode (either IEC60870_LINK_LAYER_BALANCED or IEC60870_LINK_LAYER_UNBALANCED)
+ * \param queueSize set the message queue size (only for balanced mode)
+ *
+ * \return the new CS101_Master instance
+ */
+CS101_Master
+CS101_Master_createEx(SerialPort serialPort, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode,
+        int queueSize);
+
+/**
+ * \brief Receive a new message and run the protocol state machine(s).
+ *
+ * NOTE: This function has to be called frequently in order to send and
+ * receive messages to and from slaves.
+ */
+void
+CS101_Master_run(CS101_Master self);
+
+/**
+ * \brief Start a background thread that handles the link layer connections
+ *
+ * NOTE: This requires threads. If you don't want to use a separate thread
+ * for the master instance you have to call the \ref CS101_Master_run function
+ * periodically.
+ *
+ * \param self CS101_Master instance
+ */
+void
+CS101_Master_start(CS101_Master self);
+
+/**
+ * \brief Stops the background thread that handles the link layer connections
+ *
+ * \param self CS101_Master instance
+ */
+void
+CS101_Master_stop(CS101_Master self);
+
+/**
+ * \brief Add a new slave connection
+ *
+ * This function creates and starts a new link layer state machine
+ * to be used for communication with the slave. It has to be called
+ * before any application data can be send/received to/from the slave.
+ *
+ * \param address link layer address of the slave
+ */
+void
+CS101_Master_addSlave(CS101_Master self, int address);
+
+
+/**
+ * \brief Poll a slave (only unbalanced mode)
+ *
+ * NOTE: This command will instruct the unbalanced link layer to send a
+ * request for class 2 data. It is required to frequently call this
+ * message for each slave in order to receive application layer data from
+ * the slave
+ *
+ * \param address the link layer address of the slave
+ */
+void
+CS101_Master_pollSingleSlave(CS101_Master self, int address);
+
+/**
+ * \brief Destroy the master instance and release all resources
+ */
+void
+CS101_Master_destroy(CS101_Master self);
+
+/**
+ * \brief Set the value of the DIR bit when sending messages (only balanced mode)
+ *
+ * NOTE: Default value is true (controlling station). In the case of two equivalent stations
+ * the value is defined by agreement.
+ *
+ * \param dir the value of the DIR bit when sending messages
+ */
+void
+CS101_Master_setDIR(CS101_Master self, bool dir);
+
+/**
+ * \brief Set the own link layer address (only balanced mode)
+ *
+ * \param address the link layer address to use
+ */
+void
+CS101_Master_setOwnAddress(CS101_Master self, int address);
+
+/**
+ * \brief Set the slave address for the following send functions
+ *
+ * NOTE: This is always required in unbalanced mode. Some balanced slaves
+ * also check the link layer address. In this case the slave address
+ * has also to be set in balanced mode.
+ *
+ * \param address the link layer address of the slave to address
+ */
+void
+CS101_Master_useSlaveAddress(CS101_Master self, int address);
+
+
+
+/**
+ * \brief Returns the application layer parameters object of this master instance
+ *
+ * \return the CS101_AppLayerParameters instance used by this master
+ */
+CS101_AppLayerParameters
+CS101_Master_getAppLayerParameters(CS101_Master self);
+
+/**
+ * \brief Returns the link layer parameters object of this master instance
+ *
+ * \return the LinkLayerParameters instance used by this master
+ */
+LinkLayerParameters
+CS101_Master_getLinkLayerParameters(CS101_Master self);
+
+/**
+ * \brief Is the channel ready to transmit an ASDU (only unbalanced mode)
+ *
+ * The function will return true when the channel (slave) transmit buffer
+ * is empty.
+ *
+ * \param address slave address of the recipient
+ *
+ * \return true, if channel ready to send a new ASDU, false otherwise
+ */
+bool
+CS101_Master_isChannelReady(CS101_Master self, int address);
+
+/**
+ * \brief Manually send link layer test function.
+ *
+ * Together with the IEC60870_LinkLayerStateChangedHandler this function can
+ * be used to ensure that the link is working correctly
+ */
+void
+CS101_Master_sendLinkLayerTestFunction(CS101_Master self);
+
+/**
+ * \brief send an interrogation command
+ *
+ * \param cot cause of transmission
+ * \param ca Common address of the slave/server
+ * \param qoi qualifier of interrogation (20 for station interrogation)
+ */
+void
+CS101_Master_sendInterrogationCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, QualifierOfInterrogation qoi);
+
+/**
+ * \brief send a counter interrogation command
+ *
+ * \param cot cause of transmission
+ * \param ca Common address of the slave/server
+ * \param qcc
+ */
+void
+CS101_Master_sendCounterInterrogationCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, uint8_t qcc);
+
+/**
+ * \brief  Sends a read command (C_RD_NA_1 typeID: 102)
+ *
+ * This will send a read command C_RC_NA_1 (102) to the slave/outstation. The COT is always REQUEST (5).
+ * It is used to implement the cyclical polling of data application function.
+ *
+ * \param ca Common address of the slave/server
+ * \param ioa Information object address of the data point to read
+ */
+void
+CS101_Master_sendReadCommand(CS101_Master self, int ca, int ioa);
+
+/**
+ * \brief Sends a clock synchronization command (C_CS_NA_1 typeID: 103)
+ *
+ * \param ca Common address of the slave/server
+ * \param time new system time for the slave/server
+ */
+void
+CS101_Master_sendClockSyncCommand(CS101_Master self, int ca, CP56Time2a time);
+
+/**
+ * \brief Send a test command (C_TS_NA_1 typeID: 104)
+ *
+ * Note: This command is not supported by IEC 60870-5-104
+ *
+ * \param ca Common address of the slave/server
+ */
+void
+CS101_Master_sendTestCommand(CS101_Master self, int ca);
+
+/**
+ * \brief Send a process command to the controlled (or other) station
+ *
+ * \param cot the cause of transmission (should be ACTIVATION to select/execute or ACT_TERM to cancel the command)
+ * \param ca the common address of the information object
+ * \param command the command information object (e.g. SingleCommand or DoubleCommand)
+ *
+ */
+void
+CS101_Master_sendProcessCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, InformationObject command);
+
+
+/**
+ * \brief Send a user specified ASDU
+ *
+ * This function can be used for any kind of ASDU types. It can
+ * also be used for monitoring messages in reverse direction.
+ *
+ * NOTE: The ASDU is put into a message queue and will be sent whenever
+ * the link layer state machine is able to transmit the ASDU. The ASDUs will
+ * be sent in the order they are put into the queue.
+ *
+ * \param asdu the ASDU to send
+ */
+void
+CS101_Master_sendASDU(CS101_Master self, CS101_ASDU asdu);
+
+/**
+ * \brief Register a callback handler for received ASDUs
+ *
+ * \param handler user provided callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+void
+CS101_Master_setASDUReceivedHandler(CS101_Master self, CS101_ASDUReceivedHandler handler, void* parameter);
+
+/**
+ * \brief Set a callback handler for link layer state changes
+ */
+void
+CS101_Master_setLinkLayerStateChanged(CS101_Master self, IEC60870_LinkLayerStateChangedHandler handler, void* parameter);
+
+/**
+ * \brief Set the raw message callback (called when a message is sent or received)
+ *
+ * \param handler user provided callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+void
+CS101_Master_setRawMessageHandler(CS101_Master self, IEC60870_RawMessageHandler handler, void* parameter);
+
+/**
+ * \brief Set the idle timeout (only for balanced mode)
+ *
+ * Time with no activity after which the connection is considered
+ * in idle (LL_STATE_IDLE) state.
+ *
+ * \param timeoutInMs the timeout value in milliseconds
+ */
+void
+CS101_Master_setIdleTimeout(CS101_Master self, int timeoutInMs);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_INC_API_CS101_MASTER_H_ */

+ 351 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/cs101_slave.h

@@ -0,0 +1,351 @@
+/*
+ *  cs101_slave.h
+ *
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60870_CS101_CS101_SLAVE_H_
+#define SRC_IEC60870_CS101_CS101_SLAVE_H_
+
+/**
+ * \file cs101_slave.h
+ * \brief Functions for CS101_Slave ADT.
+ * Can be used to implement a balanced or unbalanced CS 101 slave.
+ */
+
+#include "hal_serial.h"
+#include "iec60870_common.h"
+#include "iec60870_slave.h"
+#include "link_layer_parameters.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup SLAVE Slave related functions
+ *
+ * @{
+ */
+
+
+/**
+ * @defgroup CS101_SLAVE CS 101 slave (serial link layer) related functions
+ *
+ * @{
+ */
+
+
+/**
+ * \brief CS101_Slave type
+ */
+typedef struct sCS101_Slave* CS101_Slave;
+
+/**
+ * \brief Create a new balanced or unbalanced CS101 slave
+ *
+ * NOTE: The CS101_Slave instance has two separate data queues for class 1 and class 2 data.
+ * This constructor uses the default max queue size for both queues.
+ *
+ * \param serialPort the serial port to be used
+ * \param llParameters the link layer parameters to be used
+ * \param alParameters the CS101 application layer parameters
+ * \param linkLayerMode the link layer mode (either BALANCED or UNBALANCED)
+ *
+ * \return the new slave instance
+ */
+CS101_Slave
+CS101_Slave_create(SerialPort serialPort, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode);
+
+/**
+ * \brief Create a new balanced or unbalanced CS101 slave
+ *
+ * NOTE: The CS101_Slave instance has two separate data queues for class 1 and class 2 data.
+ *
+ * \param serialPort the serial port to be used
+ * \param llParameters the link layer parameters to be used
+ * \param alParameters the CS101 application layer parameters
+ * \param linkLayerMode the link layer mode (either BALANCED or UNBALANCED)
+ * \param class1QueueSize size of the class1 data queue
+ * \param class2QueueSize size of the class2 data queue
+ *
+ * \return the new slave instance
+ */
+CS101_Slave
+CS101_Slave_createEx(SerialPort serialPort, const LinkLayerParameters llParameters, const CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode,
+        int class1QueueSize, int class2QueueSize);
+
+/**
+ * \brief Destroy the slave instance and cleanup all resources
+ *
+ * \param self CS101_Slave instance
+ */
+void
+CS101_Slave_destroy(CS101_Slave self);
+
+/**
+ * \brief Set the value of the DIR bit when sending messages (only balanced mode)
+ *
+ * NOTE: Default value is false (controlled station). In the case of two equivalent stations
+ * the value is defined by agreement.
+ *
+ * \param dir the value of the DIR bit when sending messages
+ */
+void
+CS101_Slave_setDIR(CS101_Slave self, bool dir);
+
+/**
+ * \brief Register a plugin instance with this slave instance
+ *
+ * \param the plugin instance.
+ */
+void
+CS101_Slave_addPlugin(CS101_Slave self, CS101_SlavePlugin plugin);
+
+/**
+ * \brief Set the idle timeout
+ *
+ * Time with no activity after which the connection is considered
+ * in idle (LL_STATE_IDLE) state.
+ *
+ * \param timeoutInMs the timeout value in milliseconds
+ */
+void
+CS101_Slave_setIdleTimeout(CS101_Slave self, int timeoutInMs);
+
+/**
+ * \brief Set a callback handler for link layer state changes
+ */
+void
+CS101_Slave_setLinkLayerStateChanged(CS101_Slave self, IEC60870_LinkLayerStateChangedHandler handler, void* parameter);
+
+
+/**
+ * \brief Set the local link layer address
+ *
+ * \param self CS101_Slave instance
+ * \param address the link layer address (can be either 1 or 2 byte wide).
+ */
+void
+CS101_Slave_setLinkLayerAddress(CS101_Slave self, int address);
+
+/**
+ * \brief Set the link layer address of the remote station
+ *
+ * \param self CS101_Slave instance
+ * \param address the link layer address (can be either 1 or 2 byte wide).
+ */
+void
+CS101_Slave_setLinkLayerAddressOtherStation(CS101_Slave self, int address);
+
+/**
+ * \brief Check if the class 1 ASDU is full
+ *
+ * \param self CS101_Slave instance
+ *
+ * \return true when the queue is full, false otherwise
+ */
+bool
+CS101_Slave_isClass1QueueFull(CS101_Slave self);
+
+/**
+ * \brief Enqueue an ASDU into the class 1 data queue
+ *
+ * \param self CS101_Slave instance
+ * \param asdu the ASDU instance to enqueue
+ */
+void
+CS101_Slave_enqueueUserDataClass1(CS101_Slave self, CS101_ASDU asdu);
+
+/**
+ * \brief Check if the class 2 ASDU is full
+ *
+ * \param self CS101_Slave instance
+ *
+ * \return true when the queue is full, false otherwise
+ */
+bool
+CS101_Slave_isClass2QueueFull(CS101_Slave self);
+
+/**
+ * \brief Enqueue an ASDU into the class 2 data queue
+ *
+ * \param self CS101_Slave instance
+ * \param asdu the ASDU instance to enqueue
+ */
+void
+CS101_Slave_enqueueUserDataClass2(CS101_Slave self, CS101_ASDU asdu);
+
+/**
+ * \brief Remove all ASDUs from the class 1/2 data queues
+ *
+ * \param self CS101_Slave instance
+ */
+void
+CS101_Slave_flushQueues(CS101_Slave self);
+
+/**
+ * \brief Receive a new message and run the link layer state machines
+ *
+ * NOTE: Has to be called frequently, when the start/stop functions are
+ * not used. Otherwise it will be called by the background thread.
+ *
+ * \param self CS101_Slave instance
+ */
+void
+CS101_Slave_run(CS101_Slave self);
+
+/**
+ * \brief Start a background thread that handles the link layer connections
+ *
+ * NOTE: This requires threads. If you don't want to use a separate thread
+ * for the slave instance you have to call the \ref CS101_Slave_run function
+ * periodically.
+ *
+ * \param self CS101_Slave instance
+ */
+void
+CS101_Slave_start(CS101_Slave self);
+
+/**
+ * \brief Stops the background thread that handles the link layer connections
+ *
+ * \param self CS101_Slave instance
+ */
+void
+CS101_Slave_stop(CS101_Slave self);
+
+/**
+ * \brief Returns the application layer parameters object of this slave instance
+ *
+ * \param self CS101_Slave instance
+ *
+ * \return the CS101_AppLayerParameters instance used by this slave
+ */
+CS101_AppLayerParameters
+CS101_Slave_getAppLayerParameters(CS101_Slave self);
+
+/**
+ * \brief Returns the link layer parameters object of this slave instance
+ *
+ * \param self CS101_Slave instance
+ *
+ * \return the LinkLayerParameters instance used by this slave
+ */
+LinkLayerParameters
+CS101_Slave_getLinkLayerParameters(CS101_Slave self);
+
+/**
+ * \brief Set the handler for the reset CU (communication unit) message
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setResetCUHandler(CS101_Slave self, CS101_ResetCUHandler handler, void* parameter);
+
+/**
+ * \brief Set the handler for the general interrogation message
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setInterrogationHandler(CS101_Slave self, CS101_InterrogationHandler handler, void*  parameter);
+
+/**
+ * \brief Set the handler for the counter interrogation message
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setCounterInterrogationHandler(CS101_Slave self, CS101_CounterInterrogationHandler handler, void*  parameter);
+
+/**
+ * \brief Set the handler for the read message
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setReadHandler(CS101_Slave self, CS101_ReadHandler handler, void* parameter);
+
+/**
+ * \brief Set the handler for the clock synchronization message
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setClockSyncHandler(CS101_Slave self, CS101_ClockSynchronizationHandler handler, void* parameter);
+
+/**
+ * \brief Set the handler for the reset process message
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setResetProcessHandler(CS101_Slave self, CS101_ResetProcessHandler handler, void* parameter);
+
+/**
+ * \brief Set the handler for the delay acquisition message
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setDelayAcquisitionHandler(CS101_Slave self, CS101_DelayAcquisitionHandler handler, void* parameter);
+
+/**
+ * \brief Set the handler for a received ASDU
+ *
+ * NOTE: This a generic handler that will only be called when the ASDU has not been handled by
+ * one of the other callback handlers.
+ *
+ * \param handler the callback handler function
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+void
+CS101_Slave_setASDUHandler(CS101_Slave self, CS101_ASDUHandler handler, void* parameter);
+
+/**
+ * \brief Set the raw message callback (called when a message is sent or received)
+ *
+ * \param handler user provided callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+void
+CS101_Slave_setRawMessageHandler(CS101_Slave self, IEC60870_RawMessageHandler handler, void* parameter);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_IEC60870_CS101_CS101_SLAVE_H_ */

+ 372 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/cs104_connection.h

@@ -0,0 +1,372 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_CS104_CONNECTION_H_
+#define SRC_INC_CS104_CONNECTION_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "tls_config.h"
+#include "iec60870_master.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file cs104_connection.h
+ * \brief CS 104 master side definitions
+ */
+
+/**
+ * @addtogroup MASTER Master related functions
+ *
+ * @{
+ */
+
+/**
+ * @defgroup CS104_MASTER CS 104 master related functions
+ *
+ * @{
+ */
+
+typedef struct sCS104_Connection* CS104_Connection;
+
+/**
+ * \brief Create a new connection object
+ *
+ * \param hostname host name of IP address of the server to connect
+ * \param tcpPort tcp port of the server to connect. If set to -1 use default port (2404)
+ *
+ * \return the new connection object
+ */
+CS104_Connection
+CS104_Connection_create(const char* hostname, int tcpPort);
+
+/**
+ * \brief Create a new secure connection object (uses TLS)
+ *
+ * \param hostname host name of IP address of the server to connect
+ * \param tcpPort tcp port of the server to connect. If set to -1 use default port (19998)
+ * \param tlcConfig the TLS configuration (certificates, keys, and parameters)
+ *
+ * \return the new connection object
+ */
+CS104_Connection
+CS104_Connection_createSecure(const char* hostname, int tcpPort, TLSConfiguration tlsConfig);
+
+
+/**
+ * \brief Set the local IP address and port to be used by the client
+ * 
+ * NOTE: This function is optional. When not used the OS decides what IP address and TCP port to use.
+ * 
+ * \param self CS104_Connection instance
+ * \param localIpAddress the local IP address or hostname as C string
+ * \param localPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
+ */
+void
+CS104_Connection_setLocalAddress(CS104_Connection self, const char* localIpAddress, int localPort);
+
+/**
+ * \brief Set the CS104 specific APCI parameters.
+ *
+ * If not set the default parameters are used. This function must be called before the
+ * CS104_Connection_connect function is called! If the function is called after the connect
+ * the behavior is undefined.
+ *
+ * \param self CS104_Connection instance
+ * \param parameters the APCI layer parameters
+ */
+void
+CS104_Connection_setAPCIParameters(CS104_Connection self, const CS104_APCIParameters parameters);
+
+/**
+ * \brief Get the currently used CS104 specific APCI parameters
+ */
+CS104_APCIParameters
+CS104_Connection_getAPCIParameters(CS104_Connection self);
+
+/**
+ * \brief Set the CS101 application layer parameters
+ *
+ * If not set the default parameters are used. This function must be called before the
+ * CS104_Connection_connect function is called! If the function is called after the connect
+ * the behavior is undefined.
+ *
+ * \param self CS104_Connection instance
+ * \param parameters the application layer parameters
+ */
+void
+CS104_Connection_setAppLayerParameters(CS104_Connection self, const CS101_AppLayerParameters parameters);
+
+/**
+ * \brief Return the currently used application layer parameter
+ *
+ * NOTE: The application layer parameters are required to create CS101_ASDU objects.
+ *
+ * \param self CS104_Connection instance
+ *
+ * \return the currently used CS101_AppLayerParameters object
+ */
+CS101_AppLayerParameters
+CS104_Connection_getAppLayerParameters(CS104_Connection self);
+
+/**
+ * \brief Sets the timeout for connecting to the server (in ms)
+ * 
+ * \deprecated Function has no effect! Set T0 parameter instead.
+ *
+ * \param self
+ * \param millies timeout value in ms
+ */
+void
+CS104_Connection_setConnectTimeout(CS104_Connection self, int millies);
+
+/**
+ * \brief non-blocking connect.
+ *
+ * Invokes a connection establishment to the server and returns immediately.
+ *
+ * \param self CS104_Connection instance
+ */
+void
+CS104_Connection_connectAsync(CS104_Connection self);
+
+/**
+ * \brief blocking connect
+ *
+ * Establishes a connection to a server. This function is blocking and will return
+ * after the connection is established or the connect timeout elapsed.
+ *
+ * \param self CS104_Connection instance
+ * \return true when connected, false otherwise
+ */
+bool
+CS104_Connection_connect(CS104_Connection self);
+
+/**
+ * \brief start data transmission on this connection
+ *
+ * After issuing this command the client (master) will receive spontaneous
+ * (unsolicited) messages from the server (slave).
+ */
+void
+CS104_Connection_sendStartDT(CS104_Connection self);
+
+/**
+ * \brief stop data transmission on this connection
+ */
+void
+CS104_Connection_sendStopDT(CS104_Connection self);
+
+/**
+ * \brief Check if the transmit (send) buffer is full. If true the next send command will fail.
+ *
+ * The transmit buffer is full when the slave/server didn't confirm the last k sent messages.
+ * In this case the next message can only be sent after the next confirmation (by I or S messages)
+ * that frees part of the sent messages buffer.
+ */
+bool
+CS104_Connection_isTransmitBufferFull(CS104_Connection self);
+
+/**
+ * \brief send an interrogation command
+ *
+ * \param cot cause of transmission
+ * \param ca Common address of the slave/server
+ * \param qoi qualifier of interrogation (20 for station interrogation)
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendInterrogationCommand(CS104_Connection self, CS101_CauseOfTransmission cot, int ca, QualifierOfInterrogation qoi);
+
+/**
+ * \brief send a counter interrogation command
+ *
+ * \param cot cause of transmission
+ * \param ca Common address of the slave/server
+ * \param qcc
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendCounterInterrogationCommand(CS104_Connection self, CS101_CauseOfTransmission cot, int ca, uint8_t qcc);
+
+/**
+ * \brief  Sends a read command (C_RD_NA_1 typeID: 102)
+ *
+ * This will send a read command C_RC_NA_1 (102) to the slave/outstation. The COT is always REQUEST (5).
+ * It is used to implement the cyclical polling of data application function.
+ *
+ * \param ca Common address of the slave/server
+ * \param ioa Information object address of the data point to read
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendReadCommand(CS104_Connection self, int ca, int ioa);
+
+/**
+ * \brief Sends a clock synchronization command (C_CS_NA_1 typeID: 103)
+ *
+ * \param ca Common address of the slave/server
+ * \param newTime new system time for the slave/server
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendClockSyncCommand(CS104_Connection self, int ca, CP56Time2a newTime);
+
+/**
+ * \brief Send a test command (C_TS_NA_1 typeID: 104)
+ *
+ * Note: This command is not supported by IEC 60870-5-104
+ *
+ * \param ca Common address of the slave/server
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendTestCommand(CS104_Connection self, int ca);
+
+/**
+ * \brief Send a test command with timestamp (C_TS_TA_1 typeID: 107)
+ *
+ * \param ca Common address of the slave/server
+ * \param tsc test sequence counter
+ * \param timestamp CP56Time2a timestamp
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendTestCommandWithTimestamp(CS104_Connection self, int ca, uint16_t tsc, CP56Time2a timestamp);
+
+/**
+ * \brief Send a process command to the controlled (or other) station
+ *
+ * \deprecated Use \ref CS104_Connection_sendProcessCommandEx instead
+ *
+ * \param typeId the type ID of the command message to send or 0 to use the type ID of the information object
+ * \param cot the cause of transmission (should be ACTIVATION to select/execute or ACT_TERM to cancel the command)
+ * \param ca the common address of the information object
+ * \param command the command information object (e.g. SingleCommand or DoubleCommand)
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendProcessCommand(CS104_Connection self, TypeID typeId, CS101_CauseOfTransmission cot,
+        int ca, InformationObject command);
+
+/**
+ * \brief Send a process command to the controlled (or other) station
+ *
+ * \param cot the cause of transmission (should be ACTIVATION to select/execute or ACT_TERM to cancel the command)
+ * \param ca the common address of the information object
+ * \param command the command information object (e.g. SingleCommand or DoubleCommand)
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendProcessCommandEx(CS104_Connection self, CS101_CauseOfTransmission cot, int ca, InformationObject sc);
+
+
+/**
+ * \brief Send a user specified ASDU
+ *
+ * \param asdu the ASDU to send
+ *
+ * \return true if message was sent, false otherwise
+ */
+bool
+CS104_Connection_sendASDU(CS104_Connection self, CS101_ASDU asdu);
+
+/**
+ * \brief Register a callback handler for received ASDUs
+ *
+ * \param handler user provided callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+void
+CS104_Connection_setASDUReceivedHandler(CS104_Connection self, CS101_ASDUReceivedHandler handler, void* parameter);
+
+typedef enum {
+    CS104_CONNECTION_OPENED = 0,
+    CS104_CONNECTION_CLOSED = 1,
+    CS104_CONNECTION_STARTDT_CON_RECEIVED = 2,
+    CS104_CONNECTION_STOPDT_CON_RECEIVED = 3,
+    CS104_CONNECTION_FAILED = 4
+} CS104_ConnectionEvent;
+
+/**
+ * \brief Handler that is called when the connection is established or closed
+ * 
+ * \note Calling \ref CS104_Connection_destroy or \ref CS104_Connection_close inside
+ * of the callback causes a memory leak!
+ *
+ * \param parameter user provided parameter
+ * \param connection the connection object
+ * \param event event type
+ */
+typedef void (*CS104_ConnectionHandler) (void* parameter, CS104_Connection connection, CS104_ConnectionEvent event);
+
+/**
+ * \brief Set the connection event handler
+ *
+ * \param handler user provided callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+void
+CS104_Connection_setConnectionHandler(CS104_Connection self, CS104_ConnectionHandler handler, void* parameter);
+
+
+/**
+ * \brief Set the raw message callback (called when a message is sent or received)
+ *
+ * \param handler user provided callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+void
+CS104_Connection_setRawMessageHandler(CS104_Connection self, IEC60870_RawMessageHandler handler, void* parameter);
+
+/**
+ * \brief Close the connection
+ */
+void
+CS104_Connection_close(CS104_Connection self);
+
+/**
+ * \brief Close the connection and free all related resources
+ */
+void
+CS104_Connection_destroy(CS104_Connection self);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_INC_CS104_CONNECTION_H_ */

+ 395 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/cs104_slave.h

@@ -0,0 +1,395 @@
+/*
+ *  cs104_slave.h
+ *
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_API_CS104_SLAVE_H_
+#define SRC_INC_API_CS104_SLAVE_H_
+
+#include "iec60870_slave.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file cs104_slave.h
+ * \brief CS 104 slave side definitions
+ */
+
+/**
+ * @addtogroup SLAVE Slave related functions
+ *
+ * @{
+ */
+
+/**
+ * @defgroup CS104_SLAVE CS 104 slave (TCP/IP server) related functions
+ *
+ * @{
+ */
+
+typedef struct sCS104_Slave* CS104_Slave;
+
+typedef enum {
+    CS104_MODE_SINGLE_REDUNDANCY_GROUP,
+    CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP,
+    CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS
+} CS104_ServerMode;
+
+typedef enum
+{
+    IP_ADDRESS_TYPE_IPV4,
+    IP_ADDRESS_TYPE_IPV6
+} eCS104_IPAddressType;
+
+typedef struct sCS104_RedundancyGroup* CS104_RedundancyGroup;
+
+/**
+ * \brief Connection request handler is called when a client tries to connect to the server.
+ *
+ * \param parameter user provided parameter
+ * \param ipAddress string containing IP address and TCP port number (e.g. "192.168.1.1:34521")
+ *
+ * \return true to accept the connection request, false to deny
+ */
+typedef bool (*CS104_ConnectionRequestHandler) (void* parameter, const char* ipAddress);
+
+typedef enum {
+    CS104_CON_EVENT_CONNECTION_OPENED = 0,
+    CS104_CON_EVENT_CONNECTION_CLOSED = 1,
+    CS104_CON_EVENT_ACTIVATED = 2,
+    CS104_CON_EVENT_DEACTIVATED = 3
+} CS104_PeerConnectionEvent;
+
+
+/**
+ * \brief Handler that is called when a peer connection is established or closed, or START_DT/STOP_DT is issued
+ *
+ * \param parameter user provided parameter
+ * \param connection the connection object
+ * \param event event type
+ */
+typedef void (*CS104_ConnectionEventHandler) (void* parameter, IMasterConnection connection, CS104_PeerConnectionEvent event);
+
+/**
+ * \brief Callback handler for sent and received messages
+ *
+ * This callback handler provides access to the raw message buffer of received or sent
+ * messages. It can be used for debugging purposes. Usually it is not used nor required
+ * for applications.
+ *
+ * \param parameter user provided parameter
+ * \param connection the connection that sent or received the message
+ * \param msg the message buffer
+ * \param msgSize size of the message
+ * \param sent indicates if the message was sent or received
+ */
+typedef void (*CS104_SlaveRawMessageHandler) (void* parameter, IMasterConnection connection, uint8_t* msg, int msgSize, bool send);
+
+
+/**
+ * \brief Create a new instance of a CS104 slave (server)
+ *
+ * \param maxLowPrioQueueSize the maximum size of the event queue
+ * \param maxHighPrioQueueSize the maximum size of the high-priority queue
+ *
+ * \return the new slave instance
+ */
+CS104_Slave
+CS104_Slave_create(int maxLowPrioQueueSize, int maxHighPrioQueueSize);
+
+/**
+ * \brief Create a new instance of a CS104 slave (server) with TLS enabled
+ *
+ * \param maxLowPrioQueueSize the maximum size of the event queue
+ * \param maxHighPrioQueueSize the maximum size of the high-priority queue
+ * \param tlsConfig the TLS configuration object (containing configuration parameters, keys, and certificates)
+ *
+ * \return the new slave instance
+ */
+CS104_Slave
+CS104_Slave_createSecure(int maxLowPrioQueueSize, int maxHighPrioQueueSize, TLSConfiguration tlsConfig);
+
+void
+CS104_Slave_addPlugin(CS104_Slave self, CS101_SlavePlugin plugin);
+
+/**
+ * \brief Set the local IP address to bind the server
+ * use "0.0.0.0" to bind to all interfaces
+ *
+ * \param self the slave instance
+ * \param ipAddress the IP address string or hostname
+ */
+void
+CS104_Slave_setLocalAddress(CS104_Slave self, const char* ipAddress);
+
+/**
+ * \brief Set the local TCP port to bind the server
+ *
+ * \param self the slave instance
+ * \param tcpPort the TCP port to use (default is 2404)
+ */
+void
+CS104_Slave_setLocalPort(CS104_Slave self, int tcpPort);
+
+/**
+ * \brief Get the number of connected clients
+ *
+ * \param self the slave instance
+ */
+int
+CS104_Slave_getOpenConnections(CS104_Slave self);
+
+/**
+ * \brief set the maximum number of open client connections allowed
+ *
+ * NOTE: the number cannot be larger than the static maximum defined in
+ *
+ * \param self the slave instance
+ * \param maxOpenConnections the maximum number of open client connections allowed
+ */
+void
+CS104_Slave_setMaxOpenConnections(CS104_Slave self, int maxOpenConnections);
+
+/**
+ * \brief Set one of the server modes
+ *
+ * \param self the slave instance
+ * \param serverMode the server mode (see \ref CS104_ServerMode) to use
+ */
+void
+CS104_Slave_setServerMode(CS104_Slave self, CS104_ServerMode serverMode);
+
+/**
+ * \brief Set the connection request handler
+ *
+ * The connection request handler is called whenever a client/master is trying to connect.
+ * This handler can be used to implement access control mechanisms as it allows the user to decide
+ * if the new connection is accepted or not.
+ *
+ * \param self the slave instance
+ * \param handler the callback function to be used
+ * \param parameter user provided context parameter that will be passed to the callback function (or NULL if not required).
+ */
+void
+CS104_Slave_setConnectionRequestHandler(CS104_Slave self, CS104_ConnectionRequestHandler handler, void* parameter);
+
+/**
+ * \brief Set the connection event handler
+ *
+ * The connection request handler is called whenever a connection event happens. A connection event
+ * can be when a client connects or disconnects, or when a START_DT or STOP_DT message is received.
+ *
+ * \param self the slave instance
+ * \param handler the callback function to be used
+ * \param parameter user provided context parameter that will be passed to the callback function (or NULL if not required).
+ */
+void
+CS104_Slave_setConnectionEventHandler(CS104_Slave self, CS104_ConnectionEventHandler handler, void* parameter);
+
+void
+CS104_Slave_setInterrogationHandler(CS104_Slave self, CS101_InterrogationHandler handler, void*  parameter);
+
+void
+CS104_Slave_setCounterInterrogationHandler(CS104_Slave self, CS101_CounterInterrogationHandler handler, void*  parameter);
+
+/**
+ * \brief set handler for read request (C_RD_NA_1 - 102)
+ */
+void
+CS104_Slave_setReadHandler(CS104_Slave self, CS101_ReadHandler handler, void* parameter);
+
+void
+CS104_Slave_setASDUHandler(CS104_Slave self, CS101_ASDUHandler handler, void* parameter);
+
+void
+CS104_Slave_setClockSyncHandler(CS104_Slave self, CS101_ClockSynchronizationHandler handler, void* parameter);
+
+/**
+ * \brief Set the raw message callback (called when a message is sent or received)
+ *
+ * \param handler user provided callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+void
+CS104_Slave_setRawMessageHandler(CS104_Slave self, CS104_SlaveRawMessageHandler handler, void* parameter);
+
+/**
+ * \brief Get the APCI parameters instance. APCI parameters are CS 104 specific parameters.
+ */
+CS104_APCIParameters
+CS104_Slave_getConnectionParameters(CS104_Slave self);
+
+/**
+ * \brief Get the application layer parameters instance..
+ */
+CS101_AppLayerParameters
+CS104_Slave_getAppLayerParameters(CS104_Slave self);
+
+/**
+ * \brief Start the CS 104 slave. The slave (server) will listen on the configured TCP/IP port
+ *
+ * NOTE: This function will start a thread that handles the incoming client connections.
+ * This function requires CONFIG_USE_THREADS = 1 and CONFIG_USE_SEMAPHORES == 1 in lib60870_config.h
+ *
+ * \param self CS104_Slave instance
+ */
+void
+CS104_Slave_start(CS104_Slave self);
+
+/**
+ * \brief Check if slave is running
+ *
+ * \param self CS104_Slave instance
+ *
+ * \return true when slave is running, false otherwise
+ */
+bool
+CS104_Slave_isRunning(CS104_Slave self);
+
+/**
+ * \brief Stop the server.
+ *
+ * Stop listening to incoming TCP/IP connections and close all open connections.
+ * Event buffers will be deactivated.
+ */
+void
+CS104_Slave_stop(CS104_Slave self);
+
+/**
+ * \brief Start the slave (server) in non-threaded mode.
+ *
+ * Start listening to incoming TCP/IP connections.
+ *
+ * NOTE: Server should only be started after all configuration is done.
+ */
+void
+CS104_Slave_startThreadless(CS104_Slave self);
+
+/**
+ * \brief Stop the server in non-threaded mode
+ *
+ * Stop listening to incoming TCP/IP connections and close all open connections.
+ * Event buffers will be deactivated.
+ */
+void
+CS104_Slave_stopThreadless(CS104_Slave self);
+
+/**
+ * \brief Protocol stack tick function for non-threaded mode.
+ *
+ * Handle incoming connection requests and messages, send buffered events, and
+ * handle periodic tasks.
+ *
+ * NOTE: This function has to be called periodically by the application.
+ */
+void
+CS104_Slave_tick(CS104_Slave self);
+
+/*
+ * \brief Gets the number of ASDU in the low-priority queue
+ *
+ * NOTE: Mode CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP is not supported by this function.
+ *
+ * \param redGroup the redundancy group to use or NULL for single redundancy mode
+ *
+ * \return the number of ASDU in the low-priority queue
+ */
+int
+CS104_Slave_getNumberOfQueueEntries(CS104_Slave self, CS104_RedundancyGroup redGroup);
+
+/**
+ * \brief Add an ASDU to the low-priority queue of the slave (use for periodic and spontaneous messages)
+ *
+ * \param asdu the ASDU to add
+ */
+void
+CS104_Slave_enqueueASDU(CS104_Slave self, CS101_ASDU asdu);
+
+/**
+ * \brief Add a new redundancy group to the server.
+ *
+ * A redundancy group is a group of clients that share the same event queue. This function can
+ * only be used with server mode CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS.
+ *
+ * NOTE: Has to be called before the server is started!
+ *
+ * \param redundancyGroup the new redundancy group
+ */
+void
+CS104_Slave_addRedundancyGroup(CS104_Slave self, CS104_RedundancyGroup redundancyGroup);
+
+/**
+ * \brief Delete the slave instance. Release all resources.
+ */
+void
+CS104_Slave_destroy(CS104_Slave self);
+
+/**
+ * \brief Create a new redundancy group.
+ *
+ * A redundancy group is a group of clients that share the same event queue. Redundancy groups can
+ * only be used with server mode CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS.
+ */
+CS104_RedundancyGroup
+CS104_RedundancyGroup_create(const char* name);
+
+/**
+ * \brief Add an allowed client to the redundancy group
+ *
+ * \param ipAddress the IP address of the client as C string (can be IPv4 or IPv6 address).
+ */
+void
+CS104_RedundancyGroup_addAllowedClient(CS104_RedundancyGroup self, const char* ipAddress);
+
+/**
+ * \brief Add an allowed client to the redundancy group
+ *
+ * \param ipAddress the IP address as byte buffer (4 byte for IPv4, 16 byte for IPv6)
+ * \param addressType type of the IP address (either IP_ADDRESS_TYPE_IPV4 or IP_ADDRESS_TYPE_IPV6)
+ */
+void
+CS104_RedundancyGroup_addAllowedClientEx(CS104_RedundancyGroup self, const uint8_t* ipAddress, eCS104_IPAddressType addressType);
+
+/**
+ * \brief Destroy the instance and release all resources.
+ *
+ * NOTE: This function will be called by \ref CS104_Slave_destroy. After using
+ * the \ref CS104_Slave_addRedundancyGroup function the redundancy group object must
+ * not be destroyed manually.
+ */
+void
+CS104_RedundancyGroup_destroy(CS104_RedundancyGroup self);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_INC_API_CS104_SLAVE_H_ */

+ 888 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/iec60870_common.h

@@ -0,0 +1,888 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_IEC60870_COMMON_H_
+#define SRC_INC_IEC60870_COMMON_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file iec60870_common.h
+ * \brief Common definitions for IEC 60870-5-101/104
+ * These types are used by CS101/CS104 master and slaves
+ */
+
+/**
+ * @addtogroup COMMON Common API functions
+ *
+ * @{
+ */
+
+
+#define IEC_60870_5_104_DEFAULT_PORT 2404
+#define IEC_60870_5_104_DEFAULT_TLS_PORT 19998
+
+#define LIB60870_VERSION_MAJOR 2
+#define LIB60870_VERSION_MINOR 3
+#define LIB60870_VERSION_PATCH 3
+
+/**
+ * \brief lib60870 version information
+ */
+typedef struct {
+    int major;
+    int minor;
+    int patch;
+} Lib60870VersionInfo;
+
+/**
+ * \brief link layer mode for serial link layers
+ */
+typedef enum {
+    IEC60870_LINK_LAYER_BALANCED = 0,
+    IEC60870_LINK_LAYER_UNBALANCED = 1
+} IEC60870_LinkLayerMode;
+
+/** \brief State of the link layer */
+typedef enum {
+    /** The link layer is idle, there is no communication */
+    LL_STATE_IDLE,
+
+    /** An error has occurred at the link layer, the link may not be usable */
+    LL_STATE_ERROR,
+
+    /** The link layer is busy and therefore no usable */
+    LL_STATE_BUSY,
+
+    /** The link is available for user data transmission and reception */
+    LL_STATE_AVAILABLE
+} LinkLayerState;
+
+/**
+ * \brief Callback handler for link layer state changes
+ *
+ * \param parameter user provided parameter that is passed to the handler
+ * \param address slave address used by the link layer state machine (only relevant for unbalanced master)
+ * \param newState the new link layer state
+ */
+typedef void (*IEC60870_LinkLayerStateChangedHandler) (void* parameter, int address, LinkLayerState newState);
+
+/**
+ * \brief Callback handler for sent and received messages
+ *
+ * This callback handler provides access to the raw message buffer of received or sent
+ * messages. It can be used for debugging purposes. Usually it is not used nor required
+ * for applications.
+ *
+ * \param parameter user provided parameter
+ * \param msg the message buffer
+ * \param msgSize size of the message
+ * \param sent indicates if the message was sent or received
+ */
+typedef void (*IEC60870_RawMessageHandler) (void* parameter, uint8_t* msg, int msgSize, bool sent);
+
+/**
+ * \brief Parameters for the CS101/CS104 application layer
+ */
+typedef struct sCS101_AppLayerParameters* CS101_AppLayerParameters;
+
+struct sCS101_AppLayerParameters {
+    int sizeOfTypeId;      /* size of the type id (default = 1 - don't change) */
+    int sizeOfVSQ;         /* don't change */
+    int sizeOfCOT;         /* size of COT (1/2 - default = 2 -> COT includes OA) */
+    int originatorAddress; /* originator address (OA) to use (0-255) */
+    int sizeOfCA;          /* size of common address (CA) of ASDU (1/2 - default = 2) */
+    int sizeOfIOA;         /* size of information object address (IOA) (1/2/3 - default = 3) */
+    int maxSizeOfASDU;     /* maximum size of the ASDU that is generated - the maximum maximum value is 249 for IEC 104 and 254 for IEC 101 */
+};
+
+/**
+ * \brief Message type IDs
+ */
+typedef enum {
+    M_SP_NA_1 = 1,
+    M_SP_TA_1 = 2,
+    M_DP_NA_1 = 3,
+    M_DP_TA_1 = 4,
+    M_ST_NA_1 = 5,
+    M_ST_TA_1 = 6,
+    M_BO_NA_1 = 7,
+    M_BO_TA_1 = 8,
+    M_ME_NA_1 = 9,
+    M_ME_TA_1 = 10,
+    M_ME_NB_1 = 11,
+    M_ME_TB_1 = 12,
+    M_ME_NC_1 = 13,
+    M_ME_TC_1 = 14,
+    M_IT_NA_1 = 15,
+    M_IT_TA_1 = 16,
+    M_EP_TA_1 = 17,
+    M_EP_TB_1 = 18,
+    M_EP_TC_1 = 19,
+    M_PS_NA_1 = 20,
+    M_ME_ND_1 = 21,
+    M_SP_TB_1 = 30,
+    M_DP_TB_1 = 31,
+    M_ST_TB_1 = 32,
+    M_BO_TB_1 = 33,
+    M_ME_TD_1 = 34,
+    M_ME_TE_1 = 35,
+    M_ME_TF_1 = 36,
+    M_IT_TB_1 = 37,
+    M_EP_TD_1 = 38,
+    M_EP_TE_1 = 39,
+    M_EP_TF_1 = 40,
+    S_IT_TC_1 = 41,
+    C_SC_NA_1 = 45,
+    C_DC_NA_1 = 46,
+    C_RC_NA_1 = 47,
+    C_SE_NA_1 = 48,
+    C_SE_NB_1 = 49,
+    C_SE_NC_1 = 50,
+    C_BO_NA_1 = 51,
+    C_SC_TA_1 = 58,
+    C_DC_TA_1 = 59,
+    C_RC_TA_1 = 60,
+    C_SE_TA_1 = 61,
+    C_SE_TB_1 = 62,
+    C_SE_TC_1 = 63,
+    C_BO_TA_1 = 64,
+    M_EI_NA_1 = 70,
+    S_CH_NA_1 = 81,
+    S_RP_NA_1 = 82,
+    S_AR_NA_1 = 83,
+    S_KR_NA_1 = 84,
+    S_KS_NA_1 = 85,
+    S_KC_NA_1 = 86,
+    S_ER_NA_1 = 87,
+    S_US_NA_1 = 90,
+    S_UQ_NA_1 = 91,
+    S_UR_NA_1 = 92,
+    S_UK_NA_1 = 93,
+    S_UA_NA_1 = 94,
+    S_UC_NA_1 = 95,
+    C_IC_NA_1 = 100,
+    C_CI_NA_1 = 101,
+    C_RD_NA_1 = 102,
+    C_CS_NA_1 = 103,
+    C_TS_NA_1 = 104,
+    C_RP_NA_1 = 105,
+    C_CD_NA_1 = 106,
+    C_TS_TA_1 = 107,
+    P_ME_NA_1 = 110,
+    P_ME_NB_1 = 111,
+    P_ME_NC_1 = 112,
+    P_AC_NA_1 = 113,
+    F_FR_NA_1 = 120,
+    F_SR_NA_1 = 121,
+    F_SC_NA_1 = 122,
+    F_LS_NA_1 = 123,
+    F_AF_NA_1 = 124,
+    F_SG_NA_1 = 125,
+    F_DR_TA_1 = 126,
+    F_SC_NB_1 = 127
+} IEC60870_5_TypeID;
+
+typedef IEC60870_5_TypeID TypeID;
+
+typedef struct sInformationObject* InformationObject;
+
+/**
+ * \brief Application Service Data Unit (ASDU) for the CS101/CS104 application layer
+ */
+typedef struct sCS101_ASDU* CS101_ASDU;
+
+typedef struct {
+    CS101_AppLayerParameters parameters;
+    uint8_t* asdu;
+    int asduHeaderLength;
+    uint8_t* payload;
+    int payloadSize;
+    uint8_t encodedData[256];
+} sCS101_StaticASDU;
+
+typedef sCS101_StaticASDU* CS101_StaticASDU;
+
+typedef struct sCP16Time2a* CP16Time2a;
+
+struct sCP16Time2a {
+    uint8_t encodedValue[2];
+};
+
+typedef struct sCP24Time2a* CP24Time2a;
+
+struct sCP24Time2a {
+    uint8_t encodedValue[3];
+};
+
+typedef struct sCP32Time2a* CP32Time2a;
+
+/**
+ * \brief 4 byte binary time
+ */
+struct sCP32Time2a {
+    uint8_t encodedValue[4];
+};
+
+/**
+ * \brief 7 byte binary time
+ */
+typedef struct sCP56Time2a* CP56Time2a;
+
+struct sCP56Time2a {
+    uint8_t encodedValue[7];
+};
+
+/**
+ * \brief Base type for counter readings
+ */
+typedef struct sBinaryCounterReading* BinaryCounterReading;
+
+struct sBinaryCounterReading {
+    uint8_t encodedValue[5];
+};
+
+/**
+ * \brief Parameters for CS104 connections - APCI (application protocol control information)
+ */
+typedef struct sCS104_APCIParameters* CS104_APCIParameters;
+
+struct sCS104_APCIParameters {
+    int k;
+    int w;
+    int t0;
+    int t1;
+    int t2;
+    int t3;
+};
+
+#include "cs101_information_objects.h"
+
+typedef enum {
+    CS101_COT_PERIODIC = 1,
+    CS101_COT_BACKGROUND_SCAN = 2,
+    CS101_COT_SPONTANEOUS = 3,
+    CS101_COT_INITIALIZED = 4,
+    CS101_COT_REQUEST = 5,
+    CS101_COT_ACTIVATION = 6,
+    CS101_COT_ACTIVATION_CON = 7,
+    CS101_COT_DEACTIVATION = 8,
+    CS101_COT_DEACTIVATION_CON = 9,
+    CS101_COT_ACTIVATION_TERMINATION = 10,
+    CS101_COT_RETURN_INFO_REMOTE = 11,
+    CS101_COT_RETURN_INFO_LOCAL = 12,
+    CS101_COT_FILE_TRANSFER = 13,
+    CS101_COT_AUTHENTICATION = 14,
+    CS101_COT_MAINTENANCE_OF_AUTH_SESSION_KEY = 15,
+    CS101_COT_MAINTENANCE_OF_USER_ROLE_AND_UPDATE_KEY = 16,
+    CS101_COT_INTERROGATED_BY_STATION = 20,
+    CS101_COT_INTERROGATED_BY_GROUP_1 = 21,
+    CS101_COT_INTERROGATED_BY_GROUP_2 = 22,
+    CS101_COT_INTERROGATED_BY_GROUP_3 = 23,
+    CS101_COT_INTERROGATED_BY_GROUP_4 = 24,
+    CS101_COT_INTERROGATED_BY_GROUP_5 = 25,
+    CS101_COT_INTERROGATED_BY_GROUP_6 = 26,
+    CS101_COT_INTERROGATED_BY_GROUP_7 = 27,
+    CS101_COT_INTERROGATED_BY_GROUP_8 = 28,
+    CS101_COT_INTERROGATED_BY_GROUP_9 = 29,
+    CS101_COT_INTERROGATED_BY_GROUP_10 = 30,
+    CS101_COT_INTERROGATED_BY_GROUP_11 = 31,
+    CS101_COT_INTERROGATED_BY_GROUP_12 = 32,
+    CS101_COT_INTERROGATED_BY_GROUP_13 = 33,
+    CS101_COT_INTERROGATED_BY_GROUP_14 = 34,
+    CS101_COT_INTERROGATED_BY_GROUP_15 = 35,
+    CS101_COT_INTERROGATED_BY_GROUP_16 = 36,
+    CS101_COT_REQUESTED_BY_GENERAL_COUNTER = 37,
+    CS101_COT_REQUESTED_BY_GROUP_1_COUNTER = 38,
+    CS101_COT_REQUESTED_BY_GROUP_2_COUNTER = 39,
+    CS101_COT_REQUESTED_BY_GROUP_3_COUNTER = 40,
+    CS101_COT_REQUESTED_BY_GROUP_4_COUNTER = 41,
+    CS101_COT_UNKNOWN_TYPE_ID = 44,
+    CS101_COT_UNKNOWN_COT = 45,
+    CS101_COT_UNKNOWN_CA = 46,
+    CS101_COT_UNKNOWN_IOA = 47
+} CS101_CauseOfTransmission;
+
+const char*
+CS101_CauseOfTransmission_toString(CS101_CauseOfTransmission self);
+
+void
+Lib60870_enableDebugOutput(bool value);
+
+Lib60870VersionInfo
+Lib60870_getLibraryVersionInfo(void);
+
+/**
+ * \brief Check if the test flag of the ASDU is set
+ */
+bool
+CS101_ASDU_isTest(CS101_ASDU self);
+
+/**
+ * \brief Set the test flag of the ASDU
+ */
+void
+CS101_ASDU_setTest(CS101_ASDU self, bool value);
+
+/**
+ * \brief Check if the negative flag of the ASDU is set
+ */
+bool
+CS101_ASDU_isNegative(CS101_ASDU self);
+
+/**
+ * \brief Set the negative flag of the ASDU
+ */
+void
+CS101_ASDU_setNegative(CS101_ASDU self, bool value);
+
+/**
+ * \brief get the OA (originator address) of the ASDU.
+ */
+int
+CS101_ASDU_getOA(CS101_ASDU self);
+
+/**
+ * \brief Get the cause of transmission (COT) of the ASDU
+ */
+CS101_CauseOfTransmission
+CS101_ASDU_getCOT(CS101_ASDU self);
+
+/**
+ * \brief Set the cause of transmission (COT) of the ASDU
+ */
+void
+CS101_ASDU_setCOT(CS101_ASDU self, CS101_CauseOfTransmission value);
+
+/**
+ * \brief Get the common address (CA) of the ASDU
+ */
+int
+CS101_ASDU_getCA(CS101_ASDU self);
+
+/**
+ * \brief Set the common address (CA) of the ASDU
+ *
+ * \param ca the ca in unstructured form
+ */
+void
+CS101_ASDU_setCA(CS101_ASDU self, int ca);
+
+
+/**
+ * \brief Get the type ID of the ASDU
+ *
+ * \return the type ID to identify the ASDU type
+ */
+IEC60870_5_TypeID
+CS101_ASDU_getTypeID(CS101_ASDU self);
+
+/**
+ * \brief Set the type ID of the ASDU
+ *
+ * NOTE: Usually it is not required to call this function as the type is determined when the
+ * ASDU is parsed or when a new information object is added with \ref CS101_ASDU_addInformationObject
+ * The function is intended to be used by library extensions of for creating private ASDU types.
+ *
+ * \param typeId the type ID to identify the ASDU type
+ */
+void
+CS101_ASDU_setTypeID(CS101_ASDU self, IEC60870_5_TypeID typeId);
+
+/**
+ * \brief Check if the ASDU contains a sequence of consecutive information objects
+ *
+ * NOTE: in a sequence of consecutive information objects only the first information object address
+ * is encoded. The following information objects ahve consecutive information object addresses.
+ *
+ * \return true when the ASDU represents a sequence of consecutive information objects, false otherwise
+ */
+bool
+CS101_ASDU_isSequence(CS101_ASDU self);
+
+/**
+ * \brief Set the ASDU to represent a sequence of consecutive information objects
+ *
+ * NOTE: It is not required to use this function when constructing ASDUs as this information is
+ * already provided when calling one of the constructors \ref CS101_ASDU_create or \ref CS101_ASDU_initializeStatic
+ *
+ * \param isSequence specify if the ASDU represents a sequence of consecutive information objects
+ */
+void
+CS101_ASDU_setSequence(CS101_ASDU self, bool isSequence);
+
+/**
+ * \brief Get the number of information objects (elements) in the ASDU
+ *
+ * \return the number of information objects/element (valid range 0 - 127)
+ */
+int
+CS101_ASDU_getNumberOfElements(CS101_ASDU self);
+
+/**
+ * \brief Set the number of information objects (elements) in the ASDU
+ *
+ * NOTE: Usually it is not required to call this function as the number of elements is set when the
+ * ASDU is parsed or when a new information object is added with \ref CS101_ASDU_addInformationObject
+ * The function is intended to be used by library extensions of for creating private ASDU types.
+ *
+ * \param numberOfElements the number of information objects/element (valid range 0 - 127)
+ */
+void
+CS101_ASDU_setNumberOfElements(CS101_ASDU self, int numberOfElements);
+
+/**
+ * \brief Get the information object with the given index
+ *
+ * \param index the index of the information object (starting with 0)
+ *
+ * \return the information object, or NULL if there is no information object with the given index
+ */
+InformationObject
+CS101_ASDU_getElement(CS101_ASDU self, int index);
+
+/**
+ * \brief Get the information object with the given index and store it in the provided information object instance
+ *
+ * \param io if not NULL use the provided information object instance to store the information, has to be of correct type.
+ * \param index the index of the information object (starting with 0)
+ *
+ * \return the information object, or NULL if there is no information object with the given index
+ */
+InformationObject
+CS101_ASDU_getElementEx(CS101_ASDU self, InformationObject io, int index);
+
+/**
+ * \brief Create a new ASDU. The type ID will be derived from the first InformationObject that will be added
+ *
+ * \param parameters the application layer parameters used to encode the ASDU
+ * \param isSequence if the information objects will be encoded as a compact sequence of information objects with subsequent IOA values
+ * \param cot cause of transmission (COT)
+ * \param oa originator address (OA) to be used
+ * \param ca the common address (CA) of the ASDU
+ * \param isTest if the test flag will be set or not
+ * \param isNegative if the negative falg will be set or not
+ *
+ * \return the new CS101_ASDU instance
+ */
+CS101_ASDU
+CS101_ASDU_create(CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca,
+        bool isTest, bool isNegative);
+
+/**
+ * \brief Create a new ASDU and store it in the provided static ASDU structure.
+ *
+ * NOTE: The type ID will be derived from the first InformationObject that will be added.
+ *
+ * \param self pointer to the statically allocated data structure
+ * \param parameters the application layer parameters used to encode the ASDU
+ * \param isSequence if the information objects will be encoded as a compact sequence of information objects with subsequent IOA values
+ * \param cot cause of transmission (COT)
+ * \param oa originator address (OA) to be used
+ * \param ca the common address (CA) of the ASDU
+ * \param isTest if the test flag will be set or not
+ * \param isNegative if the negative falg will be set or not
+ *
+ * \return the new CS101_ASDU instance
+ */
+CS101_ASDU
+CS101_ASDU_initializeStatic(CS101_StaticASDU self, CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca,
+        bool isTest, bool isNegative);
+
+/**
+ * \brief Create a new ASDU that is an exact copy of the ASDU
+ * 
+ * \param self ASDU instance to be copied
+ * \param clone static ASDU instance where to store the cloned ASDU or NULL. When this parameter is NULL the function will allocate the memory for the clone
+ * 
+ * \return the cloned ASDU instance
+ */
+CS101_ASDU
+CS101_ASDU_clone(CS101_ASDU self, CS101_StaticASDU clone);
+
+/**
+ * Get the ASDU payload
+ *
+ * The payload is the ASDU message part after the ASDU header (type ID, VSQ, COT, CASDU)
+ *
+ * \return the ASDU payload buffer
+ */
+uint8_t*
+CS101_ASDU_getPayload(CS101_ASDU self);
+
+/**
+ * \brief Append the provided data to the ASDU payload
+ *
+ * NOTE: Usually it is not required to call this function as the payload is set when the
+ * ASDU is parsed or when a new information object is added with \ref CS101_ASDU_addInformationObject
+ * The function is intended to be only used by library extensions of for creating private ASDU types.
+ *
+ * \param buffer pointer to the payload data to add
+ * \param size number of bytes to append to the payload
+ *
+ * \return true when data has been added, false otherwise
+ */
+bool
+CS101_ASDU_addPayload(CS101_ASDU self, uint8_t* buffer, int size);
+
+/**
+ * Get the ASDU payload buffer size
+ *
+ * The payload is the ASDU message part after the ASDU header (type ID, VSQ, COT, CASDU)
+ *
+ * \return the ASDU payload buffer size
+ */
+int
+CS101_ASDU_getPayloadSize(CS101_ASDU self);
+
+/**
+ * \brief Destroy the ASDU object (release all resources)
+ */
+void
+CS101_ASDU_destroy(CS101_ASDU self);
+
+/**
+ * \brief add an information object to the ASDU
+ *
+ * NOTE: Only information objects of the exact same type can be added to a single ASDU!
+ *
+ * \param self ASDU object instance
+ * \param io information object to be added
+ *
+ * \return true when added, false when there not enough space left in the ASDU or IO cannot be added to the sequence because of wrong IOA, or wrong type.
+ */
+bool
+CS101_ASDU_addInformationObject(CS101_ASDU self, InformationObject io);
+
+/**
+ * \brief remove all information elements from the ASDU object
+ *
+ * \param self ASDU object instance
+ */
+void
+CS101_ASDU_removeAllElements(CS101_ASDU self);
+
+/**
+ * \brief Get the elapsed time in ms
+ */
+int
+CP16Time2a_getEplapsedTimeInMs(const CP16Time2a self);
+
+/**
+ * \brief set the elapsed time in ms
+ */
+void
+CP16Time2a_setEplapsedTimeInMs(CP16Time2a self, int value);
+
+/**
+ * \brief Get the millisecond part of the time value
+ */
+int
+CP24Time2a_getMillisecond(const CP24Time2a self);
+
+/**
+ * \brief Set the millisecond part of the time value
+ */
+void
+CP24Time2a_setMillisecond(CP24Time2a self, int value);
+
+/**
+ * \brief Get the second part of the time value
+ */
+int
+CP24Time2a_getSecond(const CP24Time2a self);
+
+/**
+ * \brief Set the second part of the time value
+ */
+void
+CP24Time2a_setSecond(CP24Time2a self, int value);
+
+/**
+ * \brief Get the minute part of the time value
+ */
+int
+CP24Time2a_getMinute(const CP24Time2a self);
+
+/**
+ * \brief Set the minute part of the time value
+ */
+void
+CP24Time2a_setMinute(CP24Time2a self, int value);
+
+/**
+ * \brief Check if the invalid flag of the time value is set
+ */
+bool
+CP24Time2a_isInvalid(const CP24Time2a self);
+
+/**
+ * \brief Set the invalid flag of the time value
+ */
+void
+CP24Time2a_setInvalid(CP24Time2a self, bool value);
+
+/**
+ * \brief Check if the substituted flag of the time value is set
+ */
+bool
+CP24Time2a_isSubstituted(const CP24Time2a self);
+
+/**
+ * \brief Set the substituted flag of the time value
+ */
+void
+CP24Time2a_setSubstituted(CP24Time2a self, bool value);
+
+/**
+ * \brief Create a 7 byte time from a UTC ms timestamp
+ */
+CP56Time2a
+CP56Time2a_createFromMsTimestamp(CP56Time2a self, uint64_t timestamp);
+
+
+CP32Time2a
+CP32Time2a_create(CP32Time2a self);
+
+void
+CP32Time2a_setFromMsTimestamp(CP32Time2a self, uint64_t timestamp);
+
+int
+CP32Time2a_getMillisecond(const CP32Time2a self);
+
+void
+CP32Time2a_setMillisecond(CP32Time2a self, int value);
+
+int
+CP32Time2a_getSecond(const CP32Time2a self);
+
+void
+CP32Time2a_setSecond(CP32Time2a self, int value);
+
+int
+CP32Time2a_getMinute(const CP32Time2a self);
+
+
+void
+CP32Time2a_setMinute(CP32Time2a self, int value);
+
+bool
+CP32Time2a_isInvalid(const CP32Time2a self);
+
+void
+CP32Time2a_setInvalid(CP32Time2a self, bool value);
+
+bool
+CP32Time2a_isSubstituted(const CP32Time2a self);
+
+void
+CP32Time2a_setSubstituted(CP32Time2a self, bool value);
+
+int
+CP32Time2a_getHour(const CP32Time2a self);
+
+void
+CP32Time2a_setHour(CP32Time2a self, int value);
+
+bool
+CP32Time2a_isSummerTime(const CP32Time2a self);
+
+void
+CP32Time2a_setSummerTime(CP32Time2a self, bool value);
+
+/**
+ * \brief Set the time value of a 7 byte time from a UTC ms timestamp
+ */
+void
+CP56Time2a_setFromMsTimestamp(CP56Time2a self, uint64_t timestamp);
+
+/**
+ * \brief Convert a 7 byte time to a ms timestamp
+ */
+uint64_t
+CP56Time2a_toMsTimestamp(const CP56Time2a self);
+
+/**
+ * \brief Get the ms part of a time value
+ */
+int
+CP56Time2a_getMillisecond(const CP56Time2a self);
+
+/**
+ * \brief Set the ms part of a time value
+ */
+void
+CP56Time2a_setMillisecond(CP56Time2a self, int value);
+
+int
+CP56Time2a_getSecond(const CP56Time2a self);
+
+void
+CP56Time2a_setSecond(CP56Time2a self, int value);
+
+int
+CP56Time2a_getMinute(const CP56Time2a self);
+
+void
+CP56Time2a_setMinute(CP56Time2a self, int value);
+
+int
+CP56Time2a_getHour(const CP56Time2a self);
+
+void
+CP56Time2a_setHour(CP56Time2a self, int value);
+
+int
+CP56Time2a_getDayOfWeek(const CP56Time2a self);
+
+void
+CP56Time2a_setDayOfWeek(CP56Time2a self, int value);
+
+int
+CP56Time2a_getDayOfMonth(const CP56Time2a self);
+
+void
+CP56Time2a_setDayOfMonth(CP56Time2a self, int value);
+
+/**
+ * \brief Get the month field of the time
+ *
+ * \return value the month (1..12)
+ */
+int
+CP56Time2a_getMonth(const CP56Time2a self);
+
+/**
+ * \brief Set the month field of the time
+ *
+ * \param value the month (1..12)
+ */
+void
+CP56Time2a_setMonth(CP56Time2a self, int value);
+
+/**
+ * \brief Get the year (range 0..99)
+ *
+ * \param value the year (0..99)
+ */
+int
+CP56Time2a_getYear(const CP56Time2a self);
+
+/**
+ * \brief Set the year
+ *
+ * \param value the year
+ */
+void
+CP56Time2a_setYear(CP56Time2a self, int value);
+
+bool
+CP56Time2a_isSummerTime(const CP56Time2a self);
+
+void
+CP56Time2a_setSummerTime(CP56Time2a self, bool value);
+
+bool
+CP56Time2a_isInvalid(const CP56Time2a self);
+
+void
+CP56Time2a_setInvalid(CP56Time2a self, bool value);
+
+bool
+CP56Time2a_isSubstituted(const CP56Time2a self);
+
+void
+CP56Time2a_setSubstituted(CP56Time2a self, bool value);
+
+BinaryCounterReading
+BinaryCounterReading_create(BinaryCounterReading self, int32_t value, int seqNumber,
+        bool hasCarry, bool isAdjusted, bool isInvalid);
+
+void
+BinaryCounterReading_destroy(BinaryCounterReading self);
+
+int32_t
+BinaryCounterReading_getValue(BinaryCounterReading self);
+
+void
+BinaryCounterReading_setValue(BinaryCounterReading self, int32_t value);
+
+int
+BinaryCounterReading_getSequenceNumber(BinaryCounterReading self);
+
+bool
+BinaryCounterReading_hasCarry(BinaryCounterReading self);
+
+bool
+BinaryCounterReading_isAdjusted(BinaryCounterReading self);
+
+bool
+BinaryCounterReading_isInvalid(BinaryCounterReading self);
+
+void
+BinaryCounterReading_setSequenceNumber(BinaryCounterReading self, int value);
+
+void
+BinaryCounterReading_setCarry(BinaryCounterReading self, bool value);
+
+void
+BinaryCounterReading_setAdjusted(BinaryCounterReading self, bool value);
+
+void
+BinaryCounterReading_setInvalid(BinaryCounterReading self, bool value);
+
+/**
+ * @}
+ */
+
+typedef struct sFrame* Frame;
+
+void
+Frame_destroy(Frame self);
+
+void
+Frame_resetFrame(Frame self);
+
+void
+Frame_setNextByte(Frame self, uint8_t byte);
+
+void
+Frame_appendBytes(Frame self, uint8_t* bytes, int numberOfBytes);
+
+int
+Frame_getMsgSize(Frame self);
+
+uint8_t*
+Frame_getBuffer(Frame self);
+
+int
+Frame_getSpaceLeft(Frame self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_INC_IEC60870_COMMON_H_ */

+ 59 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/iec60870_master.h

@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60870_MASTER_H_
+#define SRC_IEC60870_MASTER_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "iec60870_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file iec60870_master.h
+ * \brief Common master side definitions for IEC 60870-5-101/104
+ * These types are used by CS101/CS104 master
+ */
+
+/**
+ * \brief Callback handler for received ASDUs
+ *
+ * This callback handler will be called for each received ASDU.
+ * The CS101_ASDU object that is passed is only valid in the context
+ * of the callback function.
+ *
+ * \param parameter user provided parameter
+ * \param address address of the sender (slave/other station) - undefined for CS 104
+ * \param asdu object representing the received ASDU
+ *
+ * \return true if the ASDU has been handled by the callback, false otherwise
+ */
+typedef bool (*CS101_ASDUReceivedHandler) (void* parameter, int address, CS101_ASDU asdu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_IEC60870_MASTER_H_ */

+ 268 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/iec60870_slave.h

@@ -0,0 +1,268 @@
+/*
+ *  Copyright 2016-2018 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60750_SLAVE_H_
+#define SRC_IEC60750_SLAVE_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "iec60870_common.h"
+#include "tls_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file iec60870_slave.h
+ * \brief Common slave side definitions for IEC 60870-5-101/104
+ * These types are used by CS101/CS104 slaves
+ */
+
+/**
+ * @addtogroup SLAVE Slave related functions
+ *
+ * @{
+ */
+
+/**
+ * @defgroup COMMON_SLAVE Common slave related functions and interfaces
+ *
+ * These definitions are used by both the CS 101 and CS 104 slave implementations.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup IMASTER_CONNECTION IMasterConnection interface
+ *
+ * @{
+ */
+
+/**
+ * \brief Interface to send messages to the master (used by slave)
+ */
+typedef struct sIMasterConnection* IMasterConnection;
+
+struct sIMasterConnection {
+    bool (*isReady) (IMasterConnection self);
+    bool (*sendASDU) (IMasterConnection self, CS101_ASDU asdu);
+    bool (*sendACT_CON) (IMasterConnection self, CS101_ASDU asdu, bool negative);
+    bool (*sendACT_TERM) (IMasterConnection self, CS101_ASDU asdu);
+    void (*close) (IMasterConnection self);
+    int (*getPeerAddress) (IMasterConnection self, char* addrBuf, int addrBufSize);
+    CS101_AppLayerParameters (*getApplicationLayerParameters) (IMasterConnection self);
+    void* object;
+};
+
+/*
+ * \brief Check if the connection is ready to send an ASDU.
+ *
+ * \deprecated Use one of the send functions (e.g. \ref IMasterConnection_sendASDU) and evaluate
+ * the return value.
+ *
+ * NOTE: The functions returns true when the connection is activated, the ASDU can be immediately sent,
+ * or the queue has enough space to store another ASDU.
+ *
+ * \param self the connection object (this is usually received as a parameter of a callback function)
+ *
+ * \returns true if the connection is ready to send an ASDU, false otherwise
+ */
+bool
+IMasterConnection_isReady(IMasterConnection self);
+
+/**
+ * \brief Send an ASDU to the client/master
+ *
+ * NOTE: ASDU instance has to be released by the caller!
+ *
+ * \param self the connection object (this is usually received as a parameter of a callback function)
+ * \param asdu the ASDU to send to the client/master
+ *
+ * \return true when the ASDU has been sent or queued for transmission, false otherwise
+ */
+bool
+IMasterConnection_sendASDU(IMasterConnection self, CS101_ASDU asdu);
+
+/**
+ * \brief Send an ACT_CON ASDU to the client/master
+ *
+ * ACT_CON is used for a command confirmation (positive or negative)
+ *
+ * \param asdu the ASDU to send to the client/master
+ * \param negative value of the negative flag
+ *
+ * \return true when the ASDU has been sent or queued for transmission, false otherwise
+ */
+bool
+IMasterConnection_sendACT_CON(IMasterConnection self, CS101_ASDU asdu, bool negative);
+
+/**
+ * \brief Send an ACT_TERM ASDU to the client/master
+ *
+ * ACT_TERM is used to indicate that the command execution is complete.
+ *
+ * \param asdu the ASDU to send to the client/master
+ *
+ * \return true when the ASDU has been sent or queued for transmission, false otherwise
+ */
+bool
+IMasterConnection_sendACT_TERM(IMasterConnection self, CS101_ASDU asdu);
+
+/**
+ * \brief Get the peer address of the master (only for CS 104)
+ *
+ * \param addrBuf buffer where to store the IP address as string
+ * \param addrBufSize the size of the buffer where to store the IP address
+ *
+ * \return the number of bytes written to the buffer, 0 if function not supported
+ */
+int
+IMasterConnection_getPeerAddress(IMasterConnection self, char* addrBuf, int addrBufSize);
+
+/**
+ * \brief Close the master connection (only for CS 104)
+ *
+ * Allows the slave to actively close a master connection (e.g. when some exception occurs)
+ */
+void
+IMasterConnection_close(IMasterConnection self);
+
+/**
+ * \brief Get the application layer parameters used by this connection
+ */
+CS101_AppLayerParameters
+IMasterConnection_getApplicationLayerParameters(IMasterConnection self);
+
+/**
+ * @}
+ */
+
+
+/**
+ * @defgroup SLAVE_PLUGIN Slave plugin interface
+ *
+ * Plugin interface to add functionality to the slave (e.g. file server)
+ */
+
+typedef enum
+{
+    CS101_PLUGIN_RESULT_NOT_HANDLED = 0,
+    CS101_PLUGIN_RESULT_HANDLED = 1,
+    CS101_PLUGIN_RESULT_INVALID_ASDU = 2
+} CS101_SlavePlugin_Result;
+
+/**
+ * \brief Plugin interface for CS101 or CS104 slaves
+ */
+typedef struct sCS101_SlavePlugin* CS101_SlavePlugin;
+
+struct sCS101_SlavePlugin
+{
+    CS101_SlavePlugin_Result (*handleAsdu) (void* parameter, IMasterConnection connection, CS101_ASDU asdu);
+    void (*runTask) (void* parameter, IMasterConnection connection);
+
+    void* parameter;
+};
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup CALLBACK_HANDLERS Slave callback handlers
+ *
+ * Callback handlers to handle events in the slave
+ */
+
+/**
+ * \brief Handler will be called when a link layer reset CU (communication unit) message is received
+ *
+ * NOTE: Can be used to empty the ASDU queues
+ *
+ * \param parameter a user provided parameter
+ */
+typedef void (*CS101_ResetCUHandler) (void* parameter);
+
+/**
+ * \brief Handler for interrogation command (C_IC_NA_1 - 100).
+ */
+typedef bool (*CS101_InterrogationHandler) (void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi);
+
+/**
+ * \brief Handler for counter interrogation command (C_CI_NA_1 - 101).
+ */
+typedef bool (*CS101_CounterInterrogationHandler) (void* parameter, IMasterConnection connection, CS101_ASDU asdu, QualifierOfCIC qcc);
+
+/**
+ * \brief Handler for read command (C_RD_NA_1 - 102)
+ */
+typedef bool (*CS101_ReadHandler) (void* parameter, IMasterConnection connection, CS101_ASDU asdu, int ioa);
+
+/**
+ * \brief Handler for clock synchronization command (C_CS_NA_1 - 103)
+ *
+ * This handler will be called whenever a time synchronization command is received.
+ * NOTE: The \ref CS104_Slave instance will automatically send an ACT-CON message for the received time sync command.
+ *
+ * \param[in] parameter user provided parameter
+ * \param[in] connection represents the (TCP) connection that received the time sync command
+ * \param[in] asdu the received ASDU
+ * \param[in,out] the time received with the time sync message. The user can update this time for the ACT-CON message
+ *
+ * \return true when time synchronization has been successful, false otherwise
+ */
+typedef bool (*CS101_ClockSynchronizationHandler) (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime);
+
+/**
+ * \brief Handler for reset process command (C_RP_NA_1 - 105)
+ */
+typedef bool (*CS101_ResetProcessHandler ) (void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qrp);
+
+/**
+ * \brief Handler for delay acquisition command (C_CD_NA:1 - 106)
+ */
+typedef bool (*CS101_DelayAcquisitionHandler) (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP16Time2a delayTime);
+
+/**
+ * \brief Handler for ASDUs that are not handled by other handlers (default handler)
+ */
+typedef bool (*CS101_ASDUHandler) (void* parameter, IMasterConnection connection, CS101_ASDU asdu);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* SRC_IEC60750_SLAVE_H_ */

+ 54 - 0
app/IEC_SERVER/lib60870-C/src/inc/api/link_layer_parameters.h

@@ -0,0 +1,54 @@
+/*
+ *  link_layer_parameters.h
+ *
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60870_LINK_LAYER_LINK_LAYER_PARAMETERS_H_
+#define SRC_IEC60870_LINK_LAYER_LINK_LAYER_PARAMETERS_H_
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file link_layer_parameters.h
+ *
+ * \brief Parameters for serial link layers
+ */
+
+/** \brief Parameters for the IEC 60870-5 link layer */
+typedef struct sLinkLayerParameters* LinkLayerParameters;
+
+struct sLinkLayerParameters {
+    int addressLength; /** Length of link layer address (1 or 2 byte) */
+    int timeoutForAck; /** timeout for link layer ACK in ms */
+    int timeoutRepeat; /** timeout for repeated message transmission when no ACK received in ms */
+    bool useSingleCharACK; /** use single char ACK for ACK (FC=0) or RESP_NO_USER_DATA (FC=9) */
+    int timeoutLinkState; /** interval to repeat request status of link (FC=9) after response timeout */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_IEC60870_LINK_LAYER_LINK_LAYER_PARAMETERS_H_ */

+ 65 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/apl_types_internal.h

@@ -0,0 +1,65 @@
+/*
+ *  apl_types_internal.h
+ *
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_APL_TYPES_INTERNAL_H_
+#define SRC_INC_APL_TYPES_INTERNAL_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "frame.h"
+#include "iec60870_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+CS101_ASDU_encode(CS101_ASDU self, Frame frame);
+
+bool
+CP16Time2a_getFromBuffer (CP16Time2a self, const uint8_t* msg, int msgSize, int startIndex);
+
+uint8_t*
+CP16Time2a_getEncodedValue(CP16Time2a self);
+
+bool
+CP24Time2a_getFromBuffer (CP24Time2a self, const uint8_t* msg, int msgSize, int startIndex);
+
+bool
+CP32Time2a_getFromBuffer (CP32Time2a self, const uint8_t* msg, int msgSize, int startIndex);
+
+uint8_t*
+CP32Time2a_getEncodedValue(CP32Time2a self);
+
+bool
+CP56Time2a_getFromBuffer (CP56Time2a self, const uint8_t* msg, int msgSize, int startIndex);
+
+uint8_t*
+CP56Time2a_getEncodedValue(CP56Time2a self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_INC_APL_TYPES_INTERNAL_H_ */

+ 70 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/buffer_frame.h

@@ -0,0 +1,70 @@
+/*
+ *  Copyright 2017-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60870_T104_BUFFER_FRAME_H_
+#define SRC_IEC60870_T104_BUFFER_FRAME_H_
+
+#include "frame.h"
+
+#include <stdbool.h>
+
+struct sBufferFrame {
+    FrameVFT virtualFunctionTable;
+
+    uint8_t* buffer;
+    int msgSize;
+    int startSize;
+    bool isUsed;
+};
+
+typedef struct sBufferFrame* BufferFrame;
+
+Frame
+BufferFrame_initialize(BufferFrame self, uint8_t* buffer, int startSize);
+
+void
+BufferFrame_destroy(Frame super);
+
+void
+BufferFrame_resetFrame(Frame super);
+
+void
+BufferFrame_setNextByte(Frame super, uint8_t byte);
+
+void
+BufferFrame_appendBytes(Frame super, const uint8_t* bytes, int numberOfBytes);
+
+int
+BufferFrame_getMsgSize(Frame super);
+
+uint8_t*
+BufferFrame_getBuffer(Frame super);
+
+int
+BufferFrame_getSpaceLeft(Frame super);
+
+bool
+BufferFrame_isUsed(BufferFrame self);
+
+void
+BufferFrame_markAsUsed(BufferFrame self);
+
+#endif /* SRC_IEC60870_T104_BUFFER_FRAME_H_ */

+ 59 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/cs101_asdu_internal.h

@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2016, 2017 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+
+#ifndef SRC_INC_INTERNAL_CS101_ASDU_INTERNAL_H_
+#define SRC_INC_INTERNAL_CS101_ASDU_INTERNAL_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "iec60870_common.h"
+#include "information_objects_internal.h"
+#include "apl_types_internal.h"
+#include "lib_memory.h"
+#include "lib60870_internal.h"
+
+struct sCS101_ASDU {
+    CS101_AppLayerParameters parameters;
+    uint8_t* asdu;
+    int asduHeaderLength;
+    uint8_t* payload;
+    int payloadSize;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief create a new (read-only) instance
+ *
+ * NOTE: Do not try to append information objects to the instance!
+ */
+CS101_ASDU
+CS101_ASDU_createFromBuffer(CS101_AppLayerParameters parameters, uint8_t* msg, int msgLength);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_INC_INTERNAL_CS101_ASDU_INTERNAL_H_ */

+ 106 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/cs101_queue.h

@@ -0,0 +1,106 @@
+/*
+ *  cs101_queue.h
+ *
+ *  Copyright 2017 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_INTERNAL_CS101_QUEUE_H_
+#define SRC_INC_INTERNAL_CS101_QUEUE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if ((CONFIG_USE_THREADS == 1) || (CONFIG_USE_SEMAPHORES == 1))
+#include "hal_thread.h"
+#endif
+
+#ifdef CONFIG_SLAVE_MESSAGE_QUEUE_SIZE
+#define CS101_MAX_QUEUE_SIZE CONFIG_SLAVE_MESSAGE_QUEUE_SIZE
+#else
+#define CS101_MAX_QUEUE_SIZE 10
+#endif
+
+#include "stdint.h"
+#include "buffer_frame.h"
+typedef struct sCS101_QueueElement* CS101_QueueElement;
+
+struct sCS101_QueueElement {
+    uint8_t size;
+    uint8_t buffer[256];
+};
+
+typedef struct sCS101_Queue* CS101_Queue;
+
+struct sCS101_Queue {
+
+    int size;
+    int entryCounter;
+    int lastMsgIndex;
+    int firstMsgIndex;
+
+    struct sBufferFrame encodeFrame;
+
+#if (CS101_MAX_QUEUE_SIZE == -1)
+    struct sCS101_QueueElement* elements;
+#else
+    struct sCS101_QueueElement elements[CS101_MAX_QUEUE_SIZE];
+#endif
+
+#if (CONFIG_USE_SEMAPHORES == 1)
+    Semaphore queueLock;
+#endif
+};
+
+void
+CS101_Queue_initialize(CS101_Queue self, int maxQueueSize);
+
+void
+CS101_Queue_dispose(CS101_Queue self);
+
+void
+CS101_Queue_lock(CS101_Queue self);
+
+void
+CS101_Queue_unlock(CS101_Queue self);
+
+void
+CS101_Queue_enqueue(CS101_Queue self, CS101_ASDU asdu);
+
+    /*
+     * NOTE: Locking has to be done by caller!
+     */
+Frame
+CS101_Queue_dequeue(CS101_Queue self, Frame resultStorage);
+
+bool
+CS101_Queue_isFull(CS101_Queue self);
+
+bool
+CS101_Queue_isEmpty(CS101_Queue self);
+
+void
+CS101_Queue_flush(CS101_Queue self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_INC_INTERNAL_CS101_QUEUE_H_ */

+ 59 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/cs104_frame.h

@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_T104_FRAME_H_
+#define SRC_INC_T104_FRAME_H_
+
+#include <stdint.h>
+
+#include "frame.h"
+
+typedef struct sT104Frame* T104Frame;
+
+T104Frame
+T104Frame_create(void);
+
+void
+T104Frame_destroy(Frame self);
+
+void
+T104Frame_resetFrame(Frame self);
+
+void
+T104Frame_prepareToSend(T104Frame self, int sendCounter, int receiveCounter);
+
+void
+T104Frame_setNextByte(Frame self, uint8_t byte);
+
+void
+T104Frame_appendBytes(Frame self, const uint8_t* bytes, int numberOfBytes);
+
+int
+T104Frame_getMsgSize(Frame self);
+
+uint8_t*
+T104Frame_getBuffer(Frame self);
+
+int
+T104Frame_getSpaceLeft(Frame self);
+
+
+#endif /* SRC_INC_T104_FRAME_H_ */

+ 41 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/frame.h

@@ -0,0 +1,41 @@
+/*
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_FRAME_H_
+#define SRC_INC_FRAME_H_
+
+#include <stdint.h>
+
+#include "iec60870_common.h"
+
+typedef struct sFrameVFT* FrameVFT;
+
+struct sFrameVFT {
+    void (*destroy)(Frame self);
+    void (*resetFrame)(Frame self);
+    void (*setNextByte)(Frame self, uint8_t byte);
+    void (*appendBytes)(Frame self, const uint8_t* bytes, int numberOfBytes);
+    int (*getMsgSize)(Frame self);
+    uint8_t* (*getBuffer)(Frame self);
+    int (*getSpaceLeft)(Frame self);
+};
+
+#endif /* SRC_INC_FRAME_H_ */

Datei-Diff unterdrückt, da er zu groß ist
+ 1219 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/information_objects_internal.h


+ 41 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/lib60870_internal.h

@@ -0,0 +1,41 @@
+/*
+ *  Copyright 2016 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_INC_INTERNAL_LIB60870_INTERNAL_H_
+#define SRC_INC_INTERNAL_LIB60870_INTERNAL_H_
+
+#include "lib60870_config.h"
+
+void
+lib60870_debug_print(const char *format, ...);
+
+#if (CONFIG_DEBUG_OUTPUT == 1)
+#define DEBUG_PRINT(...)  do{ lib60870_debug_print(__VA_ARGS__ ); } while( false )
+#else
+#define DEBUG_PRINT(...) do{ } while ( false )
+#endif
+
+#define IEC60870_5_104_MAX_ASDU_LENGTH 249
+#define IEC60870_5_104_APCI_LENGTH 6
+
+#define UNUSED_PARAMETER(x) (void)(x)
+
+#endif /* SRC_INC_INTERNAL_LIB60870_INTERNAL_H_ */

+ 182 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/link_layer.h

@@ -0,0 +1,182 @@
+/*
+ *  link_layer.h
+ *
+ *  Copyright 2017 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60870_LINK_LAYER_LINK_LAYER_H_
+#define SRC_IEC60870_LINK_LAYER_LINK_LAYER_H_
+
+#include <stdbool.h>
+#include "iec60870_common.h"
+#include "frame.h"
+#include "buffer_frame.h"
+#include "serial_transceiver_ft_1_2.h"
+#include "link_layer_parameters.h"
+
+typedef struct sLinkLayer* LinkLayer;
+
+typedef struct sLinkLayerBalanced* LinkLayerBalanced;
+
+typedef struct sLinkLayerSecondaryUnbalanced* LinkLayerSecondaryUnbalanced;
+
+typedef struct sLinkLayerPrimaryUnbalanced* LinkLayerPrimaryUnbalanced;
+
+typedef struct sISecondaryApplicationLayer* ISecondaryApplicationLayer;
+
+struct sISecondaryApplicationLayer {
+    bool (*IsClass1DataAvailable) (void* parameter);
+    Frame (*GetClass1Data) (void* parameter, Frame frame);
+    Frame (*GetClass2Data) (void* parameter, Frame frame);
+    bool (*HandleReceivedData) (void* parameter, uint8_t* msg, bool isBroadcast, int userDataStart, int userDataLength);
+    void (*ResetCUReceived) (void* parameter, bool onlyFCB);
+};
+
+typedef struct sIPrimaryApplicationLayer* IPrimaryApplicationLayer;
+
+struct sIPrimaryApplicationLayer {
+    void (*AccessDemand) (void* parameter, int slaveAddress);
+    void (*UserData) (void* parameter, int slaveAddress, uint8_t* msg, int start, int length);
+    void (*Timeout) (void* parameter, int slaveAddress);
+};
+
+typedef struct sIBalancedApplicationLayer* IBalancedApplicationLayer;
+
+struct sIBalancedApplicationLayer {
+    Frame (*GetUserData) (void* parameter, Frame frame);
+    bool (*HandleReceivedData) (void* parameter, uint8_t* msg, bool isBroadcast, int userDataStart, int userDataLength);
+};
+
+
+LinkLayerPrimaryUnbalanced
+LinkLayerPrimaryUnbalanced_create(
+        SerialTransceiverFT12 transceiver,
+        LinkLayerParameters linkLayerParameters,
+        IPrimaryApplicationLayer applicationLayer,
+        void* applicationLayerParameter
+        );
+
+void
+LinkLayerPrimaryUnbalanced_destroy(LinkLayerPrimaryUnbalanced self);
+
+void
+LinkLayerPrimaryUnbalanced_setStateChangeHandler(LinkLayerPrimaryUnbalanced self,
+        IEC60870_LinkLayerStateChangedHandler handler, void* parameter);
+
+void
+LinkLayerPrimaryUnbalanced_addSlaveConnection(LinkLayerPrimaryUnbalanced self, int slaveAddress);
+
+void
+LinkLayerPrimaryUnbalanced_resetCU(LinkLayerPrimaryUnbalanced self, int slaveAddress);
+
+bool
+LinkLayerPrimaryUnbalanced_isChannelAvailable(LinkLayerPrimaryUnbalanced self, int slaveAddress);
+
+void
+LinkLayerPrimaryUnbalanced_sendLinkLayerTestFunction(LinkLayerPrimaryUnbalanced self, int slaveAddress);
+
+bool
+LinkLayerPrimaryUnbalanced_requestClass1Data(LinkLayerPrimaryUnbalanced self, int slaveAddress);
+
+bool
+LinkLayerPrimaryUnbalanced_requestClass2Data(LinkLayerPrimaryUnbalanced self, int slaveAddress);
+
+bool
+LinkLayerPrimaryUnbalanced_sendConfirmed(LinkLayerPrimaryUnbalanced self, int slaveAddress, BufferFrame message);
+
+bool
+LinkLayerPrimaryUnbalanced_sendNoReply(LinkLayerPrimaryUnbalanced self, int slaveAddress, BufferFrame message);
+
+void
+LinkLayerPrimaryUnbalanced_run(LinkLayerPrimaryUnbalanced self);
+
+
+
+
+LinkLayerSecondaryUnbalanced
+LinkLayerSecondaryUnbalanced_create(
+        int linkLayerAddress,
+        SerialTransceiverFT12 transceiver,
+        LinkLayerParameters linkLayerParameters,
+        ISecondaryApplicationLayer applicationLayer,
+        void* applicationLayerParameter
+);
+
+void
+LinkLayerSecondaryUnbalanced_destroy(LinkLayerSecondaryUnbalanced self);
+
+void
+LinkLayerSecondaryUnbalanced_run(LinkLayerSecondaryUnbalanced self);
+
+void
+LinkLayerSecondaryUnbalanced_setIdleTimeout(LinkLayerSecondaryUnbalanced self, int timeoutInMs);
+
+void
+LinkLayerSecondaryUnbalanced_setStateChangeHandler(LinkLayerSecondaryUnbalanced self,
+        IEC60870_LinkLayerStateChangedHandler handler, void* parameter);
+
+void
+LinkLayerSecondaryUnbalanced_setAddress(LinkLayerSecondaryUnbalanced self, int address);
+
+LinkLayerBalanced
+LinkLayerBalanced_create(
+        int linkLayerAddress,
+        SerialTransceiverFT12 transceiver,
+        LinkLayerParameters linkLayerParameters,
+        IBalancedApplicationLayer applicationLayer,
+        void* applicationLayerParameter
+        );
+
+void
+LinkLayerBalanced_setStateChangeHandler(LinkLayerBalanced self,
+        IEC60870_LinkLayerStateChangedHandler handler, void* parameter);
+
+void
+LinkLayerBalanced_sendLinkLayerTestFunction(LinkLayerBalanced self);
+
+void
+LinkLayerBalanced_setIdleTimeout(LinkLayerBalanced self, int timeoutInMs);
+
+void
+LinkLayerBalanced_setDIR(LinkLayerBalanced self, bool dir);
+
+void
+LinkLayerBalanced_setAddress(LinkLayerBalanced self, int address);
+
+void
+LinkLayerBalanced_setOtherStationAddress(LinkLayerBalanced self, int address);
+
+void
+LinkLayerBalanced_destroy(LinkLayerBalanced self);
+
+void
+LinkLayerBalanced_run(LinkLayerBalanced self);
+
+LinkLayer
+LinkLayer_init(LinkLayer self, int address, SerialTransceiverFT12 transceiver, LinkLayerParameters linkLayerParameters);
+
+void
+LinkLayer_setDIR(LinkLayer self, bool dir);
+
+void
+LinkLayer_setAddress(LinkLayer self, int address);
+
+
+#endif /* SRC_IEC60870_LINK_LAYER_LINK_LAYER_H_ */

+ 46 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/link_layer_private.h

@@ -0,0 +1,46 @@
+/*
+ *  link_layer_private.h
+ *
+ *  Copyright 2017 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60870_LINK_LAYER_LINK_LAYER_PRIVATE_H_
+#define SRC_IEC60870_LINK_LAYER_LINK_LAYER_PRIVATE_H_
+
+#define LL_FC_00_RESET_REMOTE_LINK 0
+#define LL_FC_01_RESET_USER_PROCESS 1
+#define LL_FC_02_TEST_FUNCTION_FOR_LINK 2
+#define LL_FC_03_USER_DATA_CONFIRMED 3
+#define LL_FC_04_USER_DATA_NO_REPLY 4
+#define LL_FC_07_RESET_FCB 7
+#define LL_FC_08_REQUEST_FOR_ACCESS_DEMAND 8
+#define LL_FC_09_REQUEST_LINK_STATUS 9
+#define LL_FC_10_REQUEST_USER_DATA_CLASS_1 10
+#define LL_FC_11_REQUEST_USER_DATA_CLASS_2 11
+
+#define LL_FC_00_ACK 0
+#define LL_FC_01_NACK 1
+#define LL_FC_08_RESP_USER_DATA 8
+#define LL_FC_09_RESP_NACK_NO_DATA 9
+#define LL_FC_11_STATUS_OF_LINK_OR_ACCESS_DEMAND 11
+#define LL_FC_14_SERVICE_NOT_FUNCTIONING 14
+#define LL_FC_15_SERVICE_NOT_IMPLEMENTED 15
+
+#endif /* SRC_IEC60870_LINK_LAYER_LINK_LAYER_PRIVATE_H_ */

+ 57 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/platform_endian.h

@@ -0,0 +1,57 @@
+/*
+ *  Copyright 2016 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef ENDIAN_H_
+#define ENDIAN_H_
+
+#include "lib60870_config.h"
+
+#ifndef PLATFORM_IS_BIGENDIAN
+#ifdef __GNUC__
+#ifdef __BYTE_ORDER__
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define PLATFORM_IS_BIGENDIAN 1
+#else
+#define PLATFORM_IS_BIGENDIAN 0
+#endif
+
+#else
+
+/* older versions of GCC have __BYTE_ORDER__ not defined! */
+#ifdef __PPC__
+#define PLATFORM_IS_BIGENDIAN 1
+#endif
+
+#ifdef __m68k__
+#define PLATFORM_IS_BIGENDIAN 1
+#endif
+
+#endif /* __BYTE_ORDER__ */
+#endif /* __GNUC__ */
+#endif
+
+#if (PLATFORM_IS_BIGENDIAN == 1)
+#  define ORDER_LITTLE_ENDIAN 0
+#else
+#  define ORDER_LITTLE_ENDIAN 1
+#endif
+
+#endif /* ENDIAN_H_ */

+ 59 - 0
app/IEC_SERVER/lib60870-C/src/inc/internal/serial_transceiver_ft_1_2.h

@@ -0,0 +1,59 @@
+/*
+ *  serial_transceiver_ft_1_2.h
+ *
+ *  Copyright 2017 MZ Automation GmbH
+ *
+ *  This file is part of lib60870-C
+ *
+ *  lib60870-C is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  lib60870-C is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SRC_IEC60870_LINK_LAYER_SERIAL_TRANSCEIVER_FT_1_2_H_
+#define SRC_IEC60870_LINK_LAYER_SERIAL_TRANSCEIVER_FT_1_2_H_
+
+#include "link_layer_parameters.h"
+#include "hal_serial.h"
+#include "iec60870_common.h"
+
+typedef struct sSerialTransceiverFT12* SerialTransceiverFT12;
+
+typedef void (*SerialTXMessageHandler) (void* parameter, uint8_t* msg, int msgSize);
+
+SerialTransceiverFT12
+SerialTransceiverFT12_create(SerialPort serialPort, LinkLayerParameters linkLayerParameters);
+
+void
+SerialTransceiverFT12_destroy(SerialTransceiverFT12 self);
+
+void
+SerialTransceiverFT12_setTimeouts(SerialTransceiverFT12 self, int messageTimeout, int characterTimeout);
+
+void
+SerialTransceiverFT12_setRawMessageHandler(SerialTransceiverFT12 self, IEC60870_RawMessageHandler handler, void* parameter);
+
+int
+SerialTransceiverFT12_getBaudRate(SerialTransceiverFT12 self);
+
+void
+SerialTransceiverFT12_sendMessage(SerialTransceiverFT12 self, uint8_t* msg, int msgSize);
+
+void
+SerialTransceiverFT12_readNextMessage(SerialTransceiverFT12 self, uint8_t* buffer,
+        SerialTXMessageHandler, void* parameter);
+
+#endif /* SRC_IEC60870_LINK_LAYER_SERIAL_TRANSCEIVER_FT_1_2_H_ */
+
+

+ 13 - 0
app/IEC_SERVER/lib60870-C/src/lib60870.pc.in

@@ -0,0 +1,13 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@/bin
+libdir=@CMAKE_INSTALL_PREFIX@/lib
+sharedlibdir=@CMAKE_INSTALL_PREFIX@/lib
+includedir=@CMAKE_INSTALL_PREFIX@/include
+
+Name: @PROJECT_NAME@
+Description: @CPACK_PACKAGE_DESCRIPTION@
+Version: @LIB_VERSION_MAJOR@.@LIB_VERSION_MINOR@.@LIB_VERSION_PATCH@
+
+Requires:
+Libs: -L${libdir} -L${sharedlibdir} -llib60870
+Cflags: -I${includedir}

+ 20 - 0
app/IEC_SERVER/lib60870-C/src/version.rc.in

@@ -0,0 +1,20 @@
+1 VERSIONINFO
+FILEVERSION     @LIB_VERSION_MAJOR@,@LIB_VERSION_MINOR@,@LIB_VERSION_PATCH@,0
+PRODUCTVERSION  @LIB_VERSION_MAJOR@,@LIB_VERSION_MINOR@,@LIB_VERSION_PATCH@,0
+BEGIN
+  BLOCK "StringFileInfo"
+  BEGIN
+    BLOCK "040904E4"
+    BEGIN
+      VALUE "FileVersion", "@LIB_VERSION_MAJOR@.@LIB_VERSION_MINOR@.@LIB_VERSION_PATCH@.0"
+      VALUE "ProductVersion", "@LIB_VERSION_MAJOR@.@LIB_VERSION_MINOR@.@LIB_VERSION_PATCH@.0"
+      VALUE "ProductName", "libIEC61850"
+      VALUE "FileDescription", "lib60870 - library for IEC 60870-5-104"
+      VALUE "LegalCopyright", "Dual license : Commercial or GPLv3"
+    END
+  END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x400, 1252
+    END
+END

+ 48 - 0
app/IEC_SERVER/lib60870-C/tests/CMakeLists.txt

@@ -0,0 +1,48 @@
+include_directories(
+   ./unity
+)
+
+set(tests_SRCS
+   all_tests.c
+   unity/unity.c
+)
+
+IF(WIN32)
+set_source_files_properties(${tests_SRCS}
+                                       PROPERTIES LANGUAGE CXX)
+ENDIF(WIN32)
+
+configure_file(server-key.pem server-key.pem COPYONLY)
+configure_file(client1-key.pem client1-key.pem COPYONLY)
+configure_file(client1.cer client1.cer COPYONLY)
+configure_file(client2.cer client2.cer COPYONLY)
+configure_file(root.cer root.cer COPYONLY)
+configure_file(server.cer server.cer COPYONLY)
+
+configure_file(certs/server_CA1_1.key server_CA1_1.key COPYONLY)
+configure_file(certs/server_CA1_1.pem server_CA1_1.pem COPYONLY)
+configure_file(certs/root_CA1.pem root_CA1.pem COPYONLY)
+
+configure_file(certs/client_CA1_1.key client_CA1_1.key COPYONLY)
+configure_file(certs/client_CA1_1.pem client_CA1_1.pem COPYONLY)
+
+configure_file(certs/client_CA1_2.key client_CA1_2.key COPYONLY)
+configure_file(certs/client_CA1_2.pem client_CA1_2.pem COPYONLY)
+
+configure_file(certs/client_CA1_3.key client_CA1_3.key COPYONLY)
+configure_file(certs/client_CA1_3.pem client_CA1_3.pem COPYONLY)
+
+configure_file(certs/client_CA1_4.key client_CA1_4.key COPYONLY)
+configure_file(certs/client_CA1_4.pem client_CA1_4.pem COPYONLY)
+
+configure_file(certs/server_CA1_1_chain.pem server_CA1_1_chain.pem)
+
+configure_file(certs/test.crl test.crl COPYONLY)
+
+add_executable(tests
+  ${tests_SRCS}
+)
+
+target_link_libraries(tests
+    lib60870
+)

Datei-Diff unterdrückt, da er zu groß ist
+ 6644 - 0
app/IEC_SERVER/lib60870-C/tests/all_tests.c


+ 35 - 0
app/IEC_SERVER/lib60870-C/tests/certs/README.md

@@ -0,0 +1,35 @@
+This folder include test certificates for TLS related tests
+
+* Three different root CAs
+** root_CA1
+** root_CA2
+** root_CA3
+
+* For each root CAs there are two clients and two server keys/certificates
+** server_CA1_1
+** server_CA1_2
+** client_CA1_1
+** client_CA1_2
+** server_CA2_1
+** server_CA2_2
+** client_CA2_1
+** client_CA2_2
+** server_CA3_1
+** server_CA3_2
+** client_CA3_1
+** client_CA3_2
+
+
+Example: Generate root CA keys and certificates:
+
+  openssl genrsa -out root_CA1.key 2048
+  openssl req -x509 -new -nodes -key root_CA1.key -sha256 -days 3650 -out root_CA1.pem
+
+Example: Create endpoint (client/server) certificates:
+
+  openssl genrsa -out server_CA1_2.key 2048
+
+  openssl req -new -key server_CA1_1.key -out server_CA1_1.csr
+
+  openssl x509 -req -in server_CA1_1.csr -CA root_CA1.pem -CAkey root_CA1.key -CAcreateserial -out server_CA1_1.pem
+

+ 27 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_1.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA2PuXMNUYO43NbEvBmEAru/uL1JdU6gFuhMKLuZOPaOGjGhth
+JiDO9AsnUGzKAk3m9QZ/YhAzY9CiEeYsnGaPeI0OBdkgWmpz5k9Fw+bqaqlxYQTy
+Bw69/kYbwNyMmGsb8XqXKZvhPXdLoaxkVmS+AMlxVcN/7c2ldZGTTDrhBtJnfuPK
+rGmH9cFg+XVvUskPQsUIwJtn1sN+niZ++hkuiCzmoZ4A+m73QACltdcr7aNtHJmh
+aU/p1bmLIhYfbxGmyvm2faJ8htYuaRBj6DcZq44IyDGz2LThmdWzpIcYbovCzB2X
+Pn26b0BXsXBpN+ptf2xpAEDWDdzaR6Xp4BgJwQIDAQABAoIBAQDQGLJOlgBQlVWv
+CBSaNOj8t2nKsHwylL7uujoQ95DxUH0BO8L3Mz3n1Y6V1lAC172pvtqKLOlsUBov
+OmYMdVwhjH4nY65gqHmRJvPMxviI5Qqktn58AEp8w7Y4SAza3NaGyECTGjlxnqi9
+XD06khGbZZa5Xu6hHboSwFPZJxrLU1jaopJUgFG+p9oUgiSp5cfGDwAsU9JELmkP
+MVF0GWedpypBBKi9JsniOulr1USpNZN2rzEkkxwY0QQttw3E9dgheIsut7dUYWLz
+9NLKcRWIK/Y29NzS6Urye8lUTHHBrXgk5pUcdN3vuY7mkleqIn5tYY6xf93/5/VA
+jF+HcgolAoGBAPDhS986xppbfLmrresIUUZKxk2s/Vg+vHPLX0SJFF7Uhy8nMYoJ
+JqfG2mS+/tiM/wPBglVhsrlsfnDIag7Brqx7sjH2OHO6VX8jQPYgOuCbNwp7uL1w
+bG82R5rujcxxFAtMVAM3zYz9sNGSu8u7M/U3kfTBwtntFJ6iPC60REbjAoGBAOaa
+SdtX0bOQAYDM4moEDVnRPMHp8lZAjqKphGqTDrGOqU4usNW8+ZNBhn3vF1+n2Gq5
+KY2IWSF0j71jqpOXahW0EBoXpcTLs5JBWet8J5vKzbpN8Uq8TvTABn67G1F/DZub
+FOiCDy/Kku4yWT2aUqNwS07va7gzFhyyjMl/JWoLAoGAATpEtriH9pVsx012r3H1
+aBRNemvdRqvbLgPlUmYYcntGzRi4CeoOBmDfEBBhIB1n108PKPw8evFwm4aJ89VM
+3JgsylBk7UIP2XwGgrqbUjW4TBdhU6XVB6QRLVr14grZfU1ASFvqckOAuTC0QE+N
+7jwARG0QXyf0KPLOt7Y3et0CgYEAhJcd9EJQTsB0PMyROofN7WDDYHPVZQaFfL2f
+Z2/auPjgHBX4k0yu6553aB17AQMPCn4giEJnjTbqFukhgO9EjeoUgAwswjSlsWhl
+/WJLm+ZF1+NM473WYB+xHFkU4gz9lATdRrrRZJdDWDYW3bbH4TWF94LuGuE0y5dW
+H909c/UCgYEAiYY/TTZvfEsQvCo4Rv6qg7cI2/OdGwGhMmtziYy4SIAAm0Ga2s3R
+L7Kq72In+nbaDIUD2zTSGQmwTm3B0C73vIUAXvupcl28nE5px0YNV6NZJaaFSV66
+hP1CgPBYe6KjnVufOiqhcnDdJQ6XdqK0tblj+cavkZgW+UdeqVBXCFQ=
+-----END RSA PRIVATE KEY-----

+ 20 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_1.pem

@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWzCCAkMCFFTJkICEIidmnrisIFxZ99KKLhDFMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAkRFMQswCQYDVQQIDAJCVzERMA8GA1UEBwwIRnJlaWJ1cmcxGzAZ
+BgNVBAoMEk1aIEF1dG9tYXRpb24gR21iSDEMMAoGA1UECwwDUiZEMRAwDgYDVQQD
+DAdyb290X0NBMB4XDTIyMDMxODA5MzMxOFoXDTIyMDQxNzA5MzMxOFowajELMAkG
+A1UEBhMCREUxCzAJBgNVBAgMAkJXMREwDwYDVQQHDAhGcmVpYnVyZzEbMBkGA1UE
+CgwSTVogQXV0b21hdGlvbiBHbWJIMQwwCgYDVQQLDANSJkQxEDAOBgNVBAMMB2Ns
+aWVudDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDY+5cw1Rg7jc1s
+S8GYQCu7+4vUl1TqAW6Ewou5k49o4aMaG2EmIM70CydQbMoCTeb1Bn9iEDNj0KIR
+5iycZo94jQ4F2SBaanPmT0XD5upqqXFhBPIHDr3+RhvA3IyYaxvxepcpm+E9d0uh
+rGRWZL4AyXFVw3/tzaV1kZNMOuEG0md+48qsaYf1wWD5dW9SyQ9CxQjAm2fWw36e
+Jn76GS6ILOahngD6bvdAAKW11yvto20cmaFpT+nVuYsiFh9vEabK+bZ9onyG1i5p
+EGPoNxmrjgjIMbPYtOGZ1bOkhxhui8LMHZc+fbpvQFexcGk36m1/bGkAQNYN3NpH
+pengGAnBAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADSnrKdPqeUr3F1MIk6P8SKo
+yR1VrPmNCljaC1i3realDlG+7jlPHfTCkwZwlEfKGa/yANJAw4hv+2tR5m4CsgMB
+x6FkKG9p6NTXyv4gXZeLa3ivqFqz7awTVMBf1C1VVeTi/H2kvHSBRmbj6Z5p7/MN
+9E1t5NsgbKKfbj4hQD+f7r6zgFdgTK8C5OYT2ijYryFl1Qqrl5CYPpswm3vL0KkM
+e3RMOBqamkFqr4OCZw5juNpGrp3bK3dLF+N6Ceb+PGnS0YU29NpUXo64lzIxdwxs
+NDqbFMYXEXGKqUDVQAuj1374M85Cvqlso0Jenc+hWN2/kfAgHGE1Ne3sD9oJg5w=
+-----END CERTIFICATE-----

+ 27 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_2.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAvIZ3f7Hf5yyazcH3ouZaYYEu9X6T+eu8YJKxmhMtP5ciEnrm
+CvJ5KxyN0+XM9CB7FURmotHED3az+dFXZ+5I9qzP1sw+0c0kWSAcnXeLZwmQc/25
+LU0bWDVb3u/h2j/beBcUHZDBeCtyZhP4/4GEmIpkePQxY1yNqJvZF1meqXUFMmil
+K/s8YJqL7UI7XO8HU3SprChUMUEIXc1kGoxKWjUy/iUycqwmXLa8miC4GL+HL0G6
+ReeM23EbHaD0ZD+ms7EfRg4QmMncM1BjK9gmdguAUcAfAobMA7FOosMOXpxdr1iF
+GN+lb45kgw67B9cOFfVApEo2DINFUDJsHPJHqwIDAQABAoIBAQC2JdVfcyS53sbS
+9g2lPpskigQs/VdLqRAg3prqGo0lW4h4AnYxLYUUuknt6aHUXW2OZsAjoDimGDJb
+tH+W0wt8CgvlpQEtA9+SnQwIuG/f5cXDy+kWc+FvoF5bT7oPfJM3vFSbHDlROekV
+50Y015adK1lX49e0AMB9n9ZoURaS8ev3Hcvf0ExcKObBK9jM61RdTxDyfHF5Ae5m
+ZvWaplTtTxGh7r8Onv0OLkcu25dGa2dqcktWnE6nSQ8pIxIbuSrm6fqwqtMUSULP
+/9Nng6JzAl+Wxe009br0uTH6a8yRElx95nhiseQnrpO/skj17tLrgskZWGjW2PsN
+CTo5tIeBAoGBAOmzpMqwTLbAK35AKRToXO/iqp1o8iqlyLcFMC5c4hsqRM03Y4qy
+0c2WK/TEnnfwIrD/A+XRC7IdUis7qnSDr2EXlxkdJnmtR+5/6vialxFFRTC8Myes
+YhduUB8Sn6XFhWmRtLPZNz9QPK0Z3qDVwi95kLyEfAQ07xUIM62QXkEzAoGBAM6D
+WclZxMo1pWlMHxfuEa6cuvuZav7tGgnVPq+CGq1goedflCTM9cJjluNF5Eqxzbd4
+fYYE+8no0M0Sb99DdQtLOQj+zaKAqK4LGfokvsMj4eO2pfOjPSANMjIag3DkAHwO
+Gl0hV7TGlusBiNF8eQjPV9niBeGykgOO172p+8+pAoGAEEZmb4cfkIqJfN1S/xW/
+gyUx5IxucPHirHw8Ar6NMH8dE32L/Ri+66ZNoVof/xJGGDVqPBL20YyhMEmTcVHK
+YOSXrTQOfeLHY6Cc6Hs7kgRU8TPqDBVBL4iLI97UJ2M+C0AOaYfzBQG9eACZNHIu
+d8frgHVpfZGCJODRWID5T2kCgYEAnoGW4sLyFrqCUYXJv9ZM4BcQNZkV1MEr4Sw5
+xwA3dafb3PkxfeWLJD7IS30TsnkyioYC4mDk2Z7G1QA3ucfPCHIePtdEAlx6G8wN
+jj+x45mhAeTpD03V1soKIwbSqE14Sb+RYLX82ZYrtAkjeJbvV2G97lBbb1ZeWDjF
+QqA71LECgYEA2kEhWou5i6TrACFAvGmJPaekBYRRIC6vjxKvsDT0fc1sKz0QKP96
+OTYK9eteSn25KsfHb1UlgsSa8Dh+sBkQU9R0KBrbZPHyDXodjdI45AKrSrWsz1UI
+rZBrspjN7m7wXrGdM437E1AkAcWy81fN+829AmL6WrT2Oe6Wq+wqKBw=
+-----END RSA PRIVATE KEY-----

+ 20 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_2.pem

@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWzCCAkMCFFTJkICEIidmnrisIFxZ99KKLhDGMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAkRFMQswCQYDVQQIDAJCVzERMA8GA1UEBwwIRnJlaWJ1cmcxGzAZ
+BgNVBAoMEk1aIEF1dG9tYXRpb24gR21iSDEMMAoGA1UECwwDUiZEMRAwDgYDVQQD
+DAdyb290X0NBMB4XDTIyMDMxODA5MzY0OVoXDTIyMDQxNzA5MzY0OVowajELMAkG
+A1UEBhMCREUxCzAJBgNVBAgMAkJXMREwDwYDVQQHDAhGcmVpYnVyZzEbMBkGA1UE
+CgwSTVogQXV0b21hdGlvbiBHbWJIMQwwCgYDVQQLDANSJkQxEDAOBgNVBAMMB2Ns
+aWVudDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8hnd/sd/nLJrN
+wfei5lphgS71fpP567xgkrGaEy0/lyISeuYK8nkrHI3T5cz0IHsVRGai0cQPdrP5
+0Vdn7kj2rM/WzD7RzSRZIBydd4tnCZBz/bktTRtYNVve7+HaP9t4FxQdkMF4K3Jm
+E/j/gYSYimR49DFjXI2om9kXWZ6pdQUyaKUr+zxgmovtQjtc7wdTdKmsKFQxQQhd
+zWQajEpaNTL+JTJyrCZctryaILgYv4cvQbpF54zbcRsdoPRkP6azsR9GDhCYydwz
+UGMr2CZ2C4BRwB8ChswDsU6iww5enF2vWIUY36VvjmSDDrsH1w4V9UCkSjYMg0VQ
+Mmwc8kerAgMBAAEwDQYJKoZIhvcNAQELBQADggEBALYG8KSPm82uvgmeto76kL+N
+nUgV1ojxj+X9yBrbrkgo4rnsXFU1NUqncdCfpvA7u9mqAjZ4KN+ORZIUp1SXUl3Z
+TIpBClO5ML7wz1Iy6QrExeFwb2783Gl1jeq0lSQwWffNMwkPEqG1QYr/2IK9eSRJ
+hDrure/Ys3s5F7grQ6vTWBQrEXynd81YqqZuBFFs7FuLhg0GK/OdpJ5i2BsLS+B3
+nEOxmgxZ1qLSqbYDjhawsjiSItvO8XxZjA99n3MpBVharqqwp0Xpkm+X30rWolUp
+wur154X0dMZh8jF98KVrB/3Te9aidtyuO9EiGU2Qbkre7jK+Ol3nITR50Gy8yYU=
+-----END CERTIFICATE-----

+ 27 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_3.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA3XP+DAhoJt12nyYcRfweABS1+kINdIMwf2AW0g5qxHvvAKTi
+hfbKtU3USllRPcjPtX/4BcGZZYMLIXW4MU9Gt8Le6zJRrbAazgnZUXg31vrAX6sL
+IQY5GwTmWu4yGu2AUoYB2A8W7DXwUpBG9c9z5+Wt8Vi1neKpEZY2zCnHPn9Qh+0s
+KpH/juhE+LDmeeNSEqfie87STlDg936VPxUxXBSyXEu6QMrynEYiDbrjsvJsZKuY
+VJbJLCDw70RwcS14tcD2xiqMLwp66wSkC+7aVuugQ15VmkGgvgf+vmCQGzMTj4vY
+RVrXcDBUdPkR88iSwmZWWKI4n5YvlmkEclJDnQIDAQABAoIBAB3QOth13Ue2Mv0U
+DWRin8tU/nbVo6gW7VWIoyneZQNUyAVnLVPpuLqV4smroqNVwJjnxIVJ3hPrg3ka
+txR0Xsnog0hYuuROPB2W99ne/G2FzpZSman1g4SesUB2puo3JTp27hKfXW+ph1Qm
+NldreWgz5KoETlcWJ7rFZVUxHrDMBPaZyIww4iK9BoF7TX1gnXeEhxHYGUwJaeYj
+IrPTIcscGnKVALlTSgzxgytiW1ZU717BEyptubqA5P8mitErIZXAcxx+Ek40u2AA
+vuO+UMi/t/ppGvhaqifhJbeTzAQ+H/pTkTTaTrSALPPr/7s91/UBpqiN6w346aVI
+8gpphgECgYEA8YESS4IYiBwTBQniEhzD6ojj5ENpIivoxLD9BjygcLtL91iDhlko
+0u9jdmlBk8UMnSj96OphyKV12Qlo1DRqPC0zN2uip6NYLJy9vrss/LI13UnXHFP/
+nKuMdo9fO3hCSFHpcU5/OuiwAx5IXVXJnUGG6nmeRgXU+0sRY3FzWB0CgYEA6r7R
+YqV5MivqXZm9xP67zNrc5mNdGHCNe9XIEeXSr3GyFSUMnIYOIs1dsO0cwyDPrf2p
+C41Y8nniNR1gh3/9+Fn0WUfAtctawCO8whO0IFjhB/CW0qKB5G4TCAgydwSoxagi
+Bihmk9SRmPlZpDAKh75lZMkXoZf8WRvT1T/JwYECgYBwKvm7Vl5cgWWYFoII1ZFt
+Uk0+jMy80VYYXPf4OJpwIZ3j8RmNgcXDSuqQaczKfGAicpKT5qCqF6eHuaYVwY6C
+CqBaIkT2xZhDiD1c5AS+DWuVLyGZB66WLttbibW6ol1ux5S8SrAvRTnTCPKlXx34
+SyIFr50Cetz0JmaaIGxpRQKBgHy3osE5TxnD0UGng/ZcrGRbR4+z7OsmKVVIsIIp
+y3ThAA9R3tBuPKZq28M7RtO44/35zc1QbJhu/yrfD8EN1F4VVMf2YkFz6CQ7GHc4
+RrQE5JH2VftU0ZQOk1fqGv224QAaovEIl+8kubI/kEu2JnIWSwJwAHkfKbgiG7qp
+qESBAoGAC/vRxjHXnU8KOvQ6f70gM6QpXJiABK475JTaspPWJvd9hCy114kEC8Yk
+7TUJ7802D0tzkkbnq5TYGwWxFBiCMdeH+BrynwSrwgdiS/fVGiwYjzMlZEs5LCOD
+c0tt0S2GLcYbQFUAsCusw9Vir3Szn5HpX7HvIP51nWOk8bbXA5o=
+-----END RSA PRIVATE KEY-----

+ 20 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_3.pem

@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWzCCAkMCFFTJkICEIidmnrisIFxZ99KKLhDJMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAkRFMQswCQYDVQQIDAJCVzERMA8GA1UEBwwIRnJlaWJ1cmcxGzAZ
+BgNVBAoMEk1aIEF1dG9tYXRpb24gR21iSDEMMAoGA1UECwwDUiZEMRAwDgYDVQQD
+DAdyb290X0NBMB4XDTIyMDUyNjEwNDk1NFoXDTMwMDgxMjEwNDk1NFowajELMAkG
+A1UEBhMCREUxCzAJBgNVBAgMAkJXMREwDwYDVQQHDAhGcmVpYnVyZzEbMBkGA1UE
+CgwSTVogQXV0b21hdGlvbiBHbWJIMQwwCgYDVQQLDANSJkQxEDAOBgNVBAMMB2Ns
+aWVudDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdc/4MCGgm3Xaf
+JhxF/B4AFLX6Qg10gzB/YBbSDmrEe+8ApOKF9sq1TdRKWVE9yM+1f/gFwZllgwsh
+dbgxT0a3wt7rMlGtsBrOCdlReDfW+sBfqwshBjkbBOZa7jIa7YBShgHYDxbsNfBS
+kEb1z3Pn5a3xWLWd4qkRljbMKcc+f1CH7Swqkf+O6ET4sOZ541ISp+J7ztJOUOD3
+fpU/FTFcFLJcS7pAyvKcRiINuuOy8mxkq5hUlsksIPDvRHBxLXi1wPbGKowvCnrr
+BKQL7tpW66BDXlWaQaC+B/6+YJAbMxOPi9hFWtdwMFR0+RHzyJLCZlZYojifli+W
+aQRyUkOdAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACiLTRiylV63ZoMWmUewYksr
+PIMqW7HWgBLdO6O+56grF2Sux1Z3LEJjRrLBzI8w9nIWKlnhszl0KF8EKHDPf3j7
+MB8LpW5iOSSevW8dXV9Dfgfkp8ZiOkBTpsid7/v60xsnxRt0Qhydfkpjt5ze/GRU
+rr4867VIOK3UYC0f8tjf3hCfMjwSEhE42yl5MWW06SirmVe32vDxUw0Nel5bB/sx
+BxnK8bjIfgbI0iW6XU73+8+ajSsoU3rSNXpB/1lViruzNsH9DDF/QFcQWIjE90By
+PTy3i/+lYsHQYaNP1Tfr0cbkZDYjOKYsb9plxzZVrTlQhLyvS1oUBRGolQ6JG9U=
+-----END CERTIFICATE-----

+ 27 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_4.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAvDPZNjU3CYNYiyG/JfM2+lpv5bbVau9ZZuC/lUbDUfb9a0jS
+RJzA+ZZq9VkIgUL+WirzqIuUD3MJ+yabrBkG1bSXUUMPIefCnYVuD8j0FjFrbJws
+McbepHDBrjKu7dQcNefN5F/cSEdUWtx1RXsMSoGQkJwaJaqfAhpgFSRO/HVS4eUh
+E++jvkhV3997JkfXvfRffA/Ww9PQ1et4MYk3yugYc0Uasi+wehK1jDFcINn1kvWo
+YRGgvM39JjveCUAGPesUezhch4UIE8X3R18xWQ67xqXU6HVfj9K8zgGl95mzO5LU
+x2qJ82HwG/75NntVQCgqP+4o59muNf+PDTbbLwIDAQABAoIBAQCDUeZRZcZWc/i3
+dD+tWkzVWX9UmUPHTuVh3JaxsWOecKoZTwGw8HPSc7uEILDHiRhzkB7eTy5rrUic
+ny7mYbwcJ9uhzxni/ZUVVsIab4ypY6hia4KG5Q37TdZHF4Rp3KQmlO1cWesZ3/Oj
+RsrpRVepMUjPLq0r9SboT8EVX3Vhv8VQsioN+BRLcX/de5sknD1n1hZ7SP9zJVe1
+iNJLxIeJ/+H2twIyS+AYjq82lWNcLu+zOmMufMTu85agjPotY9rfzwNva1pNCJbw
+bTEzK6YwfYgxQ6uu7d5RXKg+oRxf9K6yyXpvh1K6rrOcTQnHxSIlvXvwjhgRvlRp
+VE7UDHXZAoGBAN6mSZlmXt7VClzSF25HRhcyP/Z0P37tpgUX4euSDTGNancMTeND
+bZiywgwV1RorVRMKNydEyfokyT3x8MUzdnxvVIehSlMZK0z8LEKGwRHhYV7RUDHx
+pmWaUEsd6KFaiYY47Hz6JKudPXOErADhM5vb5knSQakF/nnQca5RCFx7AoGBANhk
+qAyIn0uHLfoiZw7YmEJ1AzEi0ozfh29d/dx47j+QErA0wt5HllGBOKWgRr0N/LWa
+IyQO0+OMbtXJYCXTk2b6FMQMDqoqE1P6mG0ZWwVZ4pkhAFCSck7cKTpR8ftU0t80
+NxmVXAZUejofqiIvszAKYVzb/eH1PwZ4kqb5+n/dAoGABiORnfArp3s6SOrmCH1g
+ml0hVFtKMOa+kB9jdEpXoMkkaVnmf+CpEe/D1+92K72MH/VFJgkIhKQlBFc6a0WK
++81aCE9TLE1iW0IMulzaz/Jl6+ZbjrT6AI0rr5aIhoJnjlLdemivQCgavKeo0nFj
+KeX7SIfKla17ocI0kDjdwScCgYEAsaEmxWsc/93OYwb8fBZWHi95WEtSdKtEvKl2
+KxXl1K2KebRFxjsTbIJborHHf4dMyzHk6MN3MdHkZX+xejuMQzrD8w5Gt25kgUoy
+91OaAPGA7dxGKt2cEZnuCd6ceYhutSRimpCdguCzmKTHftqTB7ttotE/Pc2YV9J/
+56tJ8s0CgYB0/u1rIF4wlTOGqv1ndLOvffmj0RUEEYAE9Gsf9ktavn6pFXAgHjVI
+Bz0fuAKSRUFTEVRwzNikT+xiK8YXVv23izZvxIdFDSSCHmcjgHOMqX5TPi3D7zpA
+OIk4gUbqNKL0CYgmVZBJUbsG4pARLy7VxidKmmtmkibB7ZOP22QYRQ==
+-----END RSA PRIVATE KEY-----

+ 0 - 0
app/IEC_SERVER/lib60870-C/tests/certs/client_CA1_4.pem


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.