/**
 * OscilloscopeGeneratorTrigger.cpp
 *
 * This example sets up the generator to generate a 1 kHz triangle waveform, 4 Vpp.
 * It also sets up the oscilloscope to perform a block mode measurement, triggered on "Generator new period".
 * A measurement is performed and the data is written to OscilloscopeGeneratorTrigger.csv.
 *
 * Find more information on http://www.tiepie.com/LibTiePie .
 */
#include "PrintInfo.hpp"
#include <fstream>
#include <iostream>
#include <thread>
#if defined(__linux) || defined(__unix)
#  include <cstdlib>
#  include <unistd.h>
#endif
int main()
{
  int status = EXIT_SUCCESS;
  // Initialize library:
  TiePie::Hardware::Library::init();
  // Print library information:
  printLibraryInfo();
  // Enable network search:
  TiePie::Hardware::Network::setAutoDetectEnabled(true);
  // Update device list:
  TiePie::Hardware::DeviceList::update();
  // Try to open an oscilloscope with block measurement support and a generator in the same device:
  std::unique_ptr<TiePie::Hardware::Oscilloscope> scp;
  std::unique_ptr<TiePie::Hardware::Generator> gen;
  for(uint32_t i = 0; !(scp && gen) && i < TiePie::Hardware::DeviceList::count(); i++)
  {
    const auto item = TiePie::Hardware::DeviceList::getItemByIndex(i);
    if(item->canOpen(TIEPIE_HW_DEVICETYPE_OSCILLOSCOPE) && item->canOpen(TIEPIE_HW_DEVICETYPE_GENERATOR))
    {
      scp = item->openOscilloscope();
      // Check for valid pointer and block measurement support:
      if(scp && scp->measureModes() & TIEPIE_HW_MM_BLOCK)
        gen = item->openGenerator();
      else
        scp = nullptr;
    }
  }
  if(scp && gen)
  {
    uint16_t channelCount = 0;
    try
    {
      // Oscilloscope settings:
      // Get the number of channels:
      channelCount = scp->channels.count();
      // Set measure mode:
      scp->setMeasureMode(TIEPIE_HW_MM_BLOCK);
      // Set sample rate:
      scp->setSampleRate(1e6); // 1 MHz
      // Set record length:
      const uint64_t recordlength = scp->setRecordLength(10000); // 10 kS
      // Set pre sample ratio:
      scp->setPreSampleRatio(0); // 0 %
      // For all channels:
      for(const auto& channel : scp->channels)
      {
        // Enable channel to measure it:
        channel->setEnabled(true);
        // Set range:
        channel->setRange(8); // 8 V
        // Set coupling:
        channel->setCoupling(TIEPIE_HW_CK_DCV); // DC Volt
      }
      // Set trigger timeout:
      scp->trigger->setTimeout(1); // 1 s
      // Disable all channel trigger sources:
      for(const auto& channel : scp->channels)
        channel->trigger->setEnabled(false);
      // Locate trigger input:
      const auto& triggerInput = scp->triggerInputs.getById(TIEPIE_HW_TIID_GENERATOR_NEW_PERIOD); // or (TIEPIE_HW_TIID_GENERATOR_START || (TIEPIE_HW_TIID_GENERATOR_STOP
      if(!triggerInput)
        throw std::runtime_error("Unknown trigger input!");
      // Enable trigger input:
      triggerInput->setEnabled(true);
      // Generator settings:
      // Set signal type:
      gen->setSignalType(TIEPIE_HW_ST_TRIANGLE);
      // Set frequency:
      gen->setFrequency(1e3); // 1 kHz
      // Set amplitude:
      gen->setAmplitude(2); // 2 V
      // Set offset:
      gen->setOffset(0); // 0 V
      // Enable output:
      gen->setOutputEnable(true);
      // Print oscilloscope info:
      printDeviceInfo(*scp);
      // Print generator info:
      printDeviceInfo(*gen);
      // Start measurement:
      scp->start();
      // Start signal generation:
      gen->start();
      // Wait for measurement to complete:
      while(!scp->isDataReady())
      {
        // 10 ms delay, to save CPU time:
        using namespace std::chrono_literals;
        std::this_thread::sleep_for(10ms);
      }
      // Stop generator:
      gen->stop();
      // Disable output:
      gen->setOutputEnable(false);
      // Create data buffer:
      std::vector<std::vector<float>> channelData(channelCount, std::vector<float>(recordlength));
      std::vector<float*> channelDataPointers(channelCount);
      for(size_t i = 0; i < channelCount; i++)
        channelDataPointers[i] = channelData[i].data();
      // Get the data from the scope:
      uint64_t samplesRead = scp->getData(channelDataPointers.data(), channelCount, 0, recordlength);
      // Open file with write/update permissions:
      const std::string filename("OscilloscopeGeneratorTrigger.csv");
      std::ofstream csv(filename.c_str(), std::ofstream::out);
      if(csv.is_open())
      {
        // Write csv header:
        csv << "Sample";
        for(uint16_t ch = 0; ch < channelCount; ch++)
          csv << ";Ch" << (ch + 1);
        csv << '\n';
        // Write the data to csv:
        for(uint64_t i = 0; i < samplesRead; i++)
        {
          csv << i;
          for(uint16_t ch = 0; ch < channelCount; ch++)
            csv << ";" << channelData[ch][i];
          csv << '\n';
        }
        std::cout << "Data written to: " << filename << '\n';
        // Close file:
        csv.close();
      }
      else
      {
        std::cerr << "Couldn't open file: " << filename << '\n';
        status = EXIT_FAILURE;
      }
    }
    catch(const std::exception& e)
    {
      std::cerr << "Exception: " << e.what() << '\n';
      status = EXIT_FAILURE;
    }
    // Close oscilloscope:
    scp = nullptr;
    // Close generator:
    gen = nullptr;
  }
  else
  {
    std::cerr << "No oscilloscope available with block measurement support or generator available in the same unit!\n";
    status = EXIT_FAILURE;
  }
  // Finalize library:
  TiePie::Hardware::Library::fini();
  return status;
}
