/****************************************************************************
 * apps/examples/foc/foc_nxscope.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <errno.h>
#include <unistd.h>
#include <string.h>

#include "foc_debug.h"
#include "foc_nxscope.h"
#include "foc_thr.h"

#include "industry/foc/foc_common.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#ifndef CONFIG_LOGGING_NXSCOPE_DISABLE_PUTLOCK
#  error CONFIG_LOGGING_NXSCOPE_DISABLE_PUTLOCK must be set to proper operation.
#endif

#if defined(CONFIG_LOGGING_NXSCOPE_INTF_SERIAL) && !defined(CONFIG_SERIAL_RTT)
#  ifndef CONFIG_SERIAL_TERMIOS
#    error CONFIG_SERIAL_TERMIOS must be set to proper operation.
#  endif
#endif

/****************************************************************************
 * Private Functions
 ****************************************************************************/

#ifdef CONFIG_EXAMPLES_FOC_NXSCOPE_THREAD
/****************************************************************************
 * Name: foc_nxscope_thr
 ****************************************************************************/

static FAR void *foc_nxscope_thr(FAR void *arg)
{
  FAR struct foc_nxscope_s *nxs = (FAR struct foc_nxscope_s *)arg;

  DEBUGASSERT(nxs);

  while (1)
    {
      /* NxScope work */

      foc_nxscope_work(nxs);

      usleep(10000);
    }

  return NULL;
}
#endif

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: foc_nxscope_init
 ****************************************************************************/

int foc_nxscope_init(FAR struct foc_nxscope_s *nxs)
{
  union nxscope_chinfo_type_u u;
  struct nxscope_cfg_s        nxs_cfg;
#ifdef CONFIG_EXAMPLES_FOC_NXSCOPE_THREAD
  struct sched_param          param;
  pthread_attr_t              attr;
  pthread_t                   nxsthr = 0;
#endif
  int                         ret    = OK;
  int                         i      = 0;
  int                         j      = 0;

  DEBUGASSERT(nxs);

  /* Initialize serial interface */

  nxs->ser_cfg.path     = CONFIG_EXAMPLES_FOC_NXSCOPE_SERIAL_PATH;
  nxs->ser_cfg.nonblock = true;
  nxs->ser_cfg.baud     = CONFIG_EXAMPLES_FOC_NXSCOPE_SERIAL_BAUD;

  ret = nxscope_ser_init(&nxs->intf, &nxs->ser_cfg);
  if (ret < 0)
    {
      PRINTF("ERROR: nxscope_ser_init failed %d\n", ret);
      goto errout;
    }

  /* Default serial protocol */

  ret = nxscope_proto_ser_init(&nxs->proto, NULL);
  if (ret < 0)
    {
      PRINTF("ERROR: nxscope_proto_ser_init failed %d\n", ret);
      goto errout;
    }

  /* Initialize nxscope */

  nxs_cfg.intf_cmd      = &nxs->intf;
  nxs_cfg.proto_cmd     = &nxs->proto;
  nxs_cfg.intf_stream   = &nxs->intf;
  nxs_cfg.proto_stream  = &nxs->proto;
  nxs_cfg.channels      = CONFIG_EXAMPLES_FOC_NXSCOPE_CHANNELS;
  nxs_cfg.streambuf_len = CONFIG_EXAMPLES_FOC_NXSCOPE_STREAMBUF_LEN;
  nxs_cfg.rxbuf_len     = CONFIG_EXAMPLES_FOC_NXSCOPE_RXBUF_LEN;
  nxs_cfg.rx_padding    = CONFIG_EXAMPLES_FOC_NXSCOPE_RXPADDING;
  nxs_cfg.callbacks     = &nxs->cb;

  ret = nxscope_init(&nxs->nxs, &nxs_cfg);
  if (ret < 0)
    {
      PRINTF("ERROR: nxscope_init failed %d\n", ret);
      goto errout;
    }

  /* Get channels per instance */

  for (j = 0; j < CONFIG_MOTOR_FOC_INST; j += 1)
    {
      if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & (1 << j))
        {
          nxs->ch_per_inst += 1;
        }
    }

  /* For all FOC controllers */

  for (j = 0; j < CONFIG_MOTOR_FOC_INST; j += 1)
    {
      /* Get controller data type */

      switch (foc_thread_type(j))
        {
#if CONFIG_EXAMPLES_FOC_FLOAT_INST > 0
          case FOC_NUMBER_TYPE_FLOAT:
            {
              u.s.dtype = NXSCOPE_TYPE_FLOAT;
              break;
            }
#endif

#if CONFIG_EXAMPLES_FOC_FIXED16_INST > 0
          case FOC_NUMBER_TYPE_FIXED16:
            {
              u.s.dtype = NXSCOPE_TYPE_B16;
              break;
            }
#endif

          default:
            {
              ASSERT(0);
            }
        }

      /* Create channels */

#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG == 0)
      UNUSED(u);
#endif

#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_IABC)
      nxscope_chan_init(&nxs->nxs, i++, "iabc", u.u8,
                        CONFIG_MOTOR_FOC_PHASES, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_IDQ)
      nxscope_chan_init(&nxs->nxs, i++, "idq", u.u8, 2, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_IAB)
      nxscope_chan_init(&nxs->nxs, i++, "iab", u.u8, 2, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VABC)
      nxscope_chan_init(&nxs->nxs, i++, "vabc", u.u8,
                        CONFIG_MOTOR_FOC_PHASES, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VDQ)
      nxscope_chan_init(&nxs->nxs, i++, "vdq", u.u8, 2, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VAB)
      nxscope_chan_init(&nxs->nxs, i++, "vab", u.u8, 2, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_AEL)
      nxscope_chan_init(&nxs->nxs, i++, "a_el", u.u8, 1, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_AM)
      nxscope_chan_init(&nxs->nxs, i++, "a_m", u.u8, 1, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VEL)
      nxscope_chan_init(&nxs->nxs, i++, "v_el", u.u8, 1, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VM)
      nxscope_chan_init(&nxs->nxs, i++, "v_m", u.u8, 1, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VBUS)
      nxscope_chan_init(&nxs->nxs, i++, "vbus", u.u8, 1, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_SPTORQ)
      nxscope_chan_init(&nxs->nxs, i++, "sp_torq", u.u8, 3, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_SPVEL)
      nxscope_chan_init(&nxs->nxs, i++, "sp_vel", u.u8, 3, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_SPPOS)
      nxscope_chan_init(&nxs->nxs, i++, "sp_pos", u.u8, 3, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_DQREF)
      nxscope_chan_init(&nxs->nxs, i++, "dqref", u.u8, 2, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VDQCOMP)
      nxscope_chan_init(&nxs->nxs, i++, "vdqcomp", u.u8, 2, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_SVM3)
      nxscope_chan_init(&nxs->nxs, i++, "svm3", u.u8, 4, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_VOBS)
      nxscope_chan_init(&nxs->nxs, i++, "vobs", u.u8, 1, 0);
#endif
#if (CONFIG_EXAMPLES_FOC_NXSCOPE_CFG & FOC_NXSCOPE_AOBS)
      nxscope_chan_init(&nxs->nxs, i++, "aobs", u.u8, 1, 0);
#endif

      if (i > CONFIG_EXAMPLES_FOC_NXSCOPE_CHANNELS)
        {
          PRINTF("ERROR: invalid nxscope channels value %d\n", i);
          ret = -ENOBUFS;
          goto errout;
        }
    }

#ifdef CONFIG_EXAMPLES_FOC_NXSCOPE_THREAD
  /* Configure thread */

  pthread_attr_init(&attr);
  param.sched_priority = CONFIG_EXAMPLES_FOC_NXSCOPE_PRIO;
  pthread_attr_setschedparam(&attr, &param);
  pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_FOC_NXSCOPE_STACKSIZE);

  /* Create nxscope thread */

  ret = pthread_create(&nxsthr, &attr, foc_nxscope_thr, nxs);
  if (ret != OK)
    {
      PRINTF("ERROR: pthread_create failed %d\n", ret);
      goto errout;
    }

  /* Set thread name */

  ret = pthread_setname_np(nxsthr, "nxsthr");
  if (ret != OK)
    {
      PRINTF("ERROR: pthread_setname_np failed %d\n", ret);
      goto errout;
    }
#endif

errout:
  return ret;
}

/****************************************************************************
 * Name: foc_nxscope_deinit
 ****************************************************************************/

void foc_nxscope_deinit(FAR struct foc_nxscope_s *nxs)
{
  DEBUGASSERT(nxs);

  nxscope_ser_deinit(nxs->nxs.intf_cmd);
  nxscope_proto_ser_deinit(nxs->nxs.proto_cmd);
  nxscope_deinit(&nxs->nxs);
}

/****************************************************************************
 * Name: foc_nxscope_work
 ****************************************************************************/

void foc_nxscope_work(FAR struct foc_nxscope_s *nxs)
{
  int ret = OK;

  /* Flush stream data */

  ret = nxscope_stream(&nxs->nxs);
  if (ret < 0)
    {
      PRINTF("ERROR: nxscope_stream failed %d\n", ret);
    }

  ret = nxscope_recv(&nxs->nxs);
  if (ret < 0)
    {
      PRINTF("ERROR: nxscope_recv failed %d\n", ret);
    }
}
