/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
*
© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under Ultimate Liberty license
* SLA0044, the "License"; You may not use this file except in compliance with
* the License. You may obtain a copy of the License at:
* www.st.com/SLA0044
*
******************************************************************************
*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include
#include "ethernetif.h"
#include "lwip/api.h"
#include "lwip/tcpip.h"
#include "lwip/ip4_addr.h"
#include "snmp_core.h"
#include "usbd_cdc_if.h"
#include "my_snmp.h"
#include "Time.h"
#include "lwip.h"
#include "AT45DB.h"
#include "usart.h"
#include "plc.h"
#include "ff.h"
#include "spi.h"
#include "fatfs.h"
#include "TFTP.h"
#include "api.h"
#include "rng.h"
#include "log_and_debug.h"
#include "sntp.h"
#include "temp.h"
#include "xml.h"
#define ADDR_VERSION 0x080FFFF8
#define START_ADDR_OTP_REG_MAC 0x1FFF7800
#define END_ADDR_OTP_REG_MAC 0x1FFF78FF
#define START_ADDR_OTP_REG_SN 0x1FFF7900
#define END_ADDR_OTP_REG_SN 0x1FFF79FF
extern ETH_HandleTypeDef heth;
extern I2C_HandleTypeDef hi2c1;
extern UART_HandleTypeDef huart2;
extern struct eth_addr MACAddr;
extern plc_common_typeDef plc_common;
extern RealTimeClock_typeDef RealTimeClock;
network_settings table_network[MAX_IP] = {0};
enum linkState{
LINK_DOWN,
LINK_UP
} ETH_linkState = LINK_DOWN;
enum speedState{
SPEED_10half = 1,
SPEED_100half = 2,
SPEED_10full = 5,
SPEED_100full = 6
}ETH_speedState = SPEED_10half;
struct dev {
uint8_t num_dev;
struct board board[1];
};
struct {
struct dev dev; // [0] - 3v device, [1] - 12v device, [2] - 5v device
}device_info[3] = {{{0, {{0, "uncknown"}}}}, {{1, {{16, "MC04-PLC"}}}}, {{0, {{0, "uncknown"}}}}};
struct board *curr_device;
char *StringCause[5] = {"Unknown", "Watchdog reset", "Software reset", "Hardware reset", "Pin NRST reset"};
uint8_t NumCauseReset;
_Bool ClearPassword = 0;
BYTE work[_MAX_SS];
uint32_t FW_Version[2] = {0};
uint8_t SN[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
extern FATFS USERFatFS;
extern FIL USERFile;
extern char USERPath[4];
//char list[1024] = {0};
uint32_t timeTest = 0;
xSemaphoreHandle MutexAccessFlash;
SemaphoreHandle_t SemaphoreIRQ_PHY, SemaphorePolling;
static void vPLCTask(void *param);
static void vEthernetTask(void *param);
extern void vClockTask(void *param);
static void WebServer_task(void *param);
void StartDefaultTask(void *argument);
extern void MX_FATFS_Init(void);
extern void MX_USB_DEVICE_Init(void);
void MX_FREERTOS_Init(void);
void __logWrite(const char * format, ...);
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
*ppxIdleTaskStackBuffer = &xIdleStack[0];
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
void HW_init_DWT(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0;
}
void MX_FREERTOS_Init(void)
{
wdt_init(2000);
MX_USB_DEVICE_Init();
MutexAccessFlash = xSemaphoreCreateMutex();
SemaphoreIRQ_PHY = xSemaphoreCreateBinary();
SemaphorePolling = xSemaphoreCreateBinary();
__debug_init();
xTaskCreate(StartDefaultTask, "defaultTask", 512, NULL, osPriorityNormal, NULL);
}
/* ######################################################################################### */
/* DEFAULT TASK --------------------------------------------------------------------------- */
void StartDefaultTask(void * argument)
{
wdt_reset();
HAL_GPIO_WritePin(PHY_LEDy_GPIO_Port, PHY_LEDy_Pin, GPIO_PIN_SET);
uint8_t Flash_ID[4] = {0};
FRESULT res;
/* вычитываем версию и подверсию платы */
FW_Version[1] = (*(__IO uint32_t*) ADDR_VERSION);
FW_Version[0] = (*(__IO uint32_t*) (ADDR_VERSION + 4));
/* вычитываем SN адресс с OTP регистра */
uint8_t CurrSN[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint32_t TmpSN, Offset = 0;
do{
TmpSN = (*(__IO uint32_t*) (START_ADDR_OTP_REG_SN + Offset)); /* читаем SN с OTP регистра */
CurrSN[5] = TmpSN;
CurrSN[4] = TmpSN >> 8;
CurrSN[3] = TmpSN >> 16;
CurrSN[2] = TmpSN >> 24;
TmpSN = (*(__IO uint32_t*) (START_ADDR_OTP_REG_SN + (Offset + 4))); /* читаем SN с OTP регистра */
CurrSN[1] = TmpSN;
CurrSN[0] = TmpSN >> 8;
if((CurrSN[0] != 0xFF) && (CurrSN[1] != 0xFF) && (CurrSN[2] != 0xFF) && (CurrSN[3] != 0xFF) && (CurrSN[4] != 0xFF) && (CurrSN[5] != 0xFF)) {
memcpy(SN, CurrSN, 6);
if(END_ADDR_OTP_REG_SN - (START_ADDR_OTP_REG_SN + Offset) >= 6)Offset += 6;
else break;
}
else break;
}while(1);
osDelay(800);
__debug(DEBUG_SERVSE, "\r\n%s\r\nS-port: Firmware v%d.%d, ", StringCause[NumCauseReset], FW_Version[1], FW_Version[0]);
if((SN[0] == 0xFF) && (SN[1] == 0xFF) && (SN[2] == 0xFF) && (SN[3] == 0xFF) && (SN[4] == 0xFF) && (SN[5] == 0xFF))__debug(DEBUG_SERVSE, "S-port SN: n/a\r\n");
else __debug(DEBUG_SERVSE, "Serial number: %s\r\n", SN);
clock_init();
osDelay(200);
MX_SPI3_Init();
HAL_GPIO_WritePin(FL_RES_GPIO_Port, FL_RES_Pin, GPIO_PIN_SET);
Flash_Read_ID(Flash_ID);
__debug(DEBUG_SERVSE, "Flash device: Manufacturer ID: 0x%02X, Device ID: 0x%02X%02X\r\n", Flash_ID[0], Flash_ID[1], Flash_ID[2]);
MX_FATFS_Init();
res = f_mount(&USERFatFS,(TCHAR const*)USERPath, 1);
taskENTER_CRITICAL();
if(res == FR_NO_FILESYSTEM){
__debug(DEBUG_SERVSE,"NO Filesystem\r\n");
if(!(ReadStatusFlash() & (1 << 0)))Flash_SetPage512();
res = f_mkfs((TCHAR const*)USERPath, FM_ANY, 0, work, sizeof work);
__debug(DEBUG_SERVSE,"f_mkfs result - %d\r\n", res);
if(res == FR_OK) f_mount(&USERFatFS,(TCHAR const*)USERPath, 1);
}
if(res == FR_OK) __debug(DEBUG_SERVSE,"Filesystem: OK\r\n");
else __debug(DEBUG_SERVSE,"f_mount result - %d\r\n", res);
taskEXIT_CRITICAL();
__logWrite("%s\n", StringCause[NumCauseReset]);
__logWrite("Flash device: Manufacturer ID: 0x%02X, Device ID: 0x%02X%02X\n", Flash_ID[0], Flash_ID[1], Flash_ID[2]);
uint8_t execution = ((~EXECUTION_GPIO_Port->IDR >> 3) & 0x07) >> 1;
if(execution < 0x04) {
uint8_t type = (~VERSION_GPIO_Port->IDR >> 9) & 0x1F;
for(uint8_t i = 0; i < device_info[execution].dev.num_dev; i++){
if(type == device_info[execution].dev.board[i].type){
curr_device =& device_info[execution].dev.board[i]; // берем указатель на найденный девайс
break;
}
}
}
if(curr_device == NULL){
__debug(DEBUG_SERVSE,"Uncknown device\r\n");
__logWrite("Uncknown device\n");
while(1){
wdt_reset();
osDelay(5000);
}
}
__debug(DEBUG_SERVSE,"S-Port SNMP device: %s\r\n", curr_device->Name);
__logWrite("S-Port SNMP device: %s\n", curr_device->Name);
HW_init_DWT();
RNG_init();
xTaskCreate(vEthernetTask, "EthTask", 2048, NULL, osPriorityNormal, NULL); // priority: above normal
temp_sensor_init();
HAL_GPIO_WritePin(PHY_LEDy_GPIO_Port, PHY_LEDy_Pin, GPIO_PIN_RESET);
for(;;)
{
wdt_reset();
// vTaskList(list);
//HAL_GPIO_TogglePin(PHY_LEDy_GPIO_Port, PHY_LEDy_Pin);
osDelay(500);
}
}
/* ######################################################################################### */
/* ETHERNET TASK --------------------------------------------------------------------------- */
static void vEthernetTask(void *param)
{
__logWrite("ETH daemon started\n");
uint32_t PHY_ReadData = 0;
char ip[17] = {0}, nm[17] = {0}, gw[17] = {0}, ip_ntp[17] = {0};
HAL_GPIO_WritePin(PHY_nRST_GPIO_Port, PHY_nRST_Pin, GPIO_PIN_SET);
osDelay(50);
/*вычитываем MAC адресс с OTP регистра*/
uint32_t CurrMAC = 0xFFFFFFFF, TmpMAC = 0xFFFFFFFF, offset = 0;
do{
CurrMAC = (*(__IO uint32_t*) (START_ADDR_OTP_REG_MAC + offset)) & 0x00FFFFFF; // если тут 000001 - записанный MAC, то проверить следующие три байта, если они FFFFFF, то передыдущий был нужный MAC
if(END_ADDR_OTP_REG_MAC - (START_ADDR_OTP_REG_MAC + offset) >= 3)offset += 3;
else break;
TmpMAC = (*(__IO uint32_t*) (START_ADDR_OTP_REG_MAC + offset)) & 0x00FFFFFF;
if(TmpMAC == 0x00FFFFFF){ // значит предыдущие три ячейки это нужный мас
MACAddr.addr[3] = CurrMAC >> 16;
MACAddr.addr[4] = CurrMAC >> 8;
MACAddr.addr[5] = CurrMAC;
break;
}
}while(1);
__debug(DEBUG_SERVSE, "S-port MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", MACAddr.addr[0], MACAddr.addr[1], MACAddr.addr[2], MACAddr.addr[3], MACAddr.addr[4], MACAddr.addr[5]);
if(xml_get_tag("config.xml", "net", NULL, "ip", ip) != XML_OK) memcpy(ip, "192.168.0.254", 13);
if(xml_get_tag("config.xml", "net", NULL, "netmask", nm) != XML_OK) memcpy(nm, "255.255.255.0", 13);
if(xml_get_tag("config.xml", "net", NULL, "gateway", gw) != XML_OK) memcpy(gw, "192.168.0.1", 11);
tcpip_init(NULL, NULL);
LWIP_SetNetSettings(&table_network[0], ip, nm, gw); // тут происходит запись в таблицу и в netif
MX_LWIP_Init(&table_network[0]); // netif добавляется в netiflist
if(curr_device->type == 16){ // если девайс у нас PLC, то поднимаем еще 3 интерфейса в lwip
for(int8_t i = MAX_IP - 1; i > 0; i--) {
LWIP_SetNetSettings(&table_network[i], "192.168.0.254", "255.255.255.0", "192.168.0.1");
MX_LWIP_Init(&table_network[i]);
}
}
if(HAL_GPIO_ReadPin(RES_IP_GPIO_Port, RES_IP_Pin) == GPIO_PIN_RESET){
LWIP_resetIP(&table_network[0]); // только запись в netif
memcpy(table_network[0].ipv4_addr, ip, strlen(ip));
memcpy(table_network[0].ipv4_nm, nm, strlen(nm));
memcpy(table_network[0].ipv4_gw, gw, strlen(gw));
__debug(DEBUG_SERVSE, "Default IP is set\r\nS-port IP:192.168.0.254, mask:255.255.255.0, gw:192.168.0.1\r\n");
__logWrite("Default IP is set\n");
ClearPassword = 1; // выставляем флаг, что очистка пароля
}
else __debug(DEBUG_SERVSE, "S-port IP:%s, mask:%s, gw:%s\r\n", table_network[0].ipv4_addr, table_network[0].ipv4_nm, table_network[0].ipv4_gw);
mac_init();
netif_set_default(&table_network[0].netif);
/* считаем link принудительно */
HAL_ETH_ReadPHYRegister(&heth, 0x01, &PHY_ReadData);
ETH_linkState = (enum linkState)((PHY_ReadData & (1 << 2)) >> 2);
if(ETH_linkState == LINK_UP) netif_set_up(&table_network[0].netif);
else netif_set_down(&table_network[0].netif);
/* link interrupt enable */
HAL_ETH_WritePHYRegister(&heth, 0x1B, 0x500);
xTaskCreate(WebServer_task, "WebServer", 4096, NULL, osPriorityRealtime + 1, NULL);
if(xml_get_tag("config.xml", "net", NULL, "ntp", ip_ntp) == XML_OK){
ip_addr_t sntp_addr;
ip4addr_aton(ip_ntp, &sntp_addr);
sntp_setserver(0, &sntp_addr);
sntp_init();
__debug(DEBUG_SERVSE, "NTP server started: ip:%s\r\n", ip_ntp);
}
else sntp_setserver(0, NULL);
tftpServer_init(); // пока что запускаем потом будем делать через консоль
if(curr_device->type == 16){ // device PLC
SnmpPrepare(PLC); // snmp agent start
xTaskCreate(vPLCTask, "PLCTask", 2048, NULL, osPriorityNormal, NULL); //priority: high
}
else {} // другие девайсы
for(;;){
if(xSemaphoreTake(SemaphoreIRQ_PHY, portMAX_DELAY) == pdPASS){
HAL_ETH_ReadPHYRegister(&heth, 0x01, &PHY_ReadData);
ETH_linkState = (enum linkState)((PHY_ReadData & (1 << 2)) >> 2);
if(ETH_linkState == LINK_UP) {
netif_set_up(&table_network[0].netif);
__debug(DEBUG_ETH,"ETH LINK UP\r\n");
}
else {
netif_set_down(&table_network[0].netif);
__debug(DEBUG_ETH, "ETH LINK DOWN\r\n");
}
HAL_ETH_ReadPHYRegister(&heth, 0x1E, &PHY_ReadData);
ETH_speedState = (enum speedState)(PHY_ReadData & 0x07);
if((ETH_speedState == SPEED_10full) || (ETH_speedState == SPEED_10half)) HAL_GPIO_WritePin(PHY_LEDy_GPIO_Port, PHY_LEDy_Pin, GPIO_PIN_SET);
else HAL_GPIO_WritePin(PHY_LEDy_GPIO_Port, PHY_LEDy_Pin, GPIO_PIN_RESET);
HAL_ETH_ReadPHYRegister(&heth, 0x1B, &PHY_ReadData);
}
vTaskDelay(1);
}
}
/* ----------------------------------------------------------------------------------------- */
/* ######################################################################################### */
/* PLC TIMER POLLING ----------------------------------------------------------------------- */
xQueueHandle QueueCmdTxPLC, QueueCmdRxPLC;
extern plc_data_typeDef PLC_Data[AMOUNT_BLOCK_PLC];
void TimerPolling_Callback(void const *argument)
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(SemaphorePolling, &xHigherPriorityTaskWoken);
}
#define UDP_RECORD_LIVE_TIME 180; // секунд = 3 минуты
#define UDP_MAX_RECORDS 3 // максимум три записи в таблице
struct table_records_UDP{
unsigned short port; // адрес на который отправлять
ip_addr_t addr; // порт на который отправлять
uint32_t time; // время когда запись протухнет
struct {
volatile _Bool valid;
}flags;
}table_records_UDP[UDP_MAX_RECORDS] = {0};
/* ----------------------------------------------------------------------------------------- */
/* PLC TASK -------------------------------------------------------------------------------- */
static void vPLCTask(void *param)
{
#define AMOUNT_BOARD_CMD 9
#define MAX_ELEMENTS_TX 16
__debug(DEBUG_SERVSE,"PLC daemon started\r\n");
__logWrite("PLC daemon started\n");
plc_TxData_typeDef PLCTxData = {0};
plc_RxData_typeDef PLCRxData = {0};
QueueCmdTxPLC = xQueueCreate(MAX_ELEMENTS_TX, sizeof(plc_TxData_typeDef));
QueueCmdRxPLC = xQueueCreate(16, sizeof(plc_RxData_typeDef));
osTimerId TimerPolling;
osTimerDef(Timer_Polling, TimerPolling_Callback);
TimerPolling = osTimerCreate(osTimer(Timer_Polling), osTimerPeriodic, NULL);
err_t err;
ip_addr_t *addr;
volatile _Bool bindUDP = 0, record_find = 0;
uint8_t *UDP_PLCData;
uint16_t UDP_LenData;
uint8_t ID_Unit = 0;
unsigned short port;
struct netbuf *nbufRX, *nbufTX;
struct netconn *connUDP;
plc_buff_typeDef buffUDP_send = {0};
struct {
uint8_t shift;
uint8_t mask;
uint8_t byte;
_Bool isID : 1;
_Bool end : 1;
_Bool add : 1;
_Bool nextID : 1;
_Bool noCopy : 1;
}UDP_flag;
volatile uint16_t sBuffCnt = 0;
char sBuffer[2048] = {0};
char ip[16] = {0}, nm[16] = {0}, gw[16] = {0}, str_xml[25] = {0};
uint8_t cntByte = 0;
plc_data_typeDef *PointPLCData =& PLC_Data[0];
uint32_t TimerCounter = 0;
uint8_t board = 0;
const uint8_t Kosh_sigMass[10] = {10, 13, 16, 20, 25, 32, 40, 50, 63, 79}; // 10^0.0, 10^0.1 и тд. умноженное на 10
struct {
_Bool all_en;
const uint16_t BoardCmd[AMOUNT_BOARD_CMD]; // в старшем байте адрес платы, младший байт команда
uint8_t CurrentIndexBlock;
uint8_t CurrentIndexUM;
struct {
_Bool En;
uint8_t PointBoardCmd; // по колл-ву блоков(у каждого блока свой указатель на команду которую передаем)
uint32_t NewTime;
uint8_t Timeout;
uint8_t RequestCnt; // счетчик запросов, когда блок отвечает, то его сбрасываем
uint8_t Alarm;
_Bool Presece;
_Bool FirstRequest;
struct {
_Bool Presence;
uint8_t RequestCnt; // счетчик для перезапроса отстутсвующей платы УМ
}UM[2];
}Block[AMOUNT_BLOCK_PLC];
}Common = {0, {0x0401, 0x0411, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417, 0x0A01, 0x0C01}, 0x00, 0xFF,
{{0, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 1, {{1, 0x00},{1, 0x00}}},
{0, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 1, {{1, 0x00},{1, 0x00}}},
{0, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 1, {{1, 0x00},{1, 0x00}}},
{0, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 1, {{1, 0x00},{1, 0x00}}}}};
sprintf(PLC_Data[0].NameBlock, "");
sprintf(PLC_Data[1].NameBlock, "");
sprintf(PLC_Data[2].NameBlock, "");
sprintf(PLC_Data[3].NameBlock, "");
if(xml_get_attr("config.xml", "blocks", NULL, "num", str_xml) == XML_OK) plc_common.num = atoi(str_xml);
else plc_common.num = 1;
PLC_Data[1].PollingTimeout = 5;
PLC_Data[2].PollingTimeout = 5;
PLC_Data[3].PollingTimeout = 5;
for(uint8_t i = 0; i < plc_common.num; i++){
PLC_Data[i].EnablePoll = 1;
sprintf(sBuffer, (const char*)"id=\"%d\"", i);
if(xml_get_attr("config.xml", "block", sBuffer, "name", str_xml) == XML_OK) memcpy(PLC_Data[i].NameBlock, str_xml, strlen(str_xml));
else sprintf(PLC_Data[i].NameBlock, "");
if(xml_get_tag("config.xml", "block", sBuffer, "poll", str_xml) == XML_OK)PLC_Data[i].PollingTimeout = atoi(str_xml);
else PLC_Data[i].PollingTimeout = (i == 0) ? 3 : 5;
if(xml_get_tag("config.xml", "block", sBuffer, "filter", str_xml) == XML_OK){
PLC_Data[i].Type_Filter = (TypeFilter_typeDef)atoi(&str_xml[0]);
PLC_Data[i].FilterATT = (FilterATT_typeDef)atoi(&str_xml[2]);
}
if(i > 0){
if(xml_get_tag("config.xml", "block", sBuffer, "route", str_xml) == XML_OK) for(uint8_t q = 0; q < sizeof(PLC_Data[i].Route); q++)PLC_Data[i].Route[q] = atoi(&str_xml[q * 2]);
else for(uint8_t q = 0; q < sizeof(PLC_Data[i].Route); q++)PLC_Data[i].Route[q] = 0x00;
if(xml_get_tag("config.xml", "block", sBuffer, "ip", ip) != XML_OK) sprintf(ip, "");
if(xml_get_tag("config.xml", "block", sBuffer, "netmask", nm) != XML_OK) sprintf(nm, "");
if(xml_get_tag("config.xml", "block", sBuffer, "gateway", gw) != XML_OK) sprintf(gw, "");
LWIP_SetNetSettings(&table_network[i], ip, nm, gw);
netif_set_up(&table_network[i].netif);
}
}
MX_USART2_UART_Init();
connUDP = netconn_new(NETCONN_UDP);
if (connUDP!= NULL){
err = netconn_bind(connUDP, IP_ADDR_ANY, 12348);
if (err == ERR_OK) {
bindUDP = 1;
netconn_set_recvtimeout(connUDP, 10);
}
else netconn_delete(connUDP);
}
osTimerStart(TimerPolling, 1000); // каждую секунду срабатывает таймер
for(;;){
if((!Common.all_en) && (plc_common.enable_all_pool)){
__debug(DEBUG_SERVSE, "Block polling disabled\r\n");
osTimerStop(TimerPolling);
xQueueReset(QueueCmdTxPLC);
Common.all_en = 1;
}
else if((Common.all_en) && (!plc_common.enable_all_pool)){
__debug(DEBUG_SERVSE, "Block polling enabled\r\n");
osTimerStart(TimerPolling, 1000);
Common.all_en = 0;
}
for(uint8_t i = 0; i < UDP_MAX_RECORDS; i++){/*смотрим протухла ли таблица*/
if(table_records_UDP[i].flags.valid){
if(TimerCounter >= table_records_UDP[i].time) table_records_UDP[i].flags.valid = 0; // запись протухла
}
}
if(bindUDP) { // если UDP сервер забиндился
err = netconn_recv(connUDP, &nbufRX);
if (err == ERR_OK){ // пришли данные с UDP порта
addr = netbuf_fromaddr(nbufRX);
port = netbuf_fromport(nbufRX);
for(uint8_t i = 0; i < UDP_MAX_RECORDS; i++){ //ищем запись
if(table_records_UDP[i].flags.valid){
if((table_records_UDP[i].addr.addr == addr->addr) && (table_records_UDP[i].port == port)) {
table_records_UDP[i].time = TimerCounter + UDP_RECORD_LIVE_TIME;
record_find = 1;
break;
}
}
}
if(!record_find){ // если такой записи нет, то добавляем ее
for(uint8_t i = 0; i < UDP_MAX_RECORDS; i++){
if(!table_records_UDP[i].flags.valid){ // ищем свободное место под запись
table_records_UDP[i].addr.addr = addr->addr;
table_records_UDP[i].port = port;
table_records_UDP[i].flags.valid = 1; // занимаем запись
table_records_UDP[i].time = TimerCounter + UDP_RECORD_LIVE_TIME;
break;
}
}
}
else record_find = 0;
netbuf_data(nbufRX, (void**)&UDP_PLCData, &UDP_LenData);
PLCTxData.LenBuff = 0;
PLCTxData.Pointer = 0x01; // увеличиваем указатель
PLCTxData.Buff[PLCTxData.LenBuff++] = (PLC_ADDR_BOARD << 4) & 0xF0; // пишем свой адрес
UDP_LenData--; // вычитаем первый байт, это указатель для нас он безполезен
if(UDP_PLCData[1] == 0x0F){// это широковещательное сообщение для ближнего блока формируем 0x0E 0xF0 0x00
PLCTxData.Buff[PLCTxData.LenBuff++] = 0xF0;
PLCTxData.Buff[PLCTxData.LenBuff++] = 0x00;
}
else {
if(((UDP_PLCData[1] & 0x0F) > 1) && ((UDP_PLCData[1] & 0x0F) < 0x0F)) { // тут всегда будет ID блока
ID_Unit = UDP_PLCData[1] & 0x0F;// запоминием наш ID для ответа
if((UDP_PLCData[1] & 0xF0) == 0x00){
PLCTxData.Buff[PLCTxData.LenBuff++] = (ID_Unit << 4);
PLCTxData.Buff[PLCTxData.LenBuff++] = 0x00;
if(UDP_LenData > 0x02) {
UDP_LenData--;
memcpy(&PLCTxData.Buff[PLCTxData.LenBuff], &UDP_PLCData[2], UDP_LenData - 1);
PLCTxData.LenBuff += (UDP_LenData - 1);
}
}
else if(((UDP_PLCData[1] & 0xF0) >> 4) >= 4){ // тут может быть и плата и канал
PLCTxData.Buff[PLCTxData.LenBuff++] = ((UDP_PLCData[1] & 0xF0) >> 4); // это плата ближнего блока
UDP_LenData--; // вычитаем второй байт так как мы его развернули
memcpy(&PLCTxData.Buff[PLCTxData.LenBuff], &UDP_PLCData[PLCTxData.LenBuff], UDP_LenData - 1); //копируем на один байт меньше так как там контрольная сумма
PLCTxData.LenBuff += (UDP_LenData - 1);
}
else { // это канал
UDP_flag.add = 0;
UDP_flag.end = 0;
UDP_flag.isID = 0;
UDP_flag.nextID = 0;
for(uint8_t ptr = 1;; ptr++){
UDP_flag.mask = 0x0F; UDP_flag.shift = 0;
for(uint8_t i = 0; i < 2; i++){ // так как две тетрады в байте
if((ptr != 0x01) || ((ptr == 0x01) && (i != 0x00))) UDP_flag.byte = (UDP_PLCData[ptr] & UDP_flag.mask) >> UDP_flag.shift; // пропускаем младщую тетраду первого просматриваемого байта
else { UDP_flag.mask = 0xF0; UDP_flag.shift = 4; continue; }
if(ptr != 0x01){
if(UDP_flag.byte == 0x00) {
UDP_flag.nextID = 1; // сообщаем что следующая тетрада это ID
if(UDP_flag.isID) UDP_flag.end = 1; // значит конец
}
else if(UDP_flag.byte >= 0x04) {
if(UDP_flag.nextID){ // это ID
if(UDP_flag.byte == 0x0F){
if(UDP_flag.shift == 0x00) UDP_flag.add = 1;
UDP_flag.noCopy = 1;
UDP_flag.end = 1;
}
else UDP_flag.isID = 1;
UDP_flag.nextID = 0;
}
else UDP_flag.end = 1;
}
else UDP_flag.isID = 0;// канал
}
if(i == 0x00) PLCTxData.Buff[PLCTxData.LenBuff++] |= UDP_flag.byte << 4;
else PLCTxData.Buff[PLCTxData.LenBuff] = UDP_flag.byte;
if(UDP_flag.end) {
if(i == 0x01)PLCTxData.LenBuff++;
break;
}
UDP_flag.mask = 0xF0; UDP_flag.shift = 4;
}
if(UDP_flag.end){
if(UDP_flag.add) PLCTxData.Buff[PLCTxData.LenBuff++] = 0x00;
if(!UDP_flag.noCopy){
memcpy(&PLCTxData.Buff[PLCTxData.LenBuff], &UDP_PLCData[ptr + 1], UDP_LenData - ptr - 1);
PLCTxData.LenBuff += (UDP_LenData - ptr - 1);
}
else UDP_flag.noCopy = 0;
break;
}
}
}
}
}
if(__debug(DEBUG_PLC_TRANSIT,"********* UDP RX *********\r\n")){
sBuffCnt = 0;
sBuffCnt += sprintf(&sBuffer[sBuffCnt], "pointer:%02X\r\ndata [%d]:", PLCTxData.Pointer, PLCTxData.LenBuff);
for(uint16_t i = 0; i < PLCTxData.LenBuff; i++)sBuffCnt += sprintf(&sBuffer[sBuffCnt], "%02X", PLCTxData.Buff[i]);
__debug(DEBUG_PLC_TRANSIT,"%s\r\n***************************************\r\n", sBuffer);
}
xQueueSend(QueueCmdTxPLC, &PLCTxData, portMAX_DELAY);
netbuf_delete(nbufRX);
}
}
/******************************** РЕЖИМ РАБОТЫ "ОПРОС БЛОКОВ" ******************************/
if(plc_common.change_block > 0){
uint8_t up = 0;
up = plc_common.num - plc_common.change_block;
if(up != 0) {
for(uint8_t i = 0; i < up; i++){
memcpy(&Common.Block[plc_common.change_block], &Common.Block[plc_common.change_block + 1], sizeof(Common.Block[plc_common.change_block + 1]));
plc_common.change_block++;
}
}
plc_common.change_block = 0;
}
if(xQueueReceive(QueueCmdRxPLC, &PLCRxData, 0) == pdTRUE){ // получаем данные всегда
if(PLCRxData.Valid){ // данные валидные и готовы к дальнейшей обработке
if(PLCRxData.Query) __debug(DEBUG_PLC_CROSS,"Request me\r\n");
if(__debug(DEBUG_PLC_CROSS | DEBUG_PLC_TRANSIT,"********* RX CROSS *********\r\n")){ // выводим дебаг
sBuffCnt = 0;
sBuffCnt += sprintf(&sBuffer[sBuffCnt], "pointer:%02X\r\ndata [%d]:", PLCRxData.Pointer, PLCRxData.LenBuff);
for(uint16_t i = 0; i < PLCRxData.LenBuff; i++)sBuffCnt += sprintf(&sBuffer[sBuffCnt], "%02X", PLCRxData.Buff[i]);
__debug(DEBUG_PLC_CROSS | DEBUG_PLC_TRANSIT,"%s\r\n***************************************\r\n", sBuffer);
}
Common.CurrentIndexBlock = 0;
if((PLCRxData.Pointer & 0x80) && (PLCRxData.Pointer & 0x01)){ // ответ на запрос (указатель должен быть 1)
if((PLCRxData.Buff[0] & 0xF0) == (PLC_ADDR_BOARD << 4)){// команда для меня
/*************************** PLC->UDP *************************/
volatile uint8_t offset;
uint16_t tmpLenBuff = PLCRxData.LenBuff;
uint8_t pointer = 1;
buffUDP_send.len = 0;
buffUDP_send.data[buffUDP_send.len++] = PLCRxData.Pointer & 0x80;
tmpLenBuff--; //вычитаем первый байт так как в нем наша плата
if((PLCRxData.Buff[1] & 0x0F) == 0x00) { // ответ ID блока ближнего
buffUDP_send.data[buffUDP_send.len++] = ((PLCRxData.Buff[pointer] & 0xF0) >> 4) | ((PLCRxData.Buff[pointer] & 0x0F) << 4); // рвзвернул данные так как ответ ID блока
tmpLenBuff -= 2;
pointer += 2;
}
else if((PLCRxData.Buff[1] & 0x0F) >= 0x04){ // тут может быть и плата и канал
buffUDP_send.data[buffUDP_send.len++] = ((PLCRxData.Buff[pointer++] & 0x0F) << 4) | ID_Unit;// это ближняя плата
tmpLenBuff--;
}
else { // это канал
UDP_flag.add = 0;
UDP_flag.end = 0;
UDP_flag.isID = 0;
UDP_flag.nextID = 0;
for(uint8_t ptr = 1;; ptr++){
UDP_flag.mask = 0x0F; UDP_flag.shift = 0;
for(uint8_t i = 0; i < 2; i++){
UDP_flag.byte = (PLCRxData.Buff[ptr] & UDP_flag.mask) >> UDP_flag.shift;
if((ptr == 1) && (i == 0)) buffUDP_send.data[buffUDP_send.len] = ID_Unit;
if(UDP_flag.byte == 0x00) {
if(UDP_flag.nextID) UDP_flag.end = 1;
if(UDP_flag.isID){
if(UDP_flag.shift == 0x04) UDP_flag.add = 1;
UDP_flag.end = 1;
}
else UDP_flag.nextID = 1; // сообщаем что следующая тетрада это ID
}
else if(UDP_flag.byte >= 0x04) {
if(UDP_flag.nextID){
UDP_flag.isID = 1;
UDP_flag.nextID = 0;
}
else UDP_flag.end = 1;
}
else UDP_flag.isID = 0;// канал
if(i == 0x00) buffUDP_send.data[buffUDP_send.len++] |= UDP_flag.byte << 4;
else buffUDP_send.data[buffUDP_send.len] = UDP_flag.byte;
if(UDP_flag.end) {
if((i == 0x01) && (!UDP_flag.add)) buffUDP_send.len++;
break;
}
UDP_flag.mask = 0xF0; UDP_flag.shift = 4;
}
if(UDP_flag.end){
if(UDP_flag.add) buffUDP_send.data[buffUDP_send.len++] = 0x00;
pointer += ptr;
tmpLenBuff -= ptr;
break;
}
}
}
memcpy(&buffUDP_send.data[buffUDP_send.len], &PLCRxData.Buff[pointer], tmpLenBuff);
buffUDP_send.len += tmpLenBuff;
buffUDP_send.data[buffUDP_send.len++] = ~((uint8_t)plc_calc_cs(buffUDP_send.data, buffUDP_send.len));// данные в UDP
for(uint8_t i = 0; i < UDP_MAX_RECORDS; i++){
if(table_records_UDP[i].flags.valid){ // есть валидная запись, то отправляем
netconn_connect(connUDP, &table_records_UDP[i].addr, table_records_UDP[i].port);
nbufTX = netbuf_new();
if(nbufTX != NULL){
netbuf_alloc(nbufTX, buffUDP_send.len);
pbuf_take(nbufTX->p, (void *) &buffUDP_send.data, buffUDP_send.len);
netconn_send(connUDP, nbufTX);
netbuf_free(nbufTX);
netbuf_delete(nbufTX);
netconn_disconnect(connUDP);
}
}
}
/*****************************************************/
/*********** обработка моих пакетов из PLC ***********/
PointPLCData = NULL;
PLCRxData.LenBuff -= 1; // вычтем первый байт, так как там лежит 0xE0
if((PLCRxData.Buff[1] & 0x0F) >= 0x04) {// значит это плата и это ближний блок
board = PLCRxData.Buff[1] & 0x0F;
PLCRxData.LenBuff -= 1; // вычитаем байт платы
offset = 2;
PointPLCData =& PLC_Data[0]; // берем указатель ближнего блока
}
else{ // это при приходит от дальнего блока
for(Common.CurrentIndexBlock = 1; /*(offset <= cntByte) || */(Common.CurrentIndexBlock < AMOUNT_BLOCK_PLC); ){
if(PLC_Data[Common.CurrentIndexBlock].Route[0] > 0){
cntByte = (PLC_Data[Common.CurrentIndexBlock].Route[0] + 1) / 2; // вычисляем сколько байт
if((PLC_Data[Common.CurrentIndexBlock].Route[0] % 2) == 0){ // если четное коллво ниблов
board = (PLCRxData.Buff[cntByte + 1] & 0x0F);
if(board < 4){ Common.CurrentIndexBlock++; continue;}
if(memcmp(&PLCRxData.Buff[1], &PLC_Data[Common.CurrentIndexBlock].Route[1], cntByte) == 0x00){
PLCRxData.LenBuff = PLCRxData.LenBuff - cntByte - 1;
offset = cntByte + 2;
PointPLCData =& PLC_Data[Common.CurrentIndexBlock];
break;
}
Common.CurrentIndexBlock++;
}
else { // если коллво ниблов нечетное
board = (PLCRxData.Buff[cntByte] & 0xF0) >> 4;
if(board < 4){ Common.CurrentIndexBlock++; continue;}
uint8_t tmp = PLCRxData.Buff[cntByte];
PLCRxData.Buff[cntByte] &= 0x0F; // тут не надо убирать адресс платы
if(memcmp(&PLCRxData.Buff[1], &PLC_Data[Common.CurrentIndexBlock].Route[1], cntByte) == 0x00){
PLCRxData.LenBuff -= cntByte;
offset = cntByte + 1;
PointPLCData =& PLC_Data[Common.CurrentIndexBlock];
break;
}
PLCRxData.Buff[cntByte] = tmp;
Common.CurrentIndexBlock++;
}
}
else Common.CurrentIndexBlock++;
}
}
// есть указатель, то значит можно обрабатывать ответ
if(PointPLCData != NULL) {
memmove(&PLCRxData.Buff[0], &PLCRxData.Buff[offset], PLCRxData.LenBuff);
if(board == 0x04){ // ответ от MD
uint8_t ch;
Common.Block[Common.CurrentIndexBlock].Presece = 1;// выставляем флаг что блок ответил
Common.Block[Common.CurrentIndexBlock].RequestCnt = 0; // сбрасываем счетчик запросов
if(PLCRxData.Buff[0] == 0x01) {
sprintf(PointPLCData->Version, "%d", PLCRxData.Buff[1]);
PointPLCData->VersionMD = atoi(PointPLCData->Version);
Common.Block[Common.CurrentIndexBlock].FirstRequest = 0;
}
else if((PLCRxData.Buff[0] == 0x11) || (PLCRxData.Buff[0] == 0x12) || (PLCRxData.Buff[0] == 0x13)){
ch = (PLCRxData.Buff[0] & (0x0F)) - 1;
PointPLCData->HF_Channel[ch].En = (PLCRxData.Buff[2] & 0x80);
if(PointPLCData->HF_Channel[ch].En){
PointPLCData->HF_Channel[ch].Frequency_Transmitter = PLCRxData.Buff[1] | ((PLCRxData.Buff[2] & 0x03) << 8);
PointPLCData->HF_Channel[ch].Frequency_Receiver = PLCRxData.Buff[3] | ((PLCRxData.Buff[4] & 0x03) << 8);
PointPLCData->HF_Channel[ch].Mode = (ModeChannel_typeDef)((PLCRxData.Buff[5] & 0xC0) >> 6);
}
}
else if((PLCRxData.Buff[0] == 0x15) || (PLCRxData.Buff[0] == 0x16) || (PLCRxData.Buff[0] == 0x17)){
ch = (PLCRxData.Buff[0] & (0x0F)) - 5;
if(PointPLCData->HF_Channel[ch].En){
if(PLCRxData.Buff[1] & 0xE0){
if((PLCRxData.Buff[1] & 0x60) == 0x60) PointPLCData->HF_Channel[ch].Status_Receiver = RECEIVING;
else if(PLCRxData.Buff[1] & 0x40) PointPLCData->HF_Channel[ch].Status_Receiver = ACTIVATION;
else if(PLCRxData.Buff[1] & 0x80)PointPLCData->HF_Channel[ch].Status_Receiver = OVERLOAD;
}
else PointPLCData->HF_Channel[ch].Status_Receiver = NO_SIGNAL;
if(PLCRxData.LenBuff >= 9) { // если цифровой режим работы
if(PLCRxData.Buff[3] > 128)PointPLCData->HF_Channel[ch].Rate_Transmitter = ((PLCRxData.Buff[3] - 128) * 2) + 128; // rate 12.7kB/s -> 127 // *0.2
else PointPLCData->HF_Channel[ch].Rate_Transmitter = PLCRxData.Buff[3] * 1; // *0.1 // передаем число умноженное на 10
if(PLCRxData.Buff[4] > 128)PointPLCData->HF_Channel[ch].Rate_Receiver = ((PLCRxData.Buff[4] - 128) * 2) + 128; // rate 12.7kB/s -> 127 // *0.2
else PointPLCData->HF_Channel[ch].Rate_Receiver = PLCRxData.Buff[4] * 1; // *0.1 // передаем число умноженное на 10
PointPLCData->HF_Channel[ch].QualityNoise_Receiver = PLCRxData.Buff[5] * 10 / 8; // передаем число умноженное на 10
if(PointPLCData->HF_Channel[ch].QualityNoise_Receiver > 0)PointPLCData->HF_Channel[ch].SNR_Receiver = PointPLCData->HF_Channel[ch].Rate_Receiver + PointPLCData->HF_Channel[ch].QualityNoise_Receiver - 20; // передаем число умноженное на 10
else PointPLCData->HF_Channel[ch].SNR_Receiver = 0;
PointPLCData->HF_Channel[ch].CounterError = (PLCRxData.Buff[7] << 8) | PLCRxData.Buff[6];
PLCRxData.Buff[8] &= ~(1 << 7); // кОш обнуляем 7 бит, так как он недействительный
PointPLCData->HF_Channel[ch].Kosh_raw = PLCRxData.Buff[8];
uint8_t k = (PLCRxData.Buff[8] / 10) + 1; // это будет степень, без минуса
uint8_t i = 10 - (PLCRxData.Buff[8] % 10); // (0,2) 2 ->10-2=8 -> надо получить 10^0.8
if(i == 10){ i = 0; k -= 1; }
uint8_t tmp = Kosh_sigMass[i] * 3;
if((tmp / 100) != 0){ tmp /= 10; k -= 1; }
PointPLCData->HF_Channel[ch].Kosh_sig = tmp;// * 3; // умножаем на 3 так как ошибки не битовые а символьные, примерно ода символьная ошибка это 3 битовых
PointPLCData->HF_Channel[ch].Kosh_exp = k * (-1);
}
else {
PointPLCData->HF_Channel[ch].QualityNoise_Receiver = 0;
PointPLCData->HF_Channel[ch].Rate_Transmitter = 0;
PointPLCData->HF_Channel[ch].Rate_Receiver = 0;
PointPLCData->HF_Channel[ch].SNR_Receiver = 0;
PointPLCData->HF_Channel[ch].Kosh_sig = 0;
PointPLCData->HF_Channel[ch].Kosh_exp = 0;
}
PointPLCData->HF_Channel[ch].Level_InputSignal = PLCRxData.Buff[2] * (-2); // передаем число умноженное на 10 отрицательное число
if(PointPLCData->Power_PRD != P_NONE){
float tmp = (PLCRxData.Buff[2] * -0.2) + (0.007 * PointPLCData->HF_Channel[ch].Frequency_Receiver) + 9 + (2 * (PointPLCData->Power_PRD - 1)) + (2 * PointPLCData->Type_Filter) + (PointPLCData->FilterATT * 6);
PointPLCData->HF_Channel[ch].Level_Receiver = (int16_t)(tmp * 10); // передаем число умноженное на 10 отрицательное число
}
else PointPLCData->HF_Channel[ch].Level_Receiver = 0;
if(PointPLCData->HF_Channel[ch].Status_Receiver != RECEIVING) Common.Block[Common.CurrentIndexBlock].Alarm |= (1 << ch);
else Common.Block[Common.CurrentIndexBlock].Alarm &= ~(1 << ch);
}
else Common.Block[Common.CurrentIndexBlock].Alarm &= ~(1 << ch);
}
}// end ответ от MD
else { // ответ от любой платы
Common.CurrentIndexUM = 0xFF;
if(board == 0x0A) Common.CurrentIndexUM = 0;// ответ от UM02 первая плата
else if(board == 0X0C) Common.CurrentIndexUM = 1;// ответ от UM02 вторая плата
if(Common.CurrentIndexUM != 0xFF){
if(PLCRxData.Buff[0] == 0x01){
PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Version = PLCRxData.Buff[1];
if(PLCRxData.Buff[2] & 0x1E){
if(PLCRxData.Buff[2] & 0x02) PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Alarm = LINE_BREAK;
else if(PLCRxData.Buff[2] & 0x04) PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Alarm = FAILURE_PRD;
else if(PLCRxData.Buff[2] & 0x08) PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Alarm = NO_SIGNAL_PWR_AMP;
else if(PLCRxData.Buff[2] & 0x16) PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Alarm = HIGH_TEMPERATURE;
Common.Block[Common.CurrentIndexBlock].Alarm |= (1 << (Common.CurrentIndexUM + 4));
}
else {
PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Alarm = NO_ALARM;
Common.Block[Common.CurrentIndexBlock].Alarm &= ~(1 << (Common.CurrentIndexUM + 4));
}
PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Level_Transmitter = PLCRxData.Buff[3];
PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Temp = PLCRxData.Buff[4];
if(PLCRxData.LenBuff == 7)PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Voltage = PLCRxData.Buff[6];
else PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Voltage = 0x00;
PointPLCData->PowerAmplifier[Common.CurrentIndexUM].Presence = 1;
Common.Block[Common.CurrentIndexBlock].UM[Common.CurrentIndexUM].Presence = 1; // выставляем флаг что UM ответил
Common.Block[Common.CurrentIndexBlock].UM[Common.CurrentIndexUM].RequestCnt = 0;
}
}
if((PointPLCData->PowerAmplifier[0].Presence) && (PointPLCData->PowerAmplifier[1].Presence)) PointPLCData->Power_PRD = P_40W;
else if((PointPLCData->PowerAmplifier[0].Presence) || (PointPLCData->PowerAmplifier[1].Presence)) PointPLCData->Power_PRD = P_20W;
else PointPLCData->Power_PRD = P_NONE;
}
}
} // end cmd для меня
}
else if(PLCRxData.Query){ // был запрос данных у нас(если нужно)
__debug(DEBUG_SERVSE, "my pack\r\n");
my_cmd_plc(&PLCRxData, &PLCTxData);
}
}
}
/* коммон аларм обновляем всегда*/
for(uint8_t i = 0; i < AMOUNT_BLOCK_PLC; i++){
if(PLC_Data[i].EnablePoll){
if(PLC_Data[i].ChangeSettings){ // если было изменение настроек маршрута, говорим что блок отсутствует
Common.Block[i].Presece = 0;
Common.Block[i].FirstRequest = 1; // скидываем для отправки только первой команды
Common.Block[i].RequestCnt = 0;
PLC_Data[i].ChangeSettings = 0;
}
if(Common.Block[i].Presece){
if(Common.Block[i].Alarm) PLC_Data[i].CommonAlarm = 2;
else PLC_Data[i].CommonAlarm = 0;
}
else {
PLC_Data[i].CommonAlarm = 1;// блок отсутствует
PLC_Data[i].VersionMD = 0;
for(uint8_t q = 0; q < 3; q++)PLC_Data[i].HF_Channel[q].En = 0; // фиксируем что канал выключен
for(uint8_t q = 0; q < 2; q++)PLC_Data[i].PowerAmplifier[q].Presence = 0; // фиксируем что УМ отсутствует
}
}
}
/************************ Формирование команд и отправка в блоки *******************************/
if(xSemaphoreTake(SemaphorePolling, 0) == pdTRUE){ // семафор для подготовки данных для отправки
uint8_t indexPoll = 0;
for(uint8_t i = 0; i < AMOUNT_BLOCK_PLC; i++){ // проходим по все 4 блокам
if((Common.Block[i].NewTime == TimerCounter) || ((Common.Block[i].En != PLC_Data[i].EnablePoll) && (PLC_Data[i].EnablePoll))){ // пришло время опроса или если добавили блок для опроса
if(PLC_Data[i].EnablePoll) {// у всех блоков смотрим этот параметр
if(i != 0x00){
if(Common.Block[i].PointBoardCmd == 0x00) Common.Block[i].Timeout = 1; //выставляем время между командами 1 сек, так как мы начинаем новый цикл опросов дальних блоков
}
else Common.Block[i].Timeout = PLC_Data[i].PollingTimeout;
if((PLC_Data[i].Route[0] > 0) || (i == 0)) indexPoll |= (1 << i);
}
// else Common.Block[i].Timeout = PLC_Data[i].PollingTimeout; // если нам запрещено опрашивать какой то дальний блок, то каждые n секунд проверяем флаг enable poll
Common.Block[i].NewTime = TimerCounter + Common.Block[i].Timeout; // вычисляем время следующего опроса
}
Common.Block[i].En = PLC_Data[i].EnablePoll;
}
for(uint8_t i = 0; (indexPoll != 0) || (i < AMOUNT_BLOCK_PLC);){
if(indexPoll & (1 << i)){ // есть флаг для передачи
_Bool Send = 0;
uint8_t routeByte = 0;
PLCTxData.Pointer = 0x01;
PLCTxData.LenBuff = 0;
if((!Common.Block[i].FirstRequest) && (Common.Block[i].PointBoardCmd == 0x00)) Common.Block[i].PointBoardCmd++; // пропускаем первую команду, так как версию запрашивать не нужно
if((uint8_t)(Common.BoardCmd[Common.Block[i].PointBoardCmd] >> 8) == 0x0A) Common.CurrentIndexUM = 0;// первый УМ02
else if((uint8_t)(Common.BoardCmd[Common.Block[i].PointBoardCmd] >> 8) == 0x0C) Common.CurrentIndexUM = 1;// второй УМ02
else Common.CurrentIndexUM = 0xFF;
PLCTxData.Buff[PLCTxData.LenBuff++] = (PLC_ADDR_BOARD << 4);
if(i != 0){
routeByte = (PLC_Data[i].Route[0] + 1) / 2; // вычисляем с какого байта берем данные
memcpy(&PLCTxData.Buff[PLCTxData.LenBuff], &PLC_Data[i].Route[1], routeByte);
PLCTxData.LenBuff += routeByte;
if((PLC_Data[i].Route[0] % 2) == 0) PLCTxData.Buff[PLCTxData.LenBuff++] = (uint8_t)(Common.BoardCmd[Common.Block[i].PointBoardCmd] >> 8);// четное, поставляем в младшую
else PLCTxData.Buff[routeByte] |= (uint8_t)((Common.BoardCmd[Common.Block[i].PointBoardCmd] >> 8) << 4);
}
else PLCTxData.Buff[PLCTxData.LenBuff++] = (uint8_t)(Common.BoardCmd[Common.Block[i].PointBoardCmd] >> 8);
PLCTxData.Buff[PLCTxData.LenBuff++] = (uint8_t)Common.BoardCmd[Common.Block[i].PointBoardCmd++];
if(Common.CurrentIndexUM != 0xFF){ // передача команды ум 02
if(Common.Block[i].UM[Common.CurrentIndexUM].Presence){ // ум присутствует
if(Common.Block[i].UM[Common.CurrentIndexUM].RequestCnt++ == 10){
//__debug(DEBUG_SERVSE, "block %d, UM no answer\r\n", i);
PLC_Data[i].PowerAmplifier[Common.CurrentIndexUM].Presence = 0; // тут мы фиксируем что УМ отсутствует
PLC_Data[i].PowerAmplifier[Common.CurrentIndexUM].Level_Transmitter = 0;
PLC_Data[i].PowerAmplifier[Common.CurrentIndexUM].Temp = 0;
PLC_Data[i].PowerAmplifier[Common.CurrentIndexUM].Voltage = 0;
Common.Block[i].UM[Common.CurrentIndexUM].Presence = 0;
Common.Block[i].UM[Common.CurrentIndexUM].RequestCnt = 0;
}
else if(Common.Block[i].UM[Common.CurrentIndexUM].RequestCnt == 1) Send = 1; // отправить нужно один раз!!! зачем?????
}
else{ // ум отсутствует
if(Common.Block[i].UM[Common.CurrentIndexUM].RequestCnt++ == 10){ // сделать бы адаптивно зависящее от времени поллинга
//__debug(DEBUG_SERVSE, "block %d, UM re-request\r\n", i);
Common.Block[i].UM[Common.CurrentIndexUM].Presence = 1;
Common.Block[i].UM[Common.CurrentIndexUM].RequestCnt = 0;
Send = 1;
}
}
}
else Send = 1; // ето не UM отправляем полюбому
if(Send) {
if(__debug(DEBUG_PLC_CROSS,"********* TX CROSS *********\r\n")){ // выводим дебаг
sBuffCnt = 0;
sBuffCnt += sprintf(&sBuffer[sBuffCnt], "pointer:%02X\r\ndata [%d]:", PLCTxData.Pointer, PLCTxData.LenBuff);
for(uint16_t i = 0; i < PLCTxData.LenBuff; i++)sBuffCnt += sprintf(&sBuffer[sBuffCnt],"%02X", PLCTxData.Buff[i]);
__debug(DEBUG_PLC_CROSS,"%s\r\n***************************************\r\n", sBuffer);
}
if(uxQueueMessagesWaiting(QueueCmdTxPLC) == MAX_ELEMENTS_TX){ // если очередь полная на передачу!!
if(xQueueSend(QueueCmdTxPLC, &PLCTxData, 3000) == errQUEUE_FULL) xQueueReset(QueueCmdTxPLC);// если через 3 сек очередь не опустела то сбрасываем ее сами
}
else xQueueSend(QueueCmdTxPLC, &PLCTxData, portMAX_DELAY);
}
if(Common.Block[i].PointBoardCmd >= AMOUNT_BOARD_CMD) { // значит было 9 или больше и этим самым мы понимаем что отправили все
Common.Block[i].PointBoardCmd = 0;
if(Common.Block[i].RequestCnt++ == 5){ // было отправлено 5 запросов, но без ответа
Common.Block[i].Presece = 0; // сообщаем что блок отсутствует
Common.Block[i].FirstRequest = 1; // скидываем для отправки только первой команды
Common.Block[i].RequestCnt = 0;
Common.Block[i].UM[0].Presence = 1; // для следующих запросов
Common.Block[i].UM[0].RequestCnt = 0;
Common.Block[i].UM[1].Presence = 1; // для следующих запросов
Common.Block[i].UM[1].RequestCnt = 0;
}
if(i != 0x00) Common.Block[i].NewTime = TimerCounter + PLC_Data[i].PollingTimeout;// если передача дальних блоков и передали все, то ждем установленный timeout;
indexPoll &= ~(1 << i);
i++;
}
else {
if(Common.Block[i].FirstRequest){ // если блоки еще не отвечали
Common.Block[i].PointBoardCmd = 0;
if(i == 0x00) Common.Block[i].NewTime = TimerCounter + 10;// ближний блок
else Common.Block[i].NewTime = TimerCounter + 30;
indexPoll &= ~(1 << i);
i++;
}
else if((i != 0x00) && (Send)){ // дальний блок, и передали не все
indexPoll &= ~(1 << i);
i++;
}
}
Send = 0;
}
else i++;
}
TimerCounter++;
}
/*********************************************************************************************************/
osDelay(1);
}
}
/* ----------------------------------------------------------------------------------------- */
/* ######################################################################################### */
void GetContentType(char *FileName, char *ContentType);
int32_t substr_len(char *str, char symbol, char addSymbol);
/* WEB SERVER TASK ------------------------------------------------------------------------- */
#include "cookie.h"
static void WebServer_task(void *param)
{
__logWrite("HTTP daemon started\n");
enum {
STATE_WAIT_ETH_TASK,
STATE_CONNECT_NEW,
STATE_BIND,
STATE_LISTEN
}FSM_State = STATE_WAIT_ETH_TASK;
struct netbuf *nbuf;
ip_addr_t remote_addr;
uint16_t remoute_port;
uint16_t TCPlenData = 0;
char *TCPdata = NULL;
volatile _Bool notFountPage = 0, noMemory = 0;
uint16_t LenContent = 0;
uint16_t cnt_timeout = 0;
//ip_addr_t *addr;
/**************/
char ContentType[32], CodeResponse[16], HeaderHTTP[1024];
char *FileNameHTTP = NULL;
uint8_t *FileHTTP;
uint32_t FileSizeHTTP;
FIL FSFileHTTP;
// DIR dirFAT;
/**************/
request_typDef request = {0};
char *Cookie = NULL;/* ищем куки в хедере */
char *str = NULL;
char response_json[512] = {0}, response_cookie[512] = {0};
char *JSON_Request = NULL;
struct netconn *serv_conn, *conn;
err_t conn_err;
for(;;)
{
switch(FSM_State){
case STATE_WAIT_ETH_TASK:
if(ETH_linkState == LINK_UP) FSM_State = STATE_CONNECT_NEW;
vTaskDelay(1);
break;
case STATE_CONNECT_NEW:
serv_conn = netconn_new(NETCONN_TCP);
if(serv_conn != NULL) FSM_State = STATE_BIND;
vTaskDelay(1);
break;
case STATE_BIND:
conn_err = netconn_bind(serv_conn, IP_ADDR_ANY, 80);
if(conn_err == ERR_OK) {
netconn_listen(serv_conn);
__debug(DEBUG_WEB_SERVER, "Web server listen port 80\r\n");
FSM_State = STATE_LISTEN;
}
vTaskDelay(1);
break;
case STATE_LISTEN:
conn_err = netconn_accept(serv_conn, &conn);
if(conn_err == ERR_OK){
wdt_reset();
__debug(DEBUG_WEB_SERVER, "Connection\r\n");
TCPlenData = 0;
FileSizeHTTP = 0;
notFountPage = 0;
noMemory = 0;
LenContent = 0;
request.cookie->len = 0;
request.cookie->tokenCount = 0;
request.json_len = 0;
netconn_set_recvtimeout(conn, 200);
conn_err = netconn_recv(conn, &nbuf);
if(conn_err == ERR_OK){
wdt_reset();
netconn_getaddr(conn, &remote_addr, &remoute_port, 0); // получаем адресс удаленный
netbuf_data(nbuf, (void**)&TCPdata, &TCPlenData);
TCPdata[TCPlenData] = 0;
if(strncmp(TCPdata, "GET /", 5) == 0){
int16_t lenNameFile = substr_len(&TCPdata[5], ' ', NULL);
if(lenNameFile == 0){
FileNameHTTP = pvPortMalloc(15);
if(FileNameHTTP != NULL){
strcpy(FileNameHTTP, "web/index.html");
FileNameHTTP[14] = 0;
}
else noMemory = 1;
}
else if(lenNameFile > 0){
FileNameHTTP = pvPortMalloc(lenNameFile + 5);
if(FileNameHTTP != NULL){
if((strncmp(&TCPdata[5], "config.xml", 10) == 0) || (strncmp(&TCPdata[5], "mibs/", 5) == 0))memcpy(FileNameHTTP, &TCPdata[5], lenNameFile);
else {
sprintf(FileNameHTTP, "web/");
memcpy(&FileNameHTTP[4], &TCPdata[5], lenNameFile);
lenNameFile += 4;
}
FileNameHTTP[lenNameFile] = 0;
}
else noMemory = 1;
}
if(noMemory){
__debug(DEBUG_WEB_SERVER, "No memory\r\n", nbuf->addr.addr);
netconn_close(conn);
netbuf_delete(nbuf);
netconn_delete(conn);
continue;
}
if(xSemaphoreTake(MutexAccessFlash, portMAX_DELAY) == pdTRUE){
//struct dirpath *dir = f_get_path((char*)FileNameHTTP);
//if(dir != NULL){
//if(dir->dirname != NULL){
// if(f_opendir(&dirFAT, dir->dirname) == FR_NO_PATH) {
// f_closedir(&dirFAT);
// f_dir_path_free(dir);
// notFountPage = 1;
// }
//else dir->statedir = OPEN_DIR;
//}
//if(dir->fname != NULL) {
if(f_open(&FSFileHTTP, FileNameHTTP/*dir->fname*/, FA_READ) == FR_OK){ // есть файл, то отдаем его
FileSizeHTTP = f_size(&FSFileHTTP);
strcpy(CodeResponse, "200 OK");
GetContentType(FileNameHTTP/*dir->fname*/, ContentType);
sprintf(HeaderHTTP, "HTTP/1.1 %s\r\nContent-Type: %s; charset=utf-8\r\nConnection: close\r\nContent-Length: %d\r\n\r\n", CodeResponse, ContentType, FileSizeHTTP);
netconn_write(conn, HeaderHTTP, strlen(HeaderHTTP), FileSizeHTTP ? (NETCONN_NOCOPY | NETCONN_MORE) : NETCONN_NOCOPY);
if(FileSizeHTTP > 0){
uint32_t ByteCountRead = 0, ByteRead = 0, ByteWritten = 0;
uint8_t TCP_Flag = NETCONN_COPY | NETCONN_MORE;
if(FileSizeHTTP > TCP_MSS) FileHTTP = pvPortMalloc(TCP_MSS);
else FileHTTP = pvPortMalloc(FileSizeHTTP);
if(FileHTTP != NULL){
while(1){
if((FileSizeHTTP - ByteRead) > TCP_MSS) ByteCountRead = TCP_MSS;
else {
TCP_Flag &= ~NETCONN_MORE;
ByteCountRead = FileSizeHTTP - ByteRead;
}
f_lseek(&FSFileHTTP, ByteRead);
f_read(&FSFileHTTP, FileHTTP, (UINT)ByteCountRead, (UINT*)&ByteWritten);
netconn_write(conn, FileHTTP, ByteCountRead, TCP_Flag);
ByteRead += ByteCountRead;
if(ByteRead >= FileSizeHTTP)break;
}
vPortFree(FileHTTP);
}
else notFountPage = 1;
}
f_close(&FSFileHTTP);
}
else notFountPage = 1;
//if(dir->statedir == OPEN_DIR)f_closedir(&dirFAT);
//f_dir_path_free(dir);
//}
//else notFountPage = 1;
//}
//else notFountPage = 1;
if(notFountPage){
strcpy(CodeResponse, "404 Not Found");
strcpy(ContentType, "text/html");
sprintf(HeaderHTTP, "HTTP/1.1 %s\r\nContent-Type: %s; charset=utf-8\r\nConnection: close\r\nContent-Length: %d\r\n\r\n", CodeResponse, ContentType, FileSizeHTTP);
netconn_write(conn, HeaderHTTP, strlen(HeaderHTTP), NETCONN_NOCOPY);
}
}
vPortFree(FileNameHTTP);
xSemaphoreGive(MutexAccessFlash);
}
else if(strncmp(TCPdata, "POST /api.c", 11) == 0){ //POST /config
/* ищем длину контента */
str = strstr(TCPdata, "Content-Length:");
if(str != NULL){
str += 16; // сдвигаем указатель на 16 символов перед
LenContent = atoi(str);
}
/* ************************ */
str = strstr(TCPdata, "Cookie:"); // сдвигаем указатель на 8 символов перед
if(str != NULL) {// есть куки
str += 8;
uint16_t LenCookie = substr_len(str, '\r', '\n');
if(LenCookie > 0){
Cookie = pvPortMalloc(LenCookie);
if(Cookie != NULL){
memcpy(Cookie, str, LenCookie);
cookie_token_typDef cookie_token[8] = {0};
cookie_token_data_typDef cookie_data = {0};
cookie_data.data = Cookie;
cookie_data.len = LenCookie;
cookie_data.tokens = cookie_token;
cookie_parse_tokens(&cookie_data);
request.cookie = &cookie_data;
}
else request.cookie = NULL;
}
else request.cookie = NULL;
}
else request.cookie = NULL;
//if(Cookie == NULL) request.cookie = NULL;
/* ************************ */
if(LenContent > 0){
JSON_Request = pvPortMalloc(LenContent + 1); // выделяем память под полнзные данные, + ноль завершающий
if(JSON_Request != NULL){
memcpy(JSON_Request, &TCPdata[TCPlenData - LenContent], LenContent);
JSON_Request[LenContent] = 0;
request.json_data = JSON_Request;
request.json_len = LenContent;
request.remote_addr = &remote_addr;
__debug(DEBUG_WEB_SERVER, "\r\nrequest json %s\r\n", JSON_Request);
json_parse(&request, response_json, sizeof(response_json), response_cookie); // вернуть еще куки/ еще вернем результат выполнения команды
vPortFree(JSON_Request);
__debug(DEBUG_WEB_SERVER, "response json %s\r\n\r\n", response_json); // ответ для отправки
strcpy(CodeResponse, "200 OK");
GetContentType(".json", ContentType);
sprintf(HeaderHTTP, "HTTP/1.1 %s\r\nContent-Type: %s; charset=utf-8\r\nConnection: close\r\nContent-Length: %d\r\n%s\r\n", CodeResponse, ContentType, strlen(response_json), response_cookie);
netconn_write(conn, HeaderHTTP, strlen(HeaderHTTP), NETCONN_COPY | NETCONN_MORE);
netconn_write(conn, response_json, strlen(response_json), NETCONN_COPY);
}
}
vPortFree(Cookie);
}
}
else __debug(DEBUG_WEB_SERVER, "Receive timeout\r\n");
while(conn->state & NETCONN_WRITE){
wdt_reset();
if(cnt_timeout++ == 500) {
__debug(DEBUG_WEB_SERVER, "500ms receive timeout\r\n");
break; // принудительный таймаут на 500ms
}
else vTaskDelay(1);
}
cnt_timeout = 0;
__debug(DEBUG_WEB_SERVER, "Connection close\r\n");
netconn_close(conn);
netbuf_delete(nbuf);
netconn_delete(conn);
}
else vTaskDelay(1);
break;
}
}
}
/* ----------------------------------------------------------------------------------------- */
/* ######################################################################################### */
/* OTHER FUNCTION -------------------------------------------------------------------------- */
void GetContentType(char *FileName, char *ContentType)
{
uint8_t i = 0;
for(; FileName[i] != '.'; i++);
if(strncmp(&FileName[i], ".html", 5) == 0) strcpy(ContentType, "text/html");
else if(strncmp(&FileName[i], ".css", 4) == 0) strcpy(ContentType, "text/css");
else if(strncmp(&FileName[i], ".json", 5) == 0) strcpy(ContentType, "application/json");
else if(strncmp(&FileName[i], ".js", 3) == 0) strcpy(ContentType, "text/javascript");
else if(strncmp(&FileName[i], ".gif", 4) == 0) strcpy(ContentType, "image/gif");
else if(strncmp(&FileName[i], ".jpeg", 5) == 0) strcpy(ContentType, "image/jpeg");
else if(strncmp(&FileName[i], ".png", 4) == 0) strcpy(ContentType, "image/png");
else if(strncmp(&FileName[i], ".ico", 4) == 0) strcpy(ContentType, "image/vnd.microsoft.icon");
else strcpy(ContentType, "text/plain");
}
int32_t substr_len(char *str, char symbol, char addSymbol) // возвращает длину подстроки, 0 если нет конечного символа
{
if(str == NULL) return -1;
uint16_t len = 0;
uint16_t PosEnd = 0;
uint16_t strLen = strlen(str);
if(strLen == 0) return -1;
if(addSymbol == NULL){
for(; str[PosEnd] != symbol; PosEnd++)
{
len++;
if(strLen == len) return -1;
}
}
else {
for(;; PosEnd++)
{
if((str[PosEnd] == symbol) || (str[PosEnd] == addSymbol)) break;
else len++;
if(strLen == len) return -1;
}
}
return len;
}
/* ----------------------------------------------------------------------------------------- */
/* ######################################################################################### */
/* GPIO CALLBACK --------------------------------------------------------------------------- */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == PHY_INT_Pin) // interrupt PHY
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(SemaphoreIRQ_PHY, &xHigherPriorityTaskWoken);
}
}
/* USER CODE END Application */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/