/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <string.h>

#include "lwip/ip_addr.h"
#include "lwip/tcp.h"
#include "lwip/err.h"
#include "lwip/opt.h"
#include "lwip/sockets.h"
#include "stm32/ModbusS.h"
// #include "log.h"

#define LOG_TAG "[mb_tcp_sr]: "
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
// static err_t ModBusSlave_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
// static err_t ModBusSlave_accept(void *arg, struct tcp_pcb *pcb, err_t err);
// static void ModBusSlave_conn_err(void *arg, err_t err);
// static err_t ModBusSlave_poll(void *arg, struct tcp_pcb *pcb);
/* Private functions ---------------------------------------------------------*/
/**
 * @brief  Called when a data is received on the telnet connection
 * @param  arg	the user argument
 * @param  pcb	the tcp_pcb that has received the data
 * @param  p	the packet buffer
 * @param  err	the error value linked with the received data
 * @retval error value
 */
int ModBusTcpMonitor;
static err_t ModBusSlave_recv(void *arg, struct tcp_pcb *newpcb, struct pbuf *p, err_t err) {
  /* We perform here any necessary processing on the pbuf */
  if (p != NULL) {
    uint8_t *txbuf;
    uint8_t *rxbuf;
    int txlen, rxlen;
    /* We call this function to tell the LwIp that we have processed the data */
    /* This lets the stack advertise a larger window, so more data can be received*/
    tcp_recved(newpcb, p->tot_len);
    rxbuf = (uint8_t *)p->payload;
    rxlen = p->len;
    if (rxlen < 6) {
      pbuf_free(p);
      return tcp_close(newpcb);
    }
    txbuf = (uint8_t *)mem_malloc(1024 + 16);
    if (txbuf == NULL) {
      return tcp_close(newpcb);
    }
    ModBusTcpMonitor = 0;
    txlen = ModbusSlaveProcess(&txbuf[6], &rxbuf[6], (rxlen - 6), 0);   //以前有  2023年4月24日14:39:11
    // log_printf(LINFO, LOG_TAG "[%s] ModbusSlaveProcess (ret=%d)", __FUNCTION__, txlen);
    if (txlen > 0) {
      int i;
      for (i = 0; i < 4; i++) {
        txbuf[i] = rxbuf[i];
      }
      txbuf[4] = (uint8_t)(txlen >> 8);
      txbuf[5] = (uint8_t)(txlen & 0xff);
      tcp_write(newpcb, txbuf, txlen + 6, 1);
      ModBusTcpMonitor = 0;
    }
    mem_free(txbuf);
    /* End of processing, we free the pbuf */
    pbuf_free(p);
  } else if (err == ERR_OK) {
    /* When the pbuf is NULL and the err is ERR_OK, the remote end is closing the connection. */
    /* We free the allocated memory and we close the connection */
    // log_printf(LWARN, LOG_TAG "remote end is closing the connection\n");
    tcp_err(newpcb, NULL);
    tcp_recv(newpcb, NULL);
    tcp_poll(newpcb, NULL, 120);  // 60 second timeout
    // return tcp_close(newpcb);
    err_t err_p = tcp_close(newpcb);
    if (ERR_OK != err_p) {  
      // log_printf(LERROR, LOG_TAG  "close error. err:%d\n", err_p);  
    }
    return err_p;
  }
  return ERR_OK;
}
/**
 * @brief  This function is called when an error occurs on the connection
 * @param  arg
 * @parm   err
 * @retval None
 */
static void ModBusSlave_conn_err(void *arg, err_t err) {
  // log_printf(LERROR, LOG_TAG "connection error, err:%d\n", err);
}
static err_t ModBusSlave_poll(void *arg, struct tcp_pcb *newpcb) {
  if (newpcb) {
    // log_printf(LERROR, LOG_TAG "close pcb\n");
    tcp_close(newpcb);
    newpcb = NULL;
  }
  return ERR_OK;
}
/**
 * @brief  This function when the Telnet connection is established
 * @param  arg  user supplied argument
 * @param  pcb	 the tcp_pcb which accepted the connection
 * @param  err	 error value
 * @retval ERR_OK
 */
static err_t ModBusSlave_accept(void *arg, struct tcp_pcb *pcb, err_t err) {
  /* Tell LwIP to associate this structure with this connection. */
  // tcp_arg(pcb, mem_calloc(sizeof(struct name), 1));
  /* Configure LwIP to use our call back functions. */
  tcp_err(pcb, ModBusSlave_conn_err);
  tcp_recv(pcb, ModBusSlave_recv);
  tcp_poll(pcb, ModBusSlave_poll, 120);  // 60 second timeout
  /* Send out the first message */
  // tcp_write(pcb, GREETING, strlen(GREETING), 1);
  return ERR_OK;
}
/**
 * @brief  Initialize the hello application
 * @param  None
 * @retval None
 */
void ModBusTCPSlave_init(void) {
  struct tcp_pcb *pcb;
  /* Create a new TCP control block  */
  pcb = tcp_new();
  /* Assign to the new pcb a local IP address and a port number */
  /* Using IP_ADDR_ANY allow the pcb to be used by any local interface */
  tcp_bind(pcb, IP_ADDR_ANY, 502);
  /* Set the connection to the LISTEN state */
  pcb = tcp_listen(pcb);
  /* Specify the function to be called when a connection is established */
  tcp_accept(pcb, ModBusSlave_accept);
  // LSAPI_Log_Debug("ModBus Tcp server listen port:%d\n", 502);
}
/******************* (C) COPYRIGHT 2009 STMicroelectronics *****END OF FILE****/