mirror of
https://github.com/gwm17/Daqromancy.git
synced 2024-11-22 10:58:49 -05:00
First commit
This commit is contained in:
commit
d9d0b81cb4
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
*.o
|
||||
*.root
|
||||
*.d
|
||||
*.so
|
||||
*.pcm
|
||||
*.png
|
||||
|
||||
*.cxx
|
||||
*~
|
||||
|
||||
setting/infl_*
|
||||
.vscode/
|
||||
build/
|
||||
bin/
|
||||
out/
|
32
CMakeLists.txt
Normal file
32
CMakeLists.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
cmake_minimum_required(VERSION 3.16) #pch support
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20) #c++20
|
||||
|
||||
#currently default to debug. Eventually move to release
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
message("Building debug")
|
||||
else()
|
||||
message("Building release")
|
||||
endif()
|
||||
|
||||
project(Daqromancy)
|
||||
|
||||
set(DY_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||
set(DY_LIBRARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
||||
|
||||
#find the CAEN pre-reqs
|
||||
#For Windows, CAENDigitizer is installed non-globally (we assume default pathing cause why would ever be otherwise)
|
||||
set(CAEN_WIN_LIB_PATH "C:/Program Files/CAEN/Digitizers/Library/lib/x86_64")
|
||||
set(CAEN_WIN_INCL_PATH "C:/Program Files/CAEN/Digitizers/Library/include")
|
||||
if(MSVC)
|
||||
find_library(CAEN_DGTZ CAENDigitizer.lib HINTS ${CAEN_WIN_LIB_PATH} REQUIRED)
|
||||
else()
|
||||
find_library(CAEN_DGTZ CAENDigitizer REQUIRED)
|
||||
endif()
|
||||
|
||||
add_subdirectory(vendor/glfw)
|
||||
add_subdirectory(vendor/imgui)
|
||||
add_subdirectory(vendor/glad)
|
||||
add_subdirectory(vendor/yaml-cpp)
|
||||
add_subdirectory(src)
|
41
README.md
Normal file
41
README.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Daqromancy
|
||||
Daqromancy is a data aquisition application designed for use with CAEN DPP digitizers using the CAENDigitizer library. Daqromancy is focused on providing a clean, powerful interface to the digitizers in an open source,
|
||||
(hopefully) friendly environment.
|
||||
|
||||
Daqromancy does not provide online data analysis/visulization short of a simple osciloscope and data rates. Daqromancy does provide a data server which can be used to pipe data to an external analysis program.
|
||||
|
||||
## Installation
|
||||
Currently, Daqromancy only supports installing from source.
|
||||
|
||||
To download the Daqromancy source retrieve the repository using `git clone --recursive https://github.com/gwm17/Daqromancy.git`. Move into the repostiory and follow standard CMake build instructions. For example,
|
||||
on a Linux system use the following commands:
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j 4
|
||||
```
|
||||
|
||||
This will build and install the Daqromancy executable in the `bin` directory of the repository.
|
||||
|
||||
## About
|
||||
This project started as an extension of the [BoxScore](https://github.com/goluckyryan/BoxScore) application framework, developed by @goluckyryan. As it grew, it became clear that the extension had become it's own
|
||||
monster. This monster is known as Daqromancy!
|
||||
|
||||
First and foremost, Daqromancy was an opprotunity for me to learn more about how a DAQ works and how to design a project of this scope and complexity. So it should be noted that Daqromancy is a learning process,
|
||||
and will most likely undergo many changes and refactors before it is ready to do anything serious.
|
||||
|
||||
Daqromancy aims to provide a cross-platform DAQ that is both readable and transparent without sacrificing performance.
|
||||
|
||||
## Dependencies
|
||||
Daqromancy requires the [CAENDigitizer](https://www.caen.it) library to be installed (this is the middelware that allows for digitizer communication).
|
||||
The CAENDigitizer library only supports Linux and Windows at this time; as such Daqromancy only targets Windows and Linux.
|
||||
|
||||
On Windows, it is recommended to use the Visual Studio environment to build the project.
|
||||
|
||||
On Linux, Daqromancy needs the development tools for the X system. On Ubuntu this can be installed using:
|
||||
`sudo apt install libgl1 libgl1-mesa-dev libglu1-mesa libglu1-mesa-dev xorg-dev mesa-utils`
|
||||
|
||||
## Warning
|
||||
Daqromancy is under very heavy development still, and as such should not be used in any production capacity at this time
|
130
src/CMakeLists.txt
Normal file
130
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,130 @@
|
|||
add_executable(Daqromancy)
|
||||
|
||||
target_include_directories(Daqromancy
|
||||
PUBLIC ../vendor/imgui
|
||||
../vendor/glfw/include
|
||||
../vendor/spdlog/include
|
||||
../vendor/glad/include
|
||||
../vendor/yaml-cpp/include
|
||||
../vendor/implot/
|
||||
../vendor/asio/asio/include
|
||||
./
|
||||
)
|
||||
|
||||
#For Windows, CAENDigitizer is installed non-globally (we assume default pathing cause why would ever be otherwise)
|
||||
if(MSVC)
|
||||
target_include_directories(Daqromancy SYSTEM PUBLIC ${CAEN_WIN_INCL_PATH})
|
||||
endif()
|
||||
|
||||
target_precompile_headers(Daqromancy PRIVATE dypch.h)
|
||||
|
||||
target_sources(Daqromancy PRIVATE
|
||||
DAQ/Digitizer.h
|
||||
DAQ/Digitizer.cpp
|
||||
DAQ/DigitizerDefs.h
|
||||
Core/Logger.h
|
||||
Core/Logger.cpp
|
||||
Core/main.cpp
|
||||
Core/Application.h
|
||||
Core/Application.cpp
|
||||
Events/Event.h
|
||||
Core/DYCore.h
|
||||
Events/AppEvent.h
|
||||
Events/MouseEvent.h
|
||||
Events/AcqEvent.h
|
||||
Core/Layer.h
|
||||
Core/Layer.cpp
|
||||
Core/LayerStack.h
|
||||
Core/LayerStack.cpp
|
||||
Core/Window.h
|
||||
Platform/OpenGL/OpenGLWindow.h
|
||||
Renderer/GraphicsContext.h
|
||||
Platform/OpenGL/OpenGLContext.h
|
||||
Platform/OpenGL/OpenGLContext.cpp
|
||||
Platform/OpenGL/OpenGLWindow.cpp
|
||||
Renderer/RendererAPI.h
|
||||
Renderer/RendererAPI.cpp
|
||||
Renderer/RenderCommand.h
|
||||
Renderer/RenderCommand.cpp
|
||||
Platform/OpenGL/OpenGLRendererAPI.cpp
|
||||
ImGui/ImGuiLayer.h
|
||||
ImGui/ImGuiLayer.cpp
|
||||
ImGui/ImGuiBuild.cpp
|
||||
Editor/EditorLayer.h
|
||||
Editor/EditorLayer.cpp
|
||||
Editor/FileDialog.h
|
||||
Editor/FileDialog.cpp
|
||||
DAQ/AcquisitionLayer.h
|
||||
DAQ/AcquisitionLayer.cpp
|
||||
Core/DYProject.h
|
||||
Core/DYProject.cpp
|
||||
Editor/DigitizerPanel.h
|
||||
Editor/DigitizerPanel.cpp
|
||||
DAQ/DigitizerDefs.cpp
|
||||
Editor/SyncDialog.cpp
|
||||
Core/ProjectSerializer.h
|
||||
Core/ProjectSerializer.cpp
|
||||
Editor/ScopePanel.h
|
||||
Editor/ScopePanel.cpp
|
||||
DYio/DYFile.h
|
||||
DYio/DYFile.cpp
|
||||
DYio/DYRun.h
|
||||
DYio/DYRun.cpp
|
||||
DYio/TCPServer.h
|
||||
DYio/TCPServer.cpp
|
||||
DYio/DYListData.h
|
||||
DYio/DYMessage.h
|
||||
Core/ThreadSafeQueue.h
|
||||
Core/DataDistributor.h
|
||||
Core/DataDistributor.cpp
|
||||
DAQ/DigitizerChain.h
|
||||
DAQ/DigitizerChain.cpp
|
||||
Core/ScalarDistributor.h
|
||||
Core/ScalarDistributor.cpp
|
||||
Editor/ScalarPanel.h
|
||||
Editor/ScalarPanel.cpp
|
||||
)
|
||||
|
||||
#ImPlot sources
|
||||
target_sources(Daqromancy PRIVATE
|
||||
../vendor/implot/implot.cpp
|
||||
../vendor/implot/implot.h
|
||||
../vendor/implot/implot_demo.cpp
|
||||
../vendor/implot/implot_internal.h
|
||||
../vendor/implot/implot_items.cpp
|
||||
../vendor/implot/backends/implot_backend.h
|
||||
../vendor/implot/backends/implot_impl_opengl3.cpp
|
||||
../vendor/implot/backends/implot_impl_opengl3.h
|
||||
)
|
||||
|
||||
#Packaged dependencies
|
||||
target_link_libraries(Daqromancy PRIVATE imgui glfw glad yaml-cpp)
|
||||
|
||||
#CAEN dependencies
|
||||
target_link_libraries(Daqromancy PRIVATE ${CAEN_DGTZ})
|
||||
|
||||
#Link OS graphics implementations
|
||||
set(THREADS_PREFER_PTHREAD_FLAG On)
|
||||
find_package(Threads REQUIRED)
|
||||
if(APPLE)
|
||||
target_link_libraries(Daqromancy PRIVATE "-framework Cocoa" "-framework CoreVideo" "-framework IOKit" "-framework OpenGL" "-framework Carbon" ${CMAKE_DL_LIBS} Threads::Threads)
|
||||
target_compile_definitions(Daqromancy PRIVATE DY_APPLE)
|
||||
elseif(UNIX)
|
||||
target_link_libraries(Daqromancy PRIVATE libGL.so libX11.so ${CMAKE_DL_LIBS} Threads::Threads)
|
||||
target_compile_definitions(Daqromancy PRIVATE DY_LINUX)
|
||||
elseif(MSVC)
|
||||
target_link_libraries(Daqromancy PRIVATE opengl32.lib Threads::Threads)
|
||||
target_compile_definitions(Daqromancy PRIVATE DY_WINDOWS)
|
||||
endif()
|
||||
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
target_compile_definitions(Daqromancy PRIVATE BS_RELEASE)
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions(Daqromancy PRIVATE BS_DEBUG)
|
||||
endif()
|
||||
|
||||
#Some extra defs
|
||||
target_compile_definitions(Daqromancy PRIVATE GLFW_INCLUDE_NONE YAML_CPP_STATIC_DEFINE IMGUI_IMPL_OPENGL_LOADER_GLAD IMPLOT_BACKEND_ENABLE_OPENGL3)
|
||||
|
||||
set_target_properties(Daqromancy PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${DY_BINARY_DIR})
|
118
src/Core/Application.cpp
Normal file
118
src/Core/Application.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include "Application.h"
|
||||
#include "Renderer/RenderCommand.h"
|
||||
#include "Editor/EditorLayer.h"
|
||||
#include "DAQ/AcquisitionLayer.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
Application* Application::s_instance = nullptr;
|
||||
|
||||
Application* CreateApplication(const ApplicationArgs& args) { return new Application(args); }
|
||||
|
||||
Application::Application(const ApplicationArgs& args) :
|
||||
m_args(args), m_running(true)
|
||||
{
|
||||
if(s_instance != nullptr)
|
||||
{
|
||||
DY_ERROR("Attempting to create more than one application! CreateApplication should only be called once!");
|
||||
}
|
||||
|
||||
s_instance = this;
|
||||
|
||||
DY_INFO("Application created.");
|
||||
|
||||
//Set current path to find assets
|
||||
if(!m_args.runtimePath.empty())
|
||||
std::filesystem::current_path(m_args.runtimePath);
|
||||
|
||||
DY_INFO("Runtime path set to {0}", std::filesystem::current_path().string());
|
||||
|
||||
m_window = std::unique_ptr<Window>(Window::Create({ m_args.name, 1280, 720 }));
|
||||
m_window->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent));
|
||||
|
||||
m_imguiLayer = new ImGuiLayer();
|
||||
PushOverlay(m_imguiLayer);
|
||||
|
||||
m_project = std::make_shared<DYProject>();
|
||||
|
||||
EditorLayer* editor = new EditorLayer(m_project);
|
||||
editor->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent));
|
||||
PushLayer(editor);
|
||||
|
||||
AcquisitionLayer* acq = new AcquisitionLayer(m_project);
|
||||
acq->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent));
|
||||
PushLayer(acq);
|
||||
|
||||
}
|
||||
|
||||
Application::~Application() {}
|
||||
|
||||
void Application::OnEvent(Event& e)
|
||||
{
|
||||
EventDispatcher dispatch(e);
|
||||
dispatch.Dispatch<WindowCloseEvent>(BIND_EVENT_FUNCTION(Application::OnWindowCloseEvent));
|
||||
|
||||
for (auto iter = m_layerStack.rbegin(); iter != m_layerStack.rend(); iter++)
|
||||
{
|
||||
if (e.handled)
|
||||
break;
|
||||
|
||||
(*iter)->OnEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::PushLayer(Layer* layer)
|
||||
{
|
||||
m_layerStack.PushLayer(layer);
|
||||
layer->OnAttach();
|
||||
}
|
||||
|
||||
void Application::PushOverlay(Layer* layer)
|
||||
{
|
||||
m_layerStack.PushOverlay(layer);
|
||||
layer->OnAttach();
|
||||
}
|
||||
|
||||
bool Application::OnWindowCloseEvent(WindowCloseEvent& e)
|
||||
{
|
||||
m_running = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::Run()
|
||||
{
|
||||
if (!m_running)
|
||||
DY_ERROR("Application attempting to run after having already exited");
|
||||
|
||||
float bckgndColor[4] = { 0.1, 0.1, 0.1, 1.0 };
|
||||
|
||||
//Time stuff
|
||||
double currentTime = 0.0;
|
||||
double prevTime = 0.0;
|
||||
double timestep;
|
||||
|
||||
while (m_running)
|
||||
{
|
||||
RenderCommand::SetClearColor(bckgndColor);
|
||||
RenderCommand::Clear();
|
||||
|
||||
currentTime = RenderCommand::GetFrameTime();
|
||||
timestep = currentTime - prevTime;
|
||||
prevTime = currentTime;
|
||||
|
||||
for (auto& layer : m_layerStack)
|
||||
layer->OnUpdate();
|
||||
|
||||
m_imguiLayer->Begin();
|
||||
for (auto& layer : m_layerStack)
|
||||
layer->OnImGuiRender(timestep);
|
||||
m_imguiLayer->End();
|
||||
|
||||
m_window->OnUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
56
src/Core/Application.h
Normal file
56
src/Core/Application.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef APPLICATION_H
|
||||
#define APPLICATION_H
|
||||
|
||||
#include "Events/Event.h"
|
||||
#include "Events/AppEvent.h"
|
||||
#include "LayerStack.h"
|
||||
#include "Window.h"
|
||||
#include "ImGui/ImGuiLayer.h"
|
||||
#include "DYProject.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
struct ApplicationArgs
|
||||
{
|
||||
std::string name;
|
||||
std::string runtimePath;
|
||||
int argc;
|
||||
char** argv;
|
||||
};
|
||||
|
||||
class Application
|
||||
{
|
||||
public:
|
||||
Application(const ApplicationArgs& args);
|
||||
~Application();
|
||||
|
||||
void Run();
|
||||
void Close() { m_running = false; }
|
||||
void OnEvent(Event& e);
|
||||
void PushLayer(Layer* layer);
|
||||
void PushOverlay(Layer* layer);
|
||||
|
||||
//App Event Callbacks
|
||||
bool OnWindowCloseEvent(WindowCloseEvent& e);
|
||||
|
||||
const ApplicationArgs& GetArgs() { return m_args; }
|
||||
Window& GetWindow() { return *m_window; }
|
||||
static Application& GetInstance() { return *s_instance; }
|
||||
private:
|
||||
ApplicationArgs m_args;
|
||||
bool m_running;
|
||||
|
||||
std::unique_ptr<Window> m_window;
|
||||
LayerStack m_layerStack;
|
||||
|
||||
ImGuiLayer* m_imguiLayer;
|
||||
|
||||
DYProject::Ref m_project;
|
||||
|
||||
static Application* s_instance;
|
||||
};
|
||||
|
||||
Application* CreateApplication(const ApplicationArgs& args);
|
||||
}
|
||||
|
||||
#endif
|
26
src/Core/DYCore.h
Normal file
26
src/Core/DYCore.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef DY_CORE_H
|
||||
#define DY_CORE_H
|
||||
|
||||
#ifdef BS_WINDOWS
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4127) // condition expression is constant
|
||||
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when NAV_API is set to__declspec(dllexport)
|
||||
#pragma warning (disable: 4091) // '__declspec(dllimport)': ignored on left of class 'xxx' when no variable is declared
|
||||
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
|
||||
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
|
||||
#endif
|
||||
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
|
||||
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
|
||||
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//Bit field setter
|
||||
#define BIT(x) (1<<x)
|
||||
|
||||
//Macro to bind a function using lambda expressions
|
||||
#define BIND_EVENT_FUNCTION(x) [this](auto&&... args) -> decltype(auto) { return this->x(std::forward<decltype(args)>(args)...); }
|
||||
|
||||
#endif
|
231
src/Core/DYProject.cpp
Normal file
231
src/Core/DYProject.cpp
Normal file
|
@ -0,0 +1,231 @@
|
|||
#include "DYProject.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
DYProject::DYProject() :
|
||||
m_runNumber(0), m_dppMode(DPPAcqMode::List)
|
||||
{
|
||||
}
|
||||
|
||||
DYProject::~DYProject() {}
|
||||
|
||||
void DYProject::InternalClear()
|
||||
{
|
||||
m_argList.clear();
|
||||
m_boardParamList.clear();
|
||||
m_psdChannelMap.clear();
|
||||
m_phaChannelMap.clear();
|
||||
m_psdWaveMap.clear();
|
||||
m_phaWaveMap.clear();
|
||||
}
|
||||
|
||||
bool DYProject::SetProjectPath(const std::filesystem::path& path)
|
||||
{
|
||||
bool status = true;
|
||||
m_projectPath = path.lexically_normal();
|
||||
if (!std::filesystem::exists(m_projectPath))
|
||||
{
|
||||
status = std::filesystem::create_directory(path);
|
||||
if (!status)
|
||||
m_projectPath.clear();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//void DYProject::SetDigitizerData(const std::vector<Digitizer::Ref>& chain)
|
||||
void DYProject::SetDigitizerData(const DigitizerChain& chain)
|
||||
{
|
||||
InternalClear();
|
||||
|
||||
//buffers
|
||||
std::vector<PSDParameters> psdChannels;
|
||||
std::vector<PHAParameters> phaChannels;
|
||||
PSDWaveParameters psdWaves;
|
||||
PHAWaveParameters phaWaves;
|
||||
|
||||
for (auto& digitizer : chain)
|
||||
{
|
||||
auto& args = digitizer->GetDigitizerArgs();
|
||||
m_argList.push_back(args);
|
||||
m_boardParamList.push_back(digitizer->GetDigitizerParameters());
|
||||
if (args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
|
||||
{
|
||||
digitizer->GetChannelParameters(phaChannels);
|
||||
digitizer->GetWaveformParameters(phaWaves);
|
||||
m_phaChannelMap[args.handle] = phaChannels;
|
||||
m_phaWaveMap[args.handle] = phaWaves;
|
||||
}
|
||||
else if(args.firmware == CAEN_DGTZ_DPPFirmware_PSD)
|
||||
{
|
||||
digitizer->GetChannelParameters(psdChannels);
|
||||
digitizer->GetWaveformParameters(psdWaves);
|
||||
m_psdChannelMap[args.handle] = psdChannels;
|
||||
m_psdWaveMap[args.handle] = psdWaves;
|
||||
}
|
||||
else
|
||||
{
|
||||
DY_ERROR("Invalid digitizer firmware detected at DYProject::SetDigitizerData!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DYProject::SetDigitizerArgsList(const std::vector<DigitizerArgs>& args)
|
||||
{
|
||||
m_argList = args;
|
||||
}
|
||||
|
||||
void DYProject::SetDigitizerParameterList(const std::vector<DigitizerParameters>& params)
|
||||
{
|
||||
m_boardParamList = params;
|
||||
}
|
||||
|
||||
void DYProject::SetDigitizerParameters(int handle, const DigitizerParameters& params)
|
||||
{
|
||||
if (handle >= m_boardParamList.size() || handle == -1)
|
||||
{
|
||||
DY_ERROR("Attempting to set digitizer parameters for non-extant board: given handle {0}, number of boards {1}", handle, m_boardParamList.size());
|
||||
return;
|
||||
}
|
||||
|
||||
m_boardParamList[handle] = params;
|
||||
}
|
||||
|
||||
void DYProject::SetPHAParameters(int handle, const std::vector<PHAParameters>& params)
|
||||
{
|
||||
m_phaChannelMap[handle] = params;
|
||||
}
|
||||
|
||||
void DYProject::SetPSDParameters(int handle, const std::vector<PSDParameters>& params)
|
||||
{
|
||||
m_psdChannelMap[handle] = params;
|
||||
}
|
||||
|
||||
void DYProject::SetPHAWaveParameters(int handle, const PHAWaveParameters& params)
|
||||
{
|
||||
m_phaWaveMap[handle] = params;
|
||||
}
|
||||
|
||||
void DYProject::SetPSDWaveParameters(int handle, const PSDWaveParameters& params)
|
||||
{
|
||||
m_psdWaveMap[handle] = params;
|
||||
}
|
||||
|
||||
void DYProject::SetRunNumber(uint32_t number)
|
||||
{
|
||||
m_runNumber = number;
|
||||
}
|
||||
|
||||
void DYProject::IncrementRunNumber()
|
||||
{
|
||||
++m_runNumber;
|
||||
}
|
||||
|
||||
void DYProject::SetDPPAcqMode(DPPAcqMode mode)
|
||||
{
|
||||
m_dppMode = mode;
|
||||
}
|
||||
|
||||
const std::filesystem::path& DYProject::GetProjectPath()
|
||||
{
|
||||
return m_projectPath;
|
||||
}
|
||||
|
||||
const std::vector<DigitizerArgs>& DYProject::GetDigitizerArgsList()
|
||||
{
|
||||
return m_argList;
|
||||
}
|
||||
|
||||
DigitizerArgs DYProject::GetDigitizerArgs(int handle)
|
||||
{
|
||||
if (handle >= m_argList.size() || handle == -1)
|
||||
{
|
||||
DY_ERROR("Attempting to get digitizer args for non-extant board: given handle {0}, number of boards {1}", handle, m_argList.size());
|
||||
return DigitizerArgs();
|
||||
}
|
||||
return m_argList[handle];
|
||||
}
|
||||
|
||||
const std::vector<DigitizerParameters>& DYProject::GetDigitizerParameterList()
|
||||
{
|
||||
return m_boardParamList;
|
||||
}
|
||||
|
||||
DigitizerParameters DYProject::GetDigitizerParameters(int handle)
|
||||
{
|
||||
if (handle >= m_boardParamList.size() || handle == -1)
|
||||
{
|
||||
DY_ERROR("Attempting to get digitizer parameters for non-extant board: given handle {0}, number of boards {1}", handle, m_boardParamList.size());
|
||||
return DigitizerParameters();
|
||||
}
|
||||
return m_boardParamList[handle];
|
||||
}
|
||||
|
||||
const std::vector<PHAParameters>& DYProject::GetPHAParameters(int handle)
|
||||
{
|
||||
auto iter = m_phaChannelMap.find(handle);
|
||||
if (iter != m_phaChannelMap.end())
|
||||
return iter->second;
|
||||
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
|
||||
return m_nullphaChannels;
|
||||
}
|
||||
|
||||
const std::vector<PSDParameters>& DYProject::GetPSDParameters(int handle)
|
||||
{
|
||||
auto iter = m_psdChannelMap.find(handle);
|
||||
if (iter != m_psdChannelMap.end())
|
||||
return iter->second;
|
||||
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
|
||||
return m_nullpsdChannels;
|
||||
}
|
||||
|
||||
PHAWaveParameters DYProject::GetPHAWaveParameters(int handle)
|
||||
{
|
||||
auto iter = m_phaWaveMap.find(handle);
|
||||
if (iter != m_phaWaveMap.end())
|
||||
return iter->second;
|
||||
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
|
||||
return PHAWaveParameters();
|
||||
}
|
||||
|
||||
PSDWaveParameters DYProject::GetPSDWaveParameters(int handle)
|
||||
{
|
||||
auto iter = m_psdWaveMap.find(handle);
|
||||
if (iter != m_psdWaveMap.end())
|
||||
return iter->second;
|
||||
DY_ERROR("Invalid handle {0} given to DYProject::GetPHAParameters!", handle);
|
||||
return PSDWaveParameters();
|
||||
}
|
||||
|
||||
uint32_t DYProject::GetRunNumber()
|
||||
{
|
||||
return m_runNumber;
|
||||
}
|
||||
|
||||
DPPAcqMode DYProject::GetDPPAcqMode()
|
||||
{
|
||||
return m_dppMode;
|
||||
}
|
||||
|
||||
size_t DYProject::GetNumberOfBoards()
|
||||
{
|
||||
return m_argList.size();
|
||||
}
|
||||
|
||||
|
||||
std::filesystem::path DYProject::CreateRunDirectory()
|
||||
{
|
||||
std::string runName = "run_" + std::to_string(m_runNumber);
|
||||
std::filesystem::path runPath = m_projectPath / runName;
|
||||
|
||||
if (std::filesystem::exists(runPath))
|
||||
{
|
||||
DY_WARN("Run directory {0} already exists! Could lead to overwritting data!", runPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::filesystem::create_directory(runPath);
|
||||
}
|
||||
|
||||
return runPath;
|
||||
}
|
||||
}
|
74
src/Core/DYProject.h
Normal file
74
src/Core/DYProject.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef DY_PROJECT_H
|
||||
#define DY_PROJECT_H
|
||||
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
#include "DAQ/Digitizer.h"
|
||||
#include "DAQ/DigitizerChain.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class DYProject
|
||||
{
|
||||
public:
|
||||
using Ref = std::shared_ptr<DYProject>;
|
||||
|
||||
DYProject();
|
||||
~DYProject();
|
||||
|
||||
bool SetProjectPath(const std::filesystem::path& path);
|
||||
//void SetDigitizerData(const std::vector<Digitizer::Ref>& chain);
|
||||
void SetDigitizerData(const DigitizerChain& chain);
|
||||
|
||||
void SetDigitizerArgsList(const std::vector<DigitizerArgs>& args);
|
||||
void SetDigitizerParameterList(const std::vector<DigitizerParameters>& params);
|
||||
void SetDigitizerParameters(int handle, const DigitizerParameters& params);
|
||||
|
||||
void SetPHAParameters(int handle, const std::vector<PHAParameters>& params);
|
||||
void SetPSDParameters(int handle, const std::vector<PSDParameters>& params);
|
||||
void SetPHAWaveParameters(int handle, const PHAWaveParameters& params);
|
||||
void SetPSDWaveParameters(int handle, const PSDWaveParameters& params);
|
||||
|
||||
void SetRunNumber(uint32_t number);
|
||||
void IncrementRunNumber();
|
||||
void SetDPPAcqMode(DPPAcqMode mode);
|
||||
|
||||
const std::filesystem::path& GetProjectPath();
|
||||
|
||||
const std::vector<DigitizerArgs>& GetDigitizerArgsList();
|
||||
DigitizerArgs GetDigitizerArgs(int handle);
|
||||
const std::vector<DigitizerParameters>& GetDigitizerParameterList();
|
||||
DigitizerParameters GetDigitizerParameters(int handle);
|
||||
|
||||
const std::vector<PHAParameters>& GetPHAParameters(int handle);
|
||||
const std::vector<PSDParameters>& GetPSDParameters(int handle);
|
||||
PHAWaveParameters GetPHAWaveParameters(int handle);
|
||||
PSDWaveParameters GetPSDWaveParameters(int handle);
|
||||
|
||||
uint32_t GetRunNumber();
|
||||
DPPAcqMode GetDPPAcqMode();
|
||||
size_t GetNumberOfBoards();
|
||||
|
||||
std::filesystem::path CreateRunDirectory();
|
||||
|
||||
private:
|
||||
void InternalClear();
|
||||
|
||||
std::vector<DigitizerArgs> m_argList;
|
||||
std::vector<DigitizerParameters> m_boardParamList;
|
||||
std::unordered_map<int, std::vector<PHAParameters>> m_phaChannelMap;
|
||||
std::unordered_map<int, std::vector<PSDParameters>> m_psdChannelMap;
|
||||
std::unordered_map<int, PHAWaveParameters> m_phaWaveMap;
|
||||
std::unordered_map<int, PSDWaveParameters> m_psdWaveMap;
|
||||
std::filesystem::path m_projectPath;
|
||||
uint32_t m_runNumber;
|
||||
DPPAcqMode m_dppMode;
|
||||
|
||||
//some null result stuff
|
||||
std::vector<PHAParameters> m_nullphaChannels;
|
||||
std::vector<PSDParameters> m_nullpsdChannels;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
31
src/Core/DataDistributor.cpp
Normal file
31
src/Core/DataDistributor.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "DataDistributor.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
uint64_t DataDistributor::s_nextClientID = 0;
|
||||
std::unordered_map<uint64_t, ThreadSafeQueue<std::vector<DYData>>*> DataDistributor::s_clientMap;
|
||||
|
||||
void DataDistributor::PushData(const std::vector<DYData>& data)
|
||||
{
|
||||
for (auto& iter : s_clientMap)
|
||||
iter.second->PushBack(data);
|
||||
}
|
||||
|
||||
DistributorClient DataDistributor::Connect()
|
||||
{
|
||||
DistributorClient client;
|
||||
client.id = s_nextClientID++;
|
||||
|
||||
client.dataQueue = new ThreadSafeQueue<std::vector<DYData>>();
|
||||
s_clientMap[client.id] = client.dataQueue;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void DataDistributor::Disconnect(DistributorClient& client)
|
||||
{
|
||||
s_clientMap.erase(client.id);
|
||||
delete client.dataQueue;
|
||||
client.dataQueue = nullptr;
|
||||
}
|
||||
}
|
41
src/Core/DataDistributor.h
Normal file
41
src/Core/DataDistributor.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
DataDistributor.h
|
||||
Thread-safe distribution of data to multpile clients. Each client must register using the Connect() function, and de-register using Disconnect().
|
||||
A client is given a DistributorClient struct which contains the id for that client as well as a raw pointer to the dataQueue allocated for that client.
|
||||
Use raw pointers to emphasize that memory is not automatically managed, and the Connect()/Disconnect() idiom MUST be used.
|
||||
|
||||
This is a singleton, there must always be one and only one DataDistributor
|
||||
|
||||
DataDistributor itself is not thread-safe. All connect/disconnect calls should be done from the main application thread. PushData / queue accesss is thread safe,
|
||||
as the queues are internally thread-safe (as the name implies)
|
||||
*/
|
||||
#ifndef DATA_DISTRIBUTOR_H
|
||||
#define DATA_DISTRIBUTOR_H
|
||||
|
||||
#include "ThreadSafeQueue.h"
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
struct DistributorClient
|
||||
{
|
||||
uint64_t id = -1; //max val, illegal value
|
||||
ThreadSafeQueue<std::vector<DYData>>* dataQueue = nullptr;
|
||||
};
|
||||
|
||||
class DataDistributor
|
||||
{
|
||||
public:
|
||||
|
||||
static void PushData(const std::vector<DYData>& data);
|
||||
|
||||
static DistributorClient Connect();
|
||||
static void Disconnect(DistributorClient& client);
|
||||
|
||||
private:
|
||||
static std::unordered_map<uint64_t, ThreadSafeQueue<std::vector<DYData>>*> s_clientMap;
|
||||
static uint64_t s_nextClientID;
|
||||
};
|
||||
}
|
||||
#endif
|
11
src/Core/Layer.cpp
Normal file
11
src/Core/Layer.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "Layer.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
Layer::Layer(const std::string& name) :
|
||||
m_name(name)
|
||||
{
|
||||
}
|
||||
|
||||
Layer::~Layer() {}
|
||||
}
|
27
src/Core/Layer.h
Normal file
27
src/Core/Layer.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef LAYER_H
|
||||
#define LAYER_H
|
||||
|
||||
#include "Events/Event.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class Layer
|
||||
{
|
||||
public:
|
||||
Layer(const std::string& name = "Layer");
|
||||
virtual ~Layer();
|
||||
|
||||
virtual void OnAttach() {};
|
||||
virtual void OnDetach() {};
|
||||
virtual void OnUpdate() {};
|
||||
virtual void OnEvent(Event& e) {};
|
||||
virtual void OnImGuiRender(double timestep) {};
|
||||
|
||||
const std::string& GetName() { return m_name; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
42
src/Core/LayerStack.cpp
Normal file
42
src/Core/LayerStack.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "LayerStack.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
LayerStack::LayerStack() {}
|
||||
|
||||
LayerStack::~LayerStack()
|
||||
{
|
||||
for (Layer* layer : m_stack)
|
||||
delete layer;
|
||||
}
|
||||
|
||||
void LayerStack::PushLayer(Layer* layer)
|
||||
{
|
||||
m_stack.emplace(m_stack.begin() + m_insertIndex, layer);
|
||||
m_insertIndex++;
|
||||
}
|
||||
|
||||
void LayerStack::PushOverlay(Layer* layer)
|
||||
{
|
||||
m_stack.emplace_back(layer);
|
||||
}
|
||||
|
||||
void LayerStack::PopLayer(Layer* layer)
|
||||
{
|
||||
auto iter = std::find(m_stack.begin(), m_stack.end(), layer);
|
||||
if (iter != m_stack.end())
|
||||
{
|
||||
m_stack.erase(iter);
|
||||
m_insertIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
void LayerStack::PopOverlay(Layer* layer)
|
||||
{
|
||||
auto iter = std::find(m_stack.begin(), m_stack.end(), layer);
|
||||
if (iter != m_stack.end())
|
||||
{
|
||||
m_stack.erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
30
src/Core/LayerStack.h
Normal file
30
src/Core/LayerStack.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef LAYER_STACK_H
|
||||
#define LAYER_STACK_H
|
||||
|
||||
#include "Layer.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class LayerStack
|
||||
{
|
||||
public:
|
||||
LayerStack();
|
||||
~LayerStack();
|
||||
|
||||
void PushLayer(Layer* layer);
|
||||
void PopLayer(Layer* layer);
|
||||
|
||||
void PushOverlay(Layer* layer);
|
||||
void PopOverlay(Layer* layer);
|
||||
|
||||
std::vector<Layer*>::iterator begin() { return m_stack.begin(); }
|
||||
std::vector<Layer*>::iterator end() { return m_stack.end(); }
|
||||
std::vector<Layer*>::reverse_iterator rbegin() { return m_stack.rbegin(); }
|
||||
std::vector<Layer*>::reverse_iterator rend() { return m_stack.rend(); }
|
||||
private:
|
||||
std::vector<Layer*> m_stack;
|
||||
size_t m_insertIndex = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
14
src/Core/Logger.cpp
Normal file
14
src/Core/Logger.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "Logger.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
std::shared_ptr<spdlog::logger> Logger::s_logger;
|
||||
|
||||
void Logger::Init()
|
||||
{
|
||||
spdlog::set_pattern("%^[%T] %n: %v%$");
|
||||
s_logger = spdlog::stdout_color_mt("BXSC");
|
||||
s_logger->set_level(spdlog::level::trace);
|
||||
}
|
||||
}
|
30
src/Core/Logger.h
Normal file
30
src/Core/Logger.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/fmt/ostr.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
static std::shared_ptr<spdlog::logger> GetLogger() { return s_logger; }
|
||||
|
||||
private:
|
||||
static std::shared_ptr<spdlog::logger> s_logger;
|
||||
};
|
||||
|
||||
|
||||
//Macros for simple logging.
|
||||
|
||||
#define DY_CRITICAL(...) ::Daqromancy::Logger::GetLogger()->critical(__VA_ARGS__)
|
||||
#define DY_WARN(...) ::Daqromancy::Logger::GetLogger()->warn(__VA_ARGS__)
|
||||
#define DY_INFO(...) ::Daqromancy::Logger::GetLogger()->info(__VA_ARGS__)
|
||||
#define DY_TRACE(...) ::Daqromancy::Logger::GetLogger()->trace(__VA_ARGS__)
|
||||
#define DY_ERROR(...) ::Daqromancy::Logger::GetLogger()->error(__VA_ARGS__)
|
||||
}
|
||||
|
||||
#endif
|
555
src/Core/ProjectSerializer.cpp
Normal file
555
src/Core/ProjectSerializer.cpp
Normal file
|
@ -0,0 +1,555 @@
|
|||
#include "ProjectSerializer.h"
|
||||
|
||||
#include <fstream>
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
|
||||
namespace YAML {
|
||||
|
||||
template<>
|
||||
struct convert<Daqromancy::DigitizerArgs>
|
||||
{
|
||||
static Node encode(const Daqromancy::DigitizerArgs& args)
|
||||
{
|
||||
Node node(NodeType::Map);
|
||||
node.force_insert<std::string, int>("ConnectionType", args.type);
|
||||
node.force_insert<std::string, int>("LinkNumber", args.linkNumber);
|
||||
node.force_insert<std::string, int>("CONETNode", args.conetNode);
|
||||
node.force_insert<std::string, uint32_t>("VMEAddress", args.vmeAddress);
|
||||
node.force_insert<std::string, int>("Handle", args.handle);
|
||||
node.force_insert<std::string, int>("Model", args.model);
|
||||
node.force_insert<std::string, int>("Firmware", args.firmware);
|
||||
node.force_insert<std::string, int>("Channels", args.channels);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, Daqromancy::DigitizerArgs& args)
|
||||
{
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
|
||||
args.type = (CAEN_DGTZ_ConnectionType) node["ConnectionType"].as<int>();
|
||||
args.linkNumber = node["LinkNumber"].as<int>();
|
||||
args.conetNode = node["ConetNode"].as<int>();
|
||||
args.vmeAddress = node["VMEAddress"].as<uint32_t>();
|
||||
args.handle = node["Handle"].as<int>();
|
||||
args.model = (CAEN_DGTZ_BoardModel_t) node["Model"].as<int>();
|
||||
args.firmware = (CAEN_DGTZ_DPPFirmware_t) node["Firmware"].as<int>();
|
||||
args.channels = node["Channels"].as<int>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert<Daqromancy::DigitizerParameters>
|
||||
{
|
||||
static Node encode(const Daqromancy::DigitizerParameters& params)
|
||||
{
|
||||
Node node(NodeType::Map);
|
||||
node.force_insert<std::string, int>("RecordLength", params.recordLength);
|
||||
node.force_insert<std::string, uint32_t>("ChannelMask", params.channelMask);
|
||||
node.force_insert<std::string, int>("EventAggr", params.eventAggr);
|
||||
node.force_insert<std::string, int>("AcqMode", params.acqMode);
|
||||
node.force_insert<std::string, int>("DPPAcqMode", params.dppAcqMode);
|
||||
node.force_insert<std::string, int>("DPPSaveMode", params.dppSaveMode);
|
||||
node.force_insert<std::string, int>("IOLevel", params.IOlevel);
|
||||
node.force_insert<std::string, int>("TriggerMode", params.triggerMode);
|
||||
node.force_insert<std::string, int>("SyncMode", params.syncMode);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, Daqromancy::DigitizerParameters& params)
|
||||
{
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
|
||||
params.recordLength = node["RecordLength"].as<uint32_t>();
|
||||
params.channelMask = node["ChannelMask"].as<uint32_t>();
|
||||
params.eventAggr = node["EventAggr"].as<int>();
|
||||
params.acqMode = (CAEN_DGTZ_AcqMode_t)node["AcqMode"].as<int>();
|
||||
params.dppAcqMode = (CAEN_DGTZ_DPP_AcqMode_t)node["DPPAcqMode"].as<int>();
|
||||
params.dppSaveMode = (CAEN_DGTZ_DPP_SaveParam_t)node["DPPSaveMode"].as<int>();
|
||||
params.IOlevel = (CAEN_DGTZ_IOLevel_t)node["IOLevel"].as<int>();
|
||||
params.triggerMode = (CAEN_DGTZ_TriggerMode_t)node["TriggerMode"].as<int>();
|
||||
params.syncMode = (CAEN_DGTZ_RunSyncMode_t)node["SyncMode"].as<int>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert<Daqromancy::PHAParameters>
|
||||
{
|
||||
static Node encode(const Daqromancy::PHAParameters& params)
|
||||
{
|
||||
Node node(NodeType::Map);
|
||||
node.force_insert<std::string, bool>("IsEnabled", params.isEnabled);
|
||||
node.force_insert<std::string, uint32_t>("PreTrigger", params.preTriggerTime);
|
||||
node.force_insert<std::string, float>("DCOffset", params.dcOffset);
|
||||
node.force_insert<std::string, int>("PulsePolarity", params.pulsePolarity);
|
||||
node.force_insert<std::string, int>("DynamicRange", params.dynamicRange);
|
||||
node.force_insert<std::string, int>("DecayTimeConst", params.decayTimeConst);
|
||||
node.force_insert<std::string, int>("TrapFlatTop", params.trapFlatTop);
|
||||
node.force_insert<std::string, int>("TrapRiseTime", params.trapRiseTime);
|
||||
node.force_insert<std::string, int>("FlatTopDelay", params.flatTopDelay);
|
||||
node.force_insert<std::string, int>("TriggerSmoothing", params.triggerFilterSmoothing);
|
||||
node.force_insert<std::string, int>("InputRiseTime", params.inputRiseTime);
|
||||
node.force_insert<std::string, int>("TriggerThreshold", params.triggerThreshold);
|
||||
node.force_insert<std::string, int>("SamplesBaseline", params.samplesBaseLineMean);
|
||||
node.force_insert<std::string, int>("SamplesPeak", params.samplesPeakMean);
|
||||
node.force_insert<std::string, int>("PeakHoldoff", params.peakHoldOff);
|
||||
node.force_insert<std::string, int>("BaselineHoldoff", params.baseLineHoldOff);
|
||||
node.force_insert<std::string, int>("TriggerHoldoff", params.triggerHoldOff);
|
||||
node.force_insert<std::string, int>("RiseTimeWindow", params.riseTimeValidationWindow);
|
||||
node.force_insert<std::string, int>("RiseTimeDiscrimination", params.riseTimeDiscrimination);
|
||||
node.force_insert<std::string, int>("DigitalProbeGain", params.digitalProbeGain);
|
||||
node.force_insert<std::string, int>("EnergyNormFactor", params.energyNormalizationFactor);
|
||||
node.force_insert<std::string, int>("InputDecimation", params.inputDecimation);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, Daqromancy::PHAParameters& params)
|
||||
{
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
|
||||
params.isEnabled = node["IsEnabled"].as<bool>();
|
||||
params.preTriggerTime = node["PreTrigger"].as<uint32_t>();
|
||||
params.dcOffset = node["DCOffset"].as<float>();
|
||||
params.pulsePolarity = (CAEN_DGTZ_PulsePolarity_t)node["PulsePolarity"].as<int>();
|
||||
params.dynamicRange = (Daqromancy::DynamicRange)node["DynamicRange"].as<int>();
|
||||
params.decayTimeConst = node["DecayTimeConst"].as<int>();
|
||||
params.trapFlatTop = node["TrapFlatTop"].as<int>();
|
||||
params.trapRiseTime = node["TrapRiseTime"].as<int>();
|
||||
params.flatTopDelay = node["FlatTopDelay"].as<int>();
|
||||
params.triggerFilterSmoothing = node["TriggerSmoothing"].as<int>();
|
||||
params.inputRiseTime = node["InputRiseTime"].as<int>();
|
||||
params.triggerThreshold = node["TriggerThreshold"].as<int>();
|
||||
params.samplesBaseLineMean = node["SamplesBaseline"].as<int>();
|
||||
params.samplesPeakMean = node["SamplesPeak"].as<int>();
|
||||
params.peakHoldOff = node["PeakHoldoff"].as<int>();
|
||||
params.baseLineHoldOff = node["BaselineHoldoff"].as<int>();
|
||||
params.triggerHoldOff = node["TriggerHoldoff"].as<int>();
|
||||
params.riseTimeValidationWindow = node["RiseTimeWindow"].as<int>();
|
||||
params.riseTimeDiscrimination = node["RiseTimeDiscrimination"].as<int>();
|
||||
params.digitalProbeGain = node["DigitalProbeGain"].as<int>();
|
||||
params.energyNormalizationFactor = node["EnergyNormFactor"].as<int>();
|
||||
params.inputDecimation = node["InputDecimation"].as<int>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert<Daqromancy::PSDParameters>
|
||||
{
|
||||
static Node encode(const Daqromancy::PSDParameters& params)
|
||||
{
|
||||
Node node(NodeType::Map);
|
||||
|
||||
node.force_insert<std::string, bool>("IsEnabled", params.isEnabled);
|
||||
node.force_insert<std::string, uint32_t>("PreTrigger", params.preTriggerTime);
|
||||
node.force_insert<std::string, float>("DCOffset", params.dcOffset);
|
||||
node.force_insert<std::string, int>("PulsePolarity", params.pulsePolarity);
|
||||
node.force_insert<std::string, int>("DynamicRange", params.dynamicRange);
|
||||
node.force_insert<std::string, int>("BaselineThreshold", params.baselineThreshold);
|
||||
node.force_insert<std::string, int>("TriggerHoldoff", params.triggerHoldOff);
|
||||
node.force_insert<std::string, int>("TriggerThreshold", params.triggerThreshold);
|
||||
node.force_insert<std::string, int>("SelfTrigger", params.selfTrigger);
|
||||
node.force_insert<std::string, int>("ChargeSensitivity", params.chargeSensitivity);
|
||||
node.force_insert<std::string, int>("ShortGate", params.shortGate);
|
||||
node.force_insert<std::string, int>("LongGate", params.longGate);
|
||||
node.force_insert<std::string, int>("PreGate", params.preGate);
|
||||
node.force_insert<std::string, int>("TriggerWindow", params.triggerValidationWindow);
|
||||
node.force_insert<std::string, int>("BaselineSamples", params.samplesBasline);
|
||||
node.force_insert<std::string, int>("DiscriminatorType", params.discrminatorType);
|
||||
node.force_insert<std::string, int>("CFDFraction", params.cfdFraction);
|
||||
node.force_insert<std::string, int>("CFDDelay", params.cfdDelay);
|
||||
node.force_insert<std::string, int>("TriggerConfig", params.triggerConfig);
|
||||
node.force_insert<std::string, int>("PileUpRejection", params.pileUpRejection);
|
||||
node.force_insert<std::string, int>("PURGap", params.purgap);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, Daqromancy::PSDParameters& params)
|
||||
{
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
|
||||
params.isEnabled = node["IsEnabled"].as<bool>();
|
||||
params.preTriggerTime = node["PreTrigger"].as<int>();
|
||||
params.dcOffset = node["DCOffset"].as<float>();
|
||||
params.pulsePolarity = (CAEN_DGTZ_PulsePolarity_t)node["PulsePolarity"].as<int>();
|
||||
params.dynamicRange = (Daqromancy::DynamicRange)node["DynamicRange"].as<int>();
|
||||
params.baselineThreshold = node["BaselineThreshold"].as<int>();
|
||||
params.triggerHoldOff = node["TriggerHoldoff"].as<int>();
|
||||
params.triggerThreshold = node["TriggerThreshold"].as<int>();
|
||||
params.selfTrigger = node["SelfTrigger"].as<int>();
|
||||
params.chargeSensitivity = node["ChargeSensitivity"].as<int>();
|
||||
params.shortGate = node["ShortGate"].as<int>();
|
||||
params.longGate = node["LongGate"].as<int>();
|
||||
params.preGate = node["PreGate"].as<int>();
|
||||
params.triggerValidationWindow = node["TriggerWindow"].as<int>();
|
||||
params.samplesBasline = node["BaselineSamples"].as<int>();
|
||||
params.discrminatorType = node["DiscriminatorType"].as<int>();
|
||||
params.cfdFraction = node["CFDFraction"].as<int>();
|
||||
params.cfdDelay = node["CFDDelay"].as<int>();
|
||||
params.triggerConfig = (CAEN_DGTZ_DPP_TriggerConfig_t)node["TriggerConfig"].as<int>();
|
||||
params.pileUpRejection = (CAEN_DGTZ_DPP_PUR_t)node["PileUpRejection"].as<int>();
|
||||
params.purgap = node["PURGap"].as<int>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert<Daqromancy::PHAWaveParameters>
|
||||
{
|
||||
static Node encode(const Daqromancy::PHAWaveParameters& waves)
|
||||
{
|
||||
Node node(NodeType::Map);
|
||||
|
||||
node.force_insert<std::string, int>("IsDual", waves.isDual);
|
||||
node.force_insert<std::string, int>("AnalogProbe1", waves.analogProbe1);
|
||||
node.force_insert<std::string, int>("AnalogProbe2", waves.analogProbe2);
|
||||
node.force_insert<std::string, int>("DigitalProbe1", waves.digitalProbe1);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, Daqromancy::PHAWaveParameters& waves)
|
||||
{
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
|
||||
waves.isDual = (CAEN_DGTZ_DPP_VirtualProbe_t)node["IsDual"].as<int>();
|
||||
waves.analogProbe1 = (Daqromancy::PHAVirtualProbe1Options)node["AnalogProbe1"].as<int>();
|
||||
waves.analogProbe2 = (Daqromancy::PHAVirtualProbe2Options)node["AnalogProbe2"].as<int>();
|
||||
waves.digitalProbe1 = (Daqromancy::PHADigitalProbe1Options)node["DigitalProbe1"].as<int>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct convert<Daqromancy::PSDWaveParameters>
|
||||
{
|
||||
static Node encode(const Daqromancy::PSDWaveParameters& waves)
|
||||
{
|
||||
Node node(NodeType::Map);
|
||||
|
||||
node.force_insert<std::string, int>("IsDual", waves.isDual);
|
||||
node.force_insert<std::string, int>("AnalogProbe1", waves.analogProbe1);
|
||||
node.force_insert<std::string, int>("AnalogProbe2", waves.analogProbe2);
|
||||
node.force_insert<std::string, int>("DigitalProbe1", waves.digitalProbe1);
|
||||
node.force_insert<std::string, int>("DigitalProbe2", waves.digitalProbe2);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, Daqromancy::PSDWaveParameters& waves)
|
||||
{
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
|
||||
waves.isDual = (CAEN_DGTZ_DPP_VirtualProbe_t)node["IsDual"].as<int>();
|
||||
waves.analogProbe1 = (Daqromancy::PSDVirtualProbe1Options)node["AnalogProbe1"].as<int>();
|
||||
waves.analogProbe2 = (Daqromancy::PSDVirtualProbe2Options)node["AnalogProbe2"].as<int>();
|
||||
waves.digitalProbe1 = (Daqromancy::PSDDigitalProbe1Options)node["DigitalProbe1"].as<int>();
|
||||
waves.digitalProbe2 = (Daqromancy::PSDDigitalProbe2Options)node["DigitalProbe2"].as<int>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const Daqromancy::DigitizerArgs& args)
|
||||
{
|
||||
stream << YAML::BeginMap;
|
||||
stream << YAML::Key << "ConnectionType" << YAML::Value << args.type;
|
||||
stream << YAML::Key << "LinkNumber" << YAML::Value << args.linkNumber;
|
||||
stream << YAML::Key << "CONETNode" << YAML::Value << args.conetNode;
|
||||
stream << YAML::Key << "VMEAddress" << YAML::Value << args.vmeAddress;
|
||||
stream << YAML::Key << "Handle" << YAML::Value << args.handle;
|
||||
stream << YAML::Key << "Model" << YAML::Value << args.model;
|
||||
stream << YAML::Key << "Firmware" << YAML::Value << args.firmware;
|
||||
stream << YAML::Key << "Channels" << YAML::Value << args.channels;
|
||||
stream << YAML::EndMap;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const Daqromancy::DigitizerParameters& params)
|
||||
{
|
||||
stream << YAML::BeginMap;
|
||||
stream << YAML::Key << "RecordLength" << YAML::Value << params.recordLength;
|
||||
stream << YAML::Key << "ChannelMask" << YAML::Value << params.channelMask;
|
||||
stream << YAML::Key << "EventAggr" << YAML::Value << params.eventAggr;
|
||||
stream << YAML::Key << "AcqMode" << YAML::Value << params.acqMode;
|
||||
stream << YAML::Key << "DPPAcqMode" << YAML::Value << params.dppAcqMode;
|
||||
stream << YAML::Key << "DPPSaveMode" << YAML::Value << params.dppSaveMode;
|
||||
stream << YAML::Key << "IOLevel" << YAML::Value << params.IOlevel;
|
||||
stream << YAML::Key << "TriggerMode" << YAML::Value << params.triggerMode;
|
||||
stream << YAML::Key << "SyncMode" << YAML::Value << params.syncMode;
|
||||
stream << YAML::EndMap;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const Daqromancy::PHAParameters& params)
|
||||
{
|
||||
stream << YAML::BeginMap;
|
||||
stream << YAML::Key << "IsEnabled" << YAML::Value << params.isEnabled;
|
||||
stream << YAML::Key << "PreTrigger" << YAML::Value << params.preTriggerTime;
|
||||
stream << YAML::Key << "DCOffset" << YAML::Value << params.dcOffset;
|
||||
stream << YAML::Key << "PulsePolarity" << YAML::Value << params.pulsePolarity;
|
||||
stream << YAML::Key << "DynamicRange" << YAML::Value << params.dynamicRange;
|
||||
stream << YAML::Key << "DecayTimeConst" << YAML::Value << params.decayTimeConst;
|
||||
stream << YAML::Key << "TrapFlatTop" << YAML::Value << params.trapFlatTop;
|
||||
stream << YAML::Key << "TrapRiseTime" << YAML::Value << params.trapRiseTime;
|
||||
stream << YAML::Key << "FlatTopDelay" << YAML::Value << params.flatTopDelay;
|
||||
stream << YAML::Key << "TriggerSmoothing" << YAML::Value << params.triggerFilterSmoothing;
|
||||
stream << YAML::Key << "InputRiseTime" << YAML::Value << params.inputRiseTime;
|
||||
stream << YAML::Key << "TriggerThreshold" << YAML::Value << params.triggerThreshold;
|
||||
stream << YAML::Key << "SamplesBaseline" << YAML::Value << params.samplesBaseLineMean;
|
||||
stream << YAML::Key << "SamplesPeak" << YAML::Value << params.samplesPeakMean;
|
||||
stream << YAML::Key << "PeakHoldoff" << YAML::Value << params.peakHoldOff;
|
||||
stream << YAML::Key << "BaselineHoldoff" << YAML::Value << params.baseLineHoldOff;
|
||||
stream << YAML::Key << "TriggerHoldoff" << YAML::Value << params.triggerHoldOff;
|
||||
stream << YAML::Key << "RiseTimeWindow" << YAML::Value << params.riseTimeValidationWindow;
|
||||
stream << YAML::Key << "RiseTimeDiscrimination" << YAML::Value << params.riseTimeDiscrimination;
|
||||
stream << YAML::Key << "DigitalProbeGain" << YAML::Value << params.digitalProbeGain;
|
||||
stream << YAML::Key << "EnergyNormFactor" << YAML::Value << params.energyNormalizationFactor;
|
||||
stream << YAML::Key << "InputDecimation" << YAML::Value << params.inputDecimation;
|
||||
stream << YAML::EndMap;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const Daqromancy::PSDParameters& params)
|
||||
{
|
||||
stream << YAML::BeginMap;
|
||||
stream << YAML::Key << "IsEnabled" << YAML::Value << params.isEnabled;
|
||||
stream << YAML::Key << "PreTrigger" << YAML::Value << params.preTriggerTime;
|
||||
stream << YAML::Key << "DCOffset" << YAML::Value << params.dcOffset;
|
||||
stream << YAML::Key << "PulsePolarity" << YAML::Value << params.pulsePolarity;
|
||||
stream << YAML::Key << "DynamicRange" << YAML::Value << params.dynamicRange;
|
||||
stream << YAML::Key << "BaselineThreshold" << YAML::Value << params.baselineThreshold;
|
||||
stream << YAML::Key << "TriggerHoldoff" << YAML::Value << params.triggerHoldOff;
|
||||
stream << YAML::Key << "TriggerThreshold" << YAML::Value << params.triggerThreshold;
|
||||
stream << YAML::Key << "SelfTrigger" << YAML::Value << params.selfTrigger;
|
||||
stream << YAML::Key << "ChargeSensitivity" << YAML::Value << params.chargeSensitivity;
|
||||
stream << YAML::Key << "ShortGate" << YAML::Value << params.shortGate;
|
||||
stream << YAML::Key << "LongGate" << YAML::Value << params.longGate;
|
||||
stream << YAML::Key << "PreGate" << YAML::Value << params.preGate;
|
||||
stream << YAML::Key << "TriggerWindow" << YAML::Value << params.triggerValidationWindow;
|
||||
stream << YAML::Key << "BaselineSamples" << YAML::Value << params.samplesBasline;
|
||||
stream << YAML::Key << "DiscriminatorType" << YAML::Value << params.discrminatorType;
|
||||
stream << YAML::Key << "CFDFraction" << YAML::Value << params.cfdFraction;
|
||||
stream << YAML::Key << "CFDDelay" << YAML::Value << params.cfdDelay;
|
||||
stream << YAML::Key << "TriggerConfig" << YAML::Value << params.triggerConfig;
|
||||
stream << YAML::Key << "PileUpRejection" << YAML::Value << params.pileUpRejection;
|
||||
stream << YAML::Key << "PURGap" << YAML::Value << params.purgap;
|
||||
stream << YAML::EndMap;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const std::vector<Daqromancy::PHAParameters>& channels)
|
||||
{
|
||||
stream << YAML::BeginSeq;
|
||||
for (const auto& params : channels)
|
||||
stream << params;
|
||||
stream << YAML::EndSeq;
|
||||
return stream;
|
||||
}
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const std::vector<Daqromancy::PSDParameters>& channels)
|
||||
{
|
||||
stream << YAML::BeginSeq;
|
||||
for (const auto& params : channels)
|
||||
stream << params;
|
||||
stream << YAML::EndSeq;
|
||||
return stream;
|
||||
}
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const Daqromancy::PHAWaveParameters& waves)
|
||||
{
|
||||
stream << YAML::BeginMap;
|
||||
stream << YAML::Key << "IsDual" << YAML::Value << waves.isDual;
|
||||
stream << YAML::Key << "AnalogProbe1" << YAML::Value << waves.analogProbe1;
|
||||
stream << YAML::Key << "AnalogProbe2" << YAML::Value << waves.analogProbe1;
|
||||
stream << YAML::Key << "DigitalProbe1" << YAML::Value << waves.digitalProbe1;
|
||||
stream << YAML::EndMap;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
Emitter& operator<<(Emitter& stream, const Daqromancy::PSDWaveParameters& waves)
|
||||
{
|
||||
stream << YAML::BeginMap;
|
||||
stream << YAML::Key << "IsDual" << YAML::Value << waves.isDual;
|
||||
stream << YAML::Key << "AnalogProbe1" << YAML::Value << waves.analogProbe1;
|
||||
stream << YAML::Key << "AnalogProbe2" << YAML::Value << waves.analogProbe1;
|
||||
stream << YAML::Key << "DigitalProbe1" << YAML::Value << waves.digitalProbe1;
|
||||
stream << YAML::Key << "DigitalProbe2" << YAML::Value << waves.digitalProbe2;
|
||||
stream << YAML::EndMap;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
|
||||
ProjectSerializer::ProjectSerializer(const std::filesystem::path& filepath) :
|
||||
m_filepath(filepath)
|
||||
{
|
||||
}
|
||||
|
||||
ProjectSerializer::~ProjectSerializer() {}
|
||||
|
||||
void ProjectSerializer::SerializeData(const DYProject::Ref& project)
|
||||
{
|
||||
std::ofstream output(m_filepath);
|
||||
if (!output.is_open())
|
||||
{
|
||||
DY_ERROR("Unable to open {0} to seralize project data!", m_filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
YAML::Emitter yamlStream;
|
||||
|
||||
yamlStream << YAML::BeginMap;
|
||||
|
||||
yamlStream << YAML::Key << "ProjectPath" << YAML::Value << project->GetProjectPath().string();
|
||||
yamlStream << YAML::Key << "RunNumber" << YAML::Value << project->GetRunNumber();
|
||||
yamlStream << YAML::Key << "DPPAcqMode" << YAML::Value << project->GetDPPAcqMode();
|
||||
|
||||
std::vector<DigitizerArgs> argList = project->GetDigitizerArgsList();
|
||||
|
||||
yamlStream << YAML::Key << "Digitizers" << YAML::Value << YAML::BeginSeq;
|
||||
for (auto& args : argList)
|
||||
{
|
||||
yamlStream << YAML::BeginMap;
|
||||
yamlStream << YAML::Key << "Digitizer" << YAML::Value << args.name;
|
||||
yamlStream << YAML::Key << "DigitizerArgs" << YAML::Value << args;
|
||||
yamlStream << YAML::Key << "DigitizerParameters" << YAML::Value << project->GetDigitizerParameters(args.handle);
|
||||
switch (args.firmware)
|
||||
{
|
||||
case CAEN_DGTZ_DPPFirmware_PHA:
|
||||
{
|
||||
yamlStream << YAML::Key << "PHAChannels" << YAML::Value << project->GetPHAParameters(args.handle);
|
||||
yamlStream << YAML::Key << "PHAWaves" << YAML::Value << project->GetPHAWaveParameters(args.handle);
|
||||
break;
|
||||
}
|
||||
case CAEN_DGTZ_DPPFirmware_PSD:
|
||||
{
|
||||
yamlStream << YAML::Key << "PSDChannels" << YAML::Value << project->GetPSDParameters(args.handle);
|
||||
yamlStream << YAML::Key << "PSDWaves" << YAML::Value << project->GetPSDWaveParameters(args.handle);
|
||||
break;
|
||||
}
|
||||
case CAEN_DGTZ_DPPFirmwareNotSupported:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
yamlStream << YAML::EndMap;
|
||||
}
|
||||
yamlStream << YAML::EndSeq << YAML::EndMap;
|
||||
|
||||
output << yamlStream.c_str();
|
||||
|
||||
output.close();
|
||||
}
|
||||
|
||||
void ProjectSerializer::DeserializeData(const DYProject::Ref& project)
|
||||
{
|
||||
YAML::Node data;
|
||||
try
|
||||
{
|
||||
data = YAML::LoadFile(m_filepath.string());
|
||||
}
|
||||
catch (YAML::ParserException& e)
|
||||
{
|
||||
DY_ERROR("Unable to load in project settings from file {0}", m_filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
project->SetProjectPath(data["ProjectPath"].as<std::string>());
|
||||
project->SetRunNumber(data["RunNumber"].as<uint32_t>());
|
||||
project->SetDPPAcqMode((DPPAcqMode)data["DPPAcqMode"].as<int>());
|
||||
|
||||
YAML::Node digitizers = data["Digitizers"];
|
||||
|
||||
//Init some memory for digitizer data
|
||||
DigitizerArgs args;
|
||||
DigitizerParameters params;
|
||||
std::vector<PHAParameters> phaChannels;
|
||||
std::vector<PSDParameters> psdChannels;
|
||||
PHAWaveParameters phaWaves;
|
||||
PSDWaveParameters psdWaves;
|
||||
|
||||
if (digitizers)
|
||||
{
|
||||
for (auto dgtz : digitizers)
|
||||
{
|
||||
args = dgtz["DigitizerArgs"].as<DigitizerArgs>();
|
||||
args.name = dgtz["Digitizer"].as<std::string>();
|
||||
params = dgtz["DigitizerParameters"].as<DigitizerParameters>();
|
||||
switch (args.firmware)
|
||||
{
|
||||
case CAEN_DGTZ_DPPFirmware_PHA:
|
||||
{
|
||||
phaChannels = dgtz["PHAChannels"].as<std::vector<PHAParameters>>();
|
||||
phaWaves = dgtz["PHAWaves"].as<PHAWaveParameters>();
|
||||
|
||||
if (args != project->GetDigitizerArgs(args.handle))
|
||||
{
|
||||
DY_ERROR("When attempting to deserialize project settings, board mismatch detected at handle {0}! Stopping load, Daqromancy should be restarted.", args.handle);
|
||||
return;
|
||||
}
|
||||
|
||||
project->SetDigitizerParameters(args.handle, params);
|
||||
project->SetPHAParameters(args.handle, phaChannels);
|
||||
project->SetPHAWaveParameters(args.handle, phaWaves);
|
||||
break;
|
||||
}
|
||||
case CAEN_DGTZ_DPPFirmware_PSD:
|
||||
{
|
||||
psdChannels = dgtz["PSDChannels"].as<std::vector<PSDParameters>>();
|
||||
psdWaves = dgtz["PSDWaves"].as<PSDWaveParameters>();
|
||||
|
||||
if (args != project->GetDigitizerArgs(args.handle))
|
||||
{
|
||||
DY_ERROR("When attempting to deserialize project settings, board mismatch detected at handle {0}! Stopping load, Daqromancy should be restarted.", args.handle);
|
||||
return;
|
||||
}
|
||||
|
||||
project->SetDigitizerParameters(args.handle, params);
|
||||
project->SetPSDParameters(args.handle, psdChannels);
|
||||
project->SetPSDWaveParameters(args.handle, psdWaves);
|
||||
break;
|
||||
}
|
||||
case CAEN_DGTZ_DPPFirmwareNotSupported:
|
||||
{
|
||||
DY_WARN("Invalid firmware detected, this better just be a debug test");
|
||||
|
||||
if (args != project->GetDigitizerArgs(args.handle))
|
||||
{
|
||||
DY_ERROR("When attempting to deserialize project settings, board mismatch detected at handle {0}! Stopping load, Daqromancy should be restarted.", args.handle);
|
||||
return;
|
||||
}
|
||||
|
||||
project->SetDigitizerParameters(args.handle, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
src/Core/ProjectSerializer.h
Normal file
25
src/Core/ProjectSerializer.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef PROJECT_SERIALIZER_H
|
||||
#define PROJECT_SERIALIZER_H
|
||||
|
||||
#include "DYProject.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class ProjectSerializer
|
||||
{
|
||||
public:
|
||||
ProjectSerializer(const std::filesystem::path& path);
|
||||
~ProjectSerializer();
|
||||
|
||||
void SerializeData(const DYProject::Ref& project);
|
||||
void DeserializeData(const DYProject::Ref& project);
|
||||
|
||||
private:
|
||||
std::filesystem::path m_filepath;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
53
src/Core/ScalarDistributor.cpp
Normal file
53
src/Core/ScalarDistributor.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include "ScalarDistributor.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
std::unordered_map<std::string, Scalar::Ref> ScalarDistributor::s_scalarMap;
|
||||
|
||||
const Scalar::Ref& ScalarDistributor::Bind(const std::string& name)
|
||||
{
|
||||
auto iter = s_scalarMap.find(name);
|
||||
if (iter != s_scalarMap.end())
|
||||
return iter->second;
|
||||
else
|
||||
{
|
||||
s_scalarMap[name] = std::make_shared<Scalar>(name);
|
||||
return s_scalarMap[name];
|
||||
}
|
||||
}
|
||||
|
||||
void ScalarDistributor::UnBind(const std::string& name)
|
||||
{
|
||||
if(s_scalarMap.contains(name))
|
||||
s_scalarMap.erase(name);
|
||||
}
|
||||
|
||||
void ScalarDistributor::CalculateRates(double timestep)
|
||||
{
|
||||
for(auto& scalar : s_scalarMap)
|
||||
{
|
||||
uint64_t changeInCounts = scalar.second->value - scalar.second->lastCheckedValue;
|
||||
scalar.second->lastCheckedValue += changeInCounts;
|
||||
scalar.second->rate = changeInCounts / timestep;
|
||||
}
|
||||
}
|
||||
|
||||
float ScalarDistributor::GetRate(const std::string& name)
|
||||
{
|
||||
auto iter = s_scalarMap.find(name);
|
||||
if (iter != s_scalarMap.end())
|
||||
return iter->second->rate;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void ScalarDistributor::ClearScalars()
|
||||
{
|
||||
for (auto& scalar : s_scalarMap)
|
||||
{
|
||||
scalar.second->value = 0.0f;
|
||||
scalar.second->lastCheckedValue = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
43
src/Core/ScalarDistributor.h
Normal file
43
src/Core/ScalarDistributor.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef SCALAR_DISTRIBUTOR_H
|
||||
#define SCALAR_DISTRIBUTOR_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//Scalars can be incremented thread-safe, lock-free using atomics
|
||||
struct Scalar
|
||||
{
|
||||
using Ref = std::shared_ptr<Scalar>;
|
||||
Scalar(const std::string& n) :
|
||||
value(0), lastCheckedValue(0), name(n)
|
||||
{
|
||||
}
|
||||
|
||||
std::atomic<uint64_t> value;
|
||||
std::atomic<uint64_t> lastCheckedValue; //Value from the last time GetRate was called
|
||||
std::atomic<float> rate;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class ScalarDistributor
|
||||
{
|
||||
public:
|
||||
|
||||
//Bind/UnBind is NOT thread-safe. Should not be used in a multi-threaded context.
|
||||
static const Scalar::Ref& Bind(const std::string& name);
|
||||
static void UnBind(const std::string& name);
|
||||
|
||||
//CalculateRates/GetRate is implicitly thread safe as it does not modify the map itself
|
||||
static void CalculateRates(double timestep);
|
||||
static float GetRate(const std::string& name); //Returns in Hz; timestep should be in seconds
|
||||
//ClearScalars is implicitly thread safe as it does not modify the map itself
|
||||
static void ClearScalars();
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string, Scalar::Ref> s_scalarMap;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
127
src/Core/ThreadSafeQueue.h
Normal file
127
src/Core/ThreadSafeQueue.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
#ifndef THREAD_SAFE_QUEUE_H
|
||||
#define THREAD_SAFE_QUEUE_H
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
template<typename T>
|
||||
class ThreadSafeQueue
|
||||
{
|
||||
public:
|
||||
ThreadSafeQueue() = default;
|
||||
ThreadSafeQueue(const ThreadSafeQueue&) = delete; //no copy
|
||||
~ThreadSafeQueue() { Clear(); }
|
||||
|
||||
void PushBack(const T& data)
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
m_queue.push_back(data);
|
||||
|
||||
std::scoped_lock<std::mutex> condGuard(m_conditionMutex);
|
||||
m_conditional.notify_one();
|
||||
}
|
||||
|
||||
void PushFront(const T& data)
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
m_queue.push_front(data);
|
||||
|
||||
std::scoped_lock<std::mutex> guard(m_conditionMutex);
|
||||
m_conditional.notify_one();
|
||||
}
|
||||
|
||||
void PopBack()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
m_queue.pop_back();
|
||||
}
|
||||
|
||||
void PopFront()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
m_queue.pop_front();
|
||||
}
|
||||
|
||||
const T& Front()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
return m_queue.front();
|
||||
}
|
||||
|
||||
const T& Back()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
return m_queue.back();
|
||||
}
|
||||
|
||||
std::size_t Size()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
bool IsEmpty()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
m_queue.clear();
|
||||
}
|
||||
|
||||
//For iterator loops, need begin()/end() idiom
|
||||
|
||||
std::deque<T>::iterator begin()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
return m_queue.begin();
|
||||
}
|
||||
|
||||
std::deque<T>::iterator end()
|
||||
{
|
||||
std::scoped_lock<std::mutex> guard(m_queueMutex);
|
||||
return m_queue.end();
|
||||
}
|
||||
|
||||
void Wait()
|
||||
{
|
||||
while(IsEmpty() && !m_isForceWakeup)
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(m_conditionMutex);
|
||||
m_conditional.wait(guard);
|
||||
}
|
||||
}
|
||||
|
||||
void ForceWakeup()
|
||||
{
|
||||
m_isForceWakeup = true;
|
||||
|
||||
std::unique_lock<std::mutex> guard(m_conditionMutex);
|
||||
m_conditional.notify_one();
|
||||
}
|
||||
|
||||
void ResetWakeup()
|
||||
{
|
||||
m_isForceWakeup = false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_queueMutex;
|
||||
std::deque<T> m_queue;
|
||||
|
||||
std::mutex m_conditionMutex;
|
||||
std::condition_variable m_conditional;
|
||||
|
||||
std::atomic<bool> m_isForceWakeup = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
45
src/Core/Window.h
Normal file
45
src/Core/Window.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef WINDOW_H
|
||||
#define WINDOW_H
|
||||
|
||||
#include "DYCore.h"
|
||||
#include "Events/Event.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
struct WindowProperties
|
||||
{
|
||||
WindowProperties(const std::string& t = "Daqromancy", uint32_t w=1280, uint32_t h=720) :
|
||||
title(t), width(w), height(h)
|
||||
{
|
||||
}
|
||||
|
||||
std::string title;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
|
||||
using EventCallbackFunc = std::function<void(Event&)>;
|
||||
|
||||
virtual ~Window() {}
|
||||
|
||||
virtual void OnUpdate() = 0;
|
||||
virtual uint32_t GetWidth() = 0;
|
||||
virtual uint32_t GetHeight() = 0;
|
||||
virtual std::string GetName() = 0;
|
||||
|
||||
virtual void SetEventCallback(EventCallbackFunc function) = 0;
|
||||
virtual void SetVSync(bool enabled) = 0;
|
||||
virtual bool IsVSync() = 0;
|
||||
|
||||
virtual void* GetNativeWindow() const = 0; //dirty
|
||||
|
||||
static Window* Create(const WindowProperties& props = WindowProperties());
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
19
src/Core/main.cpp
Normal file
19
src/Core/main.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include "Application.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Daqromancy::Logger::Init();
|
||||
DY_INFO("Welcome to Daqromancy.");
|
||||
|
||||
Daqromancy::ApplicationArgs args;
|
||||
args.name = "Daqromancy";
|
||||
args.runtimePath = "";
|
||||
args.argc = argc;
|
||||
args.argv = argv;
|
||||
|
||||
Daqromancy::Application* app = Daqromancy::CreateApplication(args);
|
||||
|
||||
app->Run();
|
||||
|
||||
delete app;
|
||||
}
|
250
src/DAQ/AcquisitionLayer.cpp
Normal file
250
src/DAQ/AcquisitionLayer.cpp
Normal file
|
@ -0,0 +1,250 @@
|
|||
#include "AcquisitionLayer.h"
|
||||
#include "Core/DYCore.h"
|
||||
#include "Core/DataDistributor.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
static bool SortByHandle(const Digitizer::Ref& digi1, const Digitizer::Ref& digi2)
|
||||
{
|
||||
return digi1->GetDigitizerArgs().handle < digi2->GetDigitizerArgs().handle;
|
||||
}
|
||||
|
||||
AcquisitionLayer::AcquisitionLayer(const DYProject::Ref& project) :
|
||||
m_project(project), m_acqThread(nullptr), m_running(false), m_server(52324)
|
||||
{
|
||||
m_server.PowerOn();
|
||||
}
|
||||
|
||||
AcquisitionLayer::~AcquisitionLayer()
|
||||
{
|
||||
DY_INFO("Shutting down acq");
|
||||
if (m_running)
|
||||
{
|
||||
DestroyAcqThread();
|
||||
}
|
||||
DY_INFO("Finished");
|
||||
m_server.Shutdown();
|
||||
}
|
||||
|
||||
void AcquisitionLayer::OnUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AcquisitionLayer::OnEvent(Event& e)
|
||||
{
|
||||
EventDispatcher dispatch(e);
|
||||
|
||||
dispatch.Dispatch<AcqStartEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqStartEvent));
|
||||
dispatch.Dispatch<AcqStopEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqStopEvent));
|
||||
dispatch.Dispatch<AcqParametersEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqParametersEvent));
|
||||
dispatch.Dispatch<AcqSyncArgsEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqSyncArgsEvent));
|
||||
dispatch.Dispatch<AcqDPPModeEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqDPPModeEvent));
|
||||
dispatch.Dispatch<AcqDetectBoardsEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqDetectBoardsEvent));
|
||||
dispatch.Dispatch<AcqDisconnectBoardsEvent>(BIND_EVENT_FUNCTION(AcquisitionLayer::OnAcqDisconnectBoardsEvent));
|
||||
}
|
||||
|
||||
void AcquisitionLayer::DestroyAcqThread()
|
||||
{
|
||||
m_running = false;
|
||||
if (m_acqThread != nullptr && m_acqThread->joinable())
|
||||
{
|
||||
DY_INFO("Destroying acquisition thread...");
|
||||
m_acqThread->join();
|
||||
delete m_acqThread;
|
||||
m_acqThread = nullptr;
|
||||
DY_INFO("Acquisition thread is destroyed.");
|
||||
}
|
||||
else if(m_acqThread != nullptr)
|
||||
DY_WARN("Unable to destroy acquisition thread, but is active");
|
||||
}
|
||||
|
||||
bool AcquisitionLayer::OnAcqStartEvent(AcqStartEvent& e)
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
DY_WARN("Attempted to start a new acquisition while one is already running!");
|
||||
return true;
|
||||
}
|
||||
else if (m_digitizerChain.empty())
|
||||
{
|
||||
DY_WARN("Cannot start acquisition without any digitizers!");
|
||||
return true;
|
||||
}
|
||||
else if (m_acqThread != nullptr)
|
||||
{
|
||||
DestroyAcqThread();
|
||||
}
|
||||
|
||||
DY_INFO("Starting acquisition thread...");
|
||||
m_running = true;
|
||||
m_acqThread = new std::thread(&AcquisitionLayer::Run, std::ref(*this));
|
||||
DY_INFO("Running.");
|
||||
|
||||
//If we chose to write to disk, start the file handler
|
||||
if (e.IsWriteToDisk())
|
||||
m_fileIO.StartRun(m_project);
|
||||
//If we chose to write to server start the server feed
|
||||
if (e.IsWriteToServer())
|
||||
m_server.StartDataFeed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AcquisitionLayer::OnAcqStopEvent(AcqStopEvent& e)
|
||||
{
|
||||
DestroyAcqThread();
|
||||
|
||||
if (m_fileIO.IsRunning())
|
||||
m_fileIO.StopRun();
|
||||
if (m_server.IsFeeding())
|
||||
m_server.StopDataFeed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AcquisitionLayer::OnAcqParametersEvent(AcqParametersEvent& e)
|
||||
{
|
||||
if(m_running)
|
||||
{
|
||||
DY_WARN("Cannot change digitizer parameters while aquisition is running!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.GetAccessType() == DigitizerAccessType::All)
|
||||
{
|
||||
for (Digitizer::Ref& digitizer : m_digitizerChain)
|
||||
{
|
||||
const DigitizerArgs& args = digitizer->GetDigitizerArgs();
|
||||
if (args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
|
||||
{
|
||||
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
|
||||
digitizer->SetChannelParameters(m_project->GetPHAParameters(args.handle));
|
||||
digitizer->SetWaveformParameters(m_project->GetPHAWaveParameters(args.handle));
|
||||
|
||||
digitizer->LoadSettings();
|
||||
}
|
||||
else if (digitizer->GetDigitizerArgs().firmware == CAEN_DGTZ_DPPFirmware_PSD)
|
||||
{
|
||||
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
|
||||
digitizer->SetChannelParameters(m_project->GetPSDParameters(args.handle));
|
||||
digitizer->SetWaveformParameters(m_project->GetPSDWaveParameters(args.handle));
|
||||
|
||||
digitizer->LoadSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(e.GetAccessType() == DigitizerAccessType::Single)
|
||||
{
|
||||
Digitizer::Ref& digitizer = m_digitizerChain[e.GetBoardHandle()];
|
||||
const DigitizerArgs& args = digitizer->GetDigitizerArgs();
|
||||
if (args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
|
||||
{
|
||||
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
|
||||
digitizer->SetChannelParameters(m_project->GetPHAParameters(args.handle));
|
||||
digitizer->SetWaveformParameters(m_project->GetPHAWaveParameters(args.handle));
|
||||
|
||||
digitizer->LoadSettings();
|
||||
}
|
||||
else if (digitizer->GetDigitizerArgs().firmware == CAEN_DGTZ_DPPFirmware_PSD)
|
||||
{
|
||||
digitizer->SetDigitizerParameters(m_project->GetDigitizerParameters(args.handle));
|
||||
digitizer->SetChannelParameters(m_project->GetPSDParameters(args.handle));
|
||||
digitizer->SetWaveformParameters(m_project->GetPSDWaveParameters(args.handle));
|
||||
|
||||
digitizer->LoadSettings();
|
||||
}
|
||||
}
|
||||
|
||||
//Setting parameters can sometimes lead to CAEN modifying values (invalid value, etc), so inform the project of changes
|
||||
m_project->SetDigitizerData(m_digitizerChain);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AcquisitionLayer::OnAcqSyncArgsEvent(AcqSyncArgsEvent& e)
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
DY_WARN("Cannot update synchronization settings while acquisition is running!");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_digitizerChain.SynchronizeBoards(e.GetArgs());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AcquisitionLayer::OnAcqDPPModeEvent(AcqDPPModeEvent& e)
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
DY_WARN("Cannot update DPP Acquisition settings while aquisitiion is running!");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_digitizerChain.SetDPPAcqMode(m_project->GetDPPAcqMode());
|
||||
m_project->SetDigitizerData(m_digitizerChain);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AcquisitionLayer::OnAcqDetectBoardsEvent(AcqDetectBoardsEvent& e)
|
||||
{
|
||||
DY_INFO("Querying the system for digitizers. WARNING: Daqromancy currently only supports OpticalLink connections");
|
||||
m_digitizerChain.DetectBoards();
|
||||
//Tell the project what happened
|
||||
m_project->SetDigitizerData(m_digitizerChain);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AcquisitionLayer::OnAcqDisconnectBoardsEvent(AcqDisconnectBoardsEvent& e)
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
DY_WARN("Cannot disconnect digitizers while the acquisition is running!");
|
||||
return true;
|
||||
}
|
||||
|
||||
DY_INFO("Disconnecting digitizers...");
|
||||
|
||||
m_digitizerChain.DisconnectBoards();
|
||||
|
||||
DY_INFO("Digitizers disconnected.");
|
||||
|
||||
//Tell the project what happened
|
||||
m_project->SetDigitizerData(m_digitizerChain);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<DigitizerArgs> AcquisitionLayer::GetArgList()
|
||||
{
|
||||
std::vector<DigitizerArgs> list;
|
||||
list.reserve(m_digitizerChain.size());
|
||||
for (auto& digitizer : m_digitizerChain)
|
||||
list.push_back(digitizer->GetDigitizerArgs());
|
||||
return list;
|
||||
}
|
||||
|
||||
void AcquisitionLayer::Run()
|
||||
{
|
||||
if (!m_digitizerChain.Start())
|
||||
return;
|
||||
|
||||
std::vector<DYData> recievedData; //local data buffer
|
||||
//Run aquisition loop
|
||||
while (m_running)
|
||||
{
|
||||
for (auto& digitizer : m_digitizerChain)
|
||||
digitizer->ReadData(recievedData);
|
||||
|
||||
if (recievedData.empty())
|
||||
continue;
|
||||
|
||||
DataDistributor::PushData(recievedData);
|
||||
recievedData.clear();
|
||||
}
|
||||
|
||||
m_digitizerChain.Stop();
|
||||
}
|
||||
}
|
68
src/DAQ/AcquisitionLayer.h
Normal file
68
src/DAQ/AcquisitionLayer.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
#ifndef ACQUISITION_LAYER_H
|
||||
#define ACQUISITION_LAYER_H
|
||||
|
||||
#include "Core/Layer.h"
|
||||
#include "Core/DYProject.h"
|
||||
#include "Events/AcqEvent.h"
|
||||
#include "Digitizer.h"
|
||||
#include "DigitizerChain.h"
|
||||
|
||||
#include "DYio/DYRun.h"
|
||||
#include "DYio/TCPServer.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class AcquisitionLayer : public Layer
|
||||
{
|
||||
public:
|
||||
using EventCallbackFunc = std::function<void(Event&)>;
|
||||
|
||||
AcquisitionLayer(const DYProject::Ref& project);
|
||||
~AcquisitionLayer();
|
||||
|
||||
void SetEventCallback(const EventCallbackFunc& func) { m_callbackFunction = func; }
|
||||
|
||||
virtual void OnUpdate() override;
|
||||
virtual void OnEvent(Event& e) override;
|
||||
|
||||
bool IsRunning() { return m_running; }
|
||||
|
||||
private:
|
||||
//Event handlers
|
||||
bool OnAcqStartEvent(AcqStartEvent& e);
|
||||
bool OnAcqStopEvent(AcqStopEvent& e);
|
||||
bool OnAcqDetectBoardsEvent(AcqDetectBoardsEvent& e);
|
||||
bool OnAcqParametersEvent(AcqParametersEvent& e);
|
||||
bool OnAcqSyncArgsEvent(AcqSyncArgsEvent& e);
|
||||
bool OnAcqDisconnectBoardsEvent(AcqDisconnectBoardsEvent& e);
|
||||
bool OnAcqDPPModeEvent(AcqDPPModeEvent& e);
|
||||
|
||||
//Functionality
|
||||
void DestroyAcqThread();
|
||||
std::vector<DigitizerArgs> GetArgList();
|
||||
|
||||
//Acquistion loop
|
||||
void Run();
|
||||
|
||||
EventCallbackFunc m_callbackFunction;
|
||||
|
||||
DYProject::Ref m_project;
|
||||
|
||||
//IO
|
||||
DYRun m_fileIO;
|
||||
TCPServer m_server;
|
||||
|
||||
DigitizerChain m_digitizerChain;
|
||||
SyncArgs m_syncStatus;
|
||||
|
||||
std::thread* m_acqThread;
|
||||
std::mutex m_acqMutex;
|
||||
std::atomic<bool> m_running;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
620
src/DAQ/Digitizer.cpp
Normal file
620
src/DAQ/Digitizer.cpp
Normal file
|
@ -0,0 +1,620 @@
|
|||
#include "Digitizer.h"
|
||||
#include "CAENDigitizer.h"
|
||||
|
||||
//Mask a specific bit
|
||||
#define BIT(x) (1<<x)
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
/////////////////////// Open Function ///////////////////////
|
||||
Digitizer::Ref OpenDigitizer(DigitizerArgs& args)
|
||||
{
|
||||
int code = CAEN_DGTZ_OpenDigitizer(args.type, args.linkNumber, args.conetNode, args.vmeAddress, &args.handle);
|
||||
if(code != CAEN_DGTZ_ErrorCode::CAEN_DGTZ_Success)
|
||||
{
|
||||
//report error
|
||||
return nullptr;
|
||||
}
|
||||
CAEN_DGTZ_BoardInfo_t info;
|
||||
CAEN_DGTZ_DPPFirmware_t firmware;
|
||||
code |= CAEN_DGTZ_GetInfo(args.handle, &info);
|
||||
code |= CAEN_DGTZ_GetDPPFirmwareType(args.handle, &firmware);
|
||||
if(code != CAEN_DGTZ_ErrorCode::CAEN_DGTZ_Success)
|
||||
{
|
||||
//report errors
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch(firmware)
|
||||
{
|
||||
case CAEN_DGTZ_DPPFirmware_PHA: return std::make_shared<DigitizerPHA>(args, info, code);
|
||||
case CAEN_DGTZ_DPPFirmware_PSD: return std::make_shared<DigitizerPSD>(args, info, code);
|
||||
case CAEN_DGTZ_DPPFirmware_DAW: return nullptr;
|
||||
case CAEN_DGTZ_DPPFirmware_CI: return nullptr;
|
||||
case CAEN_DGTZ_DPPFirmware_ZLE: return nullptr;
|
||||
case CAEN_DGTZ_DPPFirmware_QDC: return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
/////////////////////// Open Function ///////////////////////
|
||||
|
||||
|
||||
/////////////////////// DigitizerPHA ///////////////////////
|
||||
DigitizerPHA::DigitizerPHA(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) :
|
||||
Digitizer(), m_eventData(nullptr), m_waveData(nullptr)
|
||||
{
|
||||
Init(args, info, ec);
|
||||
}
|
||||
|
||||
DigitizerPHA::~DigitizerPHA() { Close(); }
|
||||
|
||||
void DigitizerPHA::Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec)
|
||||
{
|
||||
m_args = args;
|
||||
m_internalData = info;
|
||||
|
||||
m_args.status = ec;
|
||||
if(info.Model == 1730)
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1730;
|
||||
else if(info.Model == 1725)
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1725;
|
||||
else if(info.Model == 1740)
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1740;
|
||||
else
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_DT5720;
|
||||
m_args.name = info.ModelName + std::to_string(info.SerialNumber);
|
||||
m_args.firmware = CAEN_DGTZ_DPPFirmware_PHA;
|
||||
m_args.channels = info.Channels;
|
||||
|
||||
m_channelParams.resize(info.Channels);
|
||||
m_eventCountsPerChannel = new uint32_t[info.Channels];
|
||||
m_eventData = new CAEN_DGTZ_DPP_PHA_Event_t*[info.Channels];
|
||||
m_waveData = new CAEN_DGTZ_DPP_PHA_Waveforms_t*[info.Channels];
|
||||
|
||||
for (int i = 0; i < info.Channels; i++)
|
||||
{
|
||||
m_scalars.push_back(ScalarDistributor::Bind(m_args.name + std::to_string(i)));
|
||||
}
|
||||
|
||||
m_samplingTime = GetSamplingPeriod(m_args.model);
|
||||
|
||||
LoadDigitizerParameters();
|
||||
LoadChannelParameters();
|
||||
//Must load default parameters here to generate a buffer
|
||||
AllocateMemory(); //More specifically: CAEN memory
|
||||
|
||||
m_isConnected = true;
|
||||
}
|
||||
|
||||
void DigitizerPHA::Close()
|
||||
{
|
||||
StopAquisition(); //Stop aquisition if needed
|
||||
|
||||
DeallocateMemory();
|
||||
|
||||
delete[] m_eventCountsPerChannel;
|
||||
|
||||
m_lowBuffer = nullptr;
|
||||
m_eventData = nullptr;
|
||||
m_eventCountsPerChannel = nullptr;
|
||||
m_waveData = nullptr;
|
||||
|
||||
m_isConnected = false;
|
||||
|
||||
//Explicitly wipe-out scalars
|
||||
for (auto& scalar : m_scalars)
|
||||
{
|
||||
ScalarDistributor::UnBind(scalar->name);
|
||||
}
|
||||
m_scalars.clear();
|
||||
}
|
||||
|
||||
void DigitizerPHA::SetDigitizerParameters(const DigitizerParameters& params)
|
||||
{
|
||||
if(!m_isConnected || m_isActive)
|
||||
return;
|
||||
|
||||
m_digitizerParams = params;
|
||||
}
|
||||
|
||||
void DigitizerPHA::InternalSetChannelParameters(const std::vector<PHAParameters>& params)
|
||||
{
|
||||
if(!m_isConnected || m_isActive)
|
||||
return;
|
||||
|
||||
m_channelParams = params;
|
||||
}
|
||||
|
||||
void DigitizerPHA::InternalSetWaveformParameters(const PHAWaveParameters& params)
|
||||
{
|
||||
if(!m_isConnected || m_isActive)
|
||||
return;
|
||||
|
||||
m_waveParams = params;
|
||||
}
|
||||
|
||||
//This cannot possibly be the correct method for cases where digitizers are chained.
|
||||
//July 2022: Is not. See Synchronize.h/.cpp
|
||||
void DigitizerPHA::StartAquisition()
|
||||
{
|
||||
if(!m_isActive && m_isConnected)
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SWStartAcquisition(m_args.handle);
|
||||
m_isActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
//This cannot possibly be the correct method for cases where digitizers are chained.
|
||||
//July 2022: Is not. See Synchronize.h/.cpp
|
||||
void DigitizerPHA::StopAquisition()
|
||||
{
|
||||
if(m_isActive && m_isConnected)
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SWStopAcquisition(m_args.handle);
|
||||
m_isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitizerPHA::LoadSettings()
|
||||
{
|
||||
DeallocateMemory();
|
||||
|
||||
LoadDigitizerParameters();
|
||||
LoadChannelParameters();
|
||||
LoadWaveformParameters();
|
||||
|
||||
AllocateMemory();
|
||||
}
|
||||
|
||||
//Set digitizer wide parameters
|
||||
void DigitizerPHA::LoadDigitizerParameters()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SetAcquisitionMode(m_args.handle, m_digitizerParams.acqMode);
|
||||
m_args.status |= CAEN_DGTZ_SetDPPAcquisitionMode(m_args.handle, m_digitizerParams.dppAcqMode, m_digitizerParams.dppSaveMode);
|
||||
m_args.status |= CAEN_DGTZ_SetRecordLength(m_args.handle, m_digitizerParams.recordLength / m_samplingTime);
|
||||
m_args.status |= CAEN_DGTZ_SetIOLevel(m_args.handle, m_digitizerParams.IOlevel);
|
||||
|
||||
m_args.status |= CAEN_DGTZ_SetExtTriggerInputMode(m_args.handle, m_digitizerParams.triggerMode);
|
||||
m_args.status |= CAEN_DGTZ_SetDPPEventAggregation(m_args.handle, m_digitizerParams.eventAggr, 0);
|
||||
m_args.status |= CAEN_DGTZ_SetRunSynchronizationMode(m_args.handle, m_digitizerParams.syncMode);
|
||||
m_args.status |= CAEN_DGTZ_SetChannelEnableMask(m_args.handle, m_digitizerParams.channelMask);
|
||||
}
|
||||
|
||||
//Set per channel data
|
||||
void DigitizerPHA::LoadChannelParameters()
|
||||
{
|
||||
m_digitizerParams.channelMask = 0;
|
||||
for(std::size_t i=0; i<m_channelParams.size(); i++)
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SetChannelDCOffset(m_args.handle, i, uint32_t(0xffff * m_channelParams[i].dcOffset)); //Max range is 0xffff
|
||||
m_args.status |= CAEN_DGTZ_SetDPPPreTriggerSize(m_args.handle, i, m_channelParams[i].preTriggerTime / m_samplingTime);
|
||||
m_args.status |= CAEN_DGTZ_SetChannelPulsePolarity(m_args.handle, i, m_channelParams[i].pulsePolarity);
|
||||
m_args.status |= CAEN_DGTZ_WriteRegister(m_args.handle, 0x1028 + (i<<8), m_channelParams[i].dynamicRange);
|
||||
|
||||
if(m_channelParams[i].isEnabled)
|
||||
{
|
||||
m_digitizerParams.channelMask |= (BIT(i)); //flip channel bit to 1 scince its enabled
|
||||
}
|
||||
|
||||
//Write data to garbage CAEN style structs
|
||||
|
||||
m_caenParams.M[i] = m_channelParams[i].decayTimeConst / m_samplingTime;
|
||||
m_caenParams.m[i] = m_channelParams[i].trapFlatTop / m_samplingTime;
|
||||
m_caenParams.k[i] = m_channelParams[i].trapRiseTime / m_samplingTime;
|
||||
m_caenParams.ftd[i] = m_channelParams[i].flatTopDelay / m_samplingTime;
|
||||
m_caenParams.a[i] = m_channelParams[i].triggerFilterSmoothing;
|
||||
m_caenParams.b[i] = m_channelParams[i].inputRiseTime / m_samplingTime;
|
||||
m_caenParams.thr[i] = m_channelParams[i].triggerThreshold;
|
||||
m_caenParams.nsbl[i] = m_channelParams[i].samplesBaseLineMean;
|
||||
m_caenParams.nspk[i] = m_channelParams[i].samplesPeakMean;
|
||||
m_caenParams.pkho[i] = m_channelParams[i].peakHoldOff / m_samplingTime;
|
||||
m_caenParams.blho[i] = m_channelParams[i].baseLineHoldOff / m_samplingTime;
|
||||
m_caenParams.trgho[i] = m_channelParams[i].triggerHoldOff / m_samplingTime;
|
||||
m_caenParams.twwdt[i] = m_channelParams[i].riseTimeValidationWindow / m_samplingTime;
|
||||
m_caenParams.trgwin[i] = m_channelParams[i].riseTimeDiscrimination / m_samplingTime;
|
||||
m_caenParams.dgain[i] = m_channelParams[i].digitalProbeGain;
|
||||
m_caenParams.enf[i] = m_channelParams[i].energyNormalizationFactor;
|
||||
m_caenParams.decimation[i] = m_channelParams[i].inputDecimation;
|
||||
//So far as I can tell these are not used
|
||||
m_caenParams.enskim[i] = 0;
|
||||
m_caenParams.eskimlld[i] = 0;
|
||||
m_caenParams.eskimuld[i] = 0;
|
||||
m_caenParams.blrclip[i] = 0;
|
||||
m_caenParams.dcomp[i] = 0;
|
||||
m_caenParams.trapbsl[i] = 0;
|
||||
m_caenParams.otrej[i] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Something not immediately clear... there is no GetDPPParameters function
|
||||
I assume that if a value is passed that is invalid (which we know can happen) the value that is used by
|
||||
the digitizer is written to the data at the pointer passed to the SetDPPParameters function...
|
||||
I proceed under this assumption
|
||||
*/
|
||||
m_args.status |= CAEN_DGTZ_SetDPPParameters(m_args.handle, m_digitizerParams.channelMask, &m_caenParams);
|
||||
|
||||
//Retrieve corrected value for the channel parameters
|
||||
for (std::size_t i = 0; i < m_channelParams.size(); i++)
|
||||
{
|
||||
if (!m_channelParams[i].isEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
m_channelParams[i].decayTimeConst = m_caenParams.M[i] * m_samplingTime;
|
||||
m_channelParams[i].trapFlatTop = m_caenParams.m[i] * m_samplingTime;
|
||||
m_channelParams[i].trapRiseTime = m_caenParams.k[i] * m_samplingTime;
|
||||
m_channelParams[i].flatTopDelay = m_caenParams.ftd[i] * m_samplingTime;
|
||||
m_channelParams[i].triggerFilterSmoothing = m_caenParams.a[i];
|
||||
m_channelParams[i].inputRiseTime = m_caenParams.b[i] * m_samplingTime;
|
||||
m_channelParams[i].triggerThreshold = m_caenParams.thr[i];
|
||||
m_channelParams[i].samplesBaseLineMean = m_caenParams.nsbl[i];
|
||||
m_channelParams[i].samplesPeakMean = m_caenParams.nspk[i];
|
||||
m_channelParams[i].peakHoldOff = m_caenParams.pkho[i] * m_samplingTime;
|
||||
m_channelParams[i].baseLineHoldOff = m_caenParams.blho[i] * m_samplingTime;
|
||||
m_channelParams[i].triggerHoldOff = m_caenParams.trgho[i] * m_samplingTime;
|
||||
m_channelParams[i].riseTimeValidationWindow = m_caenParams.twwdt[i] * m_samplingTime;
|
||||
m_channelParams[i].riseTimeDiscrimination = m_caenParams.trgwin[i] * m_samplingTime;
|
||||
m_channelParams[i].digitalProbeGain = m_caenParams.dgain[i];
|
||||
m_channelParams[i].energyNormalizationFactor = m_caenParams.enf[i];
|
||||
m_channelParams[i].inputDecimation = m_caenParams.decimation[i];
|
||||
}
|
||||
}
|
||||
|
||||
void DigitizerPHA::LoadWaveformParameters()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_1, m_waveParams.analogProbe1);
|
||||
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_2, m_waveParams.analogProbe2);
|
||||
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, DIGITAL_TRACE_1, m_waveParams.digitalProbe1);
|
||||
}
|
||||
|
||||
//Note: Even though function signatures of MallocDPPEvents and MallocDPPWaveforms are identical
|
||||
//they behave quite differently. MallocDPPEvents allocates the entire channel-buffer matrix in one call,
|
||||
//while MallocDPPWaveforms allocates only a single buffer for a single channel at a time.
|
||||
//This is why void* are dangerous!
|
||||
void DigitizerPHA::AllocateMemory()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_MallocReadoutBuffer(m_args.handle, &m_lowBuffer, &m_lowBufferSize);
|
||||
//void casts are soooo bad .... but required by CAEN API
|
||||
m_args.status |= CAEN_DGTZ_MallocDPPEvents(m_args.handle, (void**)(m_eventData), &m_eventBufferSize);
|
||||
for(int channel=0; channel<m_internalData.Channels; channel++)
|
||||
m_args.status |= CAEN_DGTZ_MallocDPPWaveforms(m_args.handle, (void**)(&m_waveData[channel]), &m_waveBufferSize);
|
||||
}
|
||||
|
||||
void DigitizerPHA::DeallocateMemory()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_FreeReadoutBuffer(&m_lowBuffer);
|
||||
m_args.status |= CAEN_DGTZ_FreeDPPEvents(m_args.handle, (void**)(m_eventData));
|
||||
for(int i=0; i<m_internalData.Channels; i++)
|
||||
m_args.status |= CAEN_DGTZ_FreeDPPWaveforms(m_args.handle, (void*)(m_waveData[i]));
|
||||
}
|
||||
|
||||
void DigitizerPHA::ReadData(std::vector<DYData>& buffer)
|
||||
{
|
||||
if(!m_isActive || !m_isConnected)
|
||||
return;
|
||||
|
||||
m_args.status |= CAEN_DGTZ_ReadData(m_args.handle, CAEN_DGTZ_SLAVE_TERMINATED_READOUT_MBLT, m_lowBuffer, &m_lowBufferSize);
|
||||
if (m_lowBufferSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_args.status |= CAEN_DGTZ_GetDPPEvents(m_args.handle, m_lowBuffer, m_lowBufferSize, (void**)(&m_eventData), m_eventCountsPerChannel);
|
||||
size_t waveSize;
|
||||
DYData tempData;
|
||||
tempData.board = m_args.handle;
|
||||
for(int i=0; i<m_internalData.Channels; i++)
|
||||
{
|
||||
tempData.channel = i;
|
||||
//Increment scalars
|
||||
m_scalars[i]->value += m_eventCountsPerChannel[i];
|
||||
for(int j=0; j<m_eventCountsPerChannel[i]; j++)
|
||||
{
|
||||
tempData.energy = m_eventData[i][j].Energy;
|
||||
tempData.timestamp = m_eventData[i][j].TimeTag;
|
||||
tempData.flags = m_eventData[i][j].Extras;
|
||||
|
||||
if(m_digitizerParams.dppAcqMode != CAEN_DGTZ_DPP_ACQ_MODE_List)
|
||||
{
|
||||
CAEN_DGTZ_DecodeDPPWaveforms(m_args.handle, (void*)&(m_eventData[i][j]), m_waveData);
|
||||
tempData.waveSize = m_waveData[i]->Ns;
|
||||
waveSize = m_waveData[i]->Ns;
|
||||
if(waveSize != 0)
|
||||
{
|
||||
//Copy the data to our vectors PHA supports 2 analog traces and 2 digital traces
|
||||
tempData.trace1Samples.assign(m_waveData[i]->Trace1, m_waveData[i]->Trace1 + waveSize);
|
||||
tempData.trace2Samples.assign(m_waveData[i]->Trace2, m_waveData[i]->Trace2 + waveSize); //This is all zero if in single analog trace mode
|
||||
tempData.digitalTrace1Samples.assign(m_waveData[i]->DTrace1, m_waveData[i]->DTrace1 + waveSize);
|
||||
tempData.digitalTrace2Samples.assign(m_waveData[i]->DTrace2, m_waveData[i]->DTrace2 + waveSize);
|
||||
}
|
||||
}
|
||||
buffer.push_back(tempData);
|
||||
}
|
||||
}
|
||||
}
|
||||
/////////////////////// DigitizerPHA ///////////////////////
|
||||
|
||||
/////////////////////// DigitizerPSD ///////////////////////
|
||||
DigitizerPSD::DigitizerPSD(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int code) :
|
||||
Digitizer(), m_eventData(nullptr), m_waveData(nullptr)
|
||||
{
|
||||
Init(args, info, code);
|
||||
}
|
||||
|
||||
DigitizerPSD::~DigitizerPSD() { Close(); }
|
||||
|
||||
void DigitizerPSD::Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec)
|
||||
{
|
||||
m_args = args;
|
||||
m_internalData = info;
|
||||
|
||||
m_args.status = ec;
|
||||
if(info.Model == 1730)
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1730;
|
||||
else if(info.Model == 1725)
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1725;
|
||||
else if(info.Model == 1740)
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_V1740;
|
||||
else
|
||||
m_args.model = CAEN_DGTZ_BoardModel_t::CAEN_DGTZ_DT5720;
|
||||
m_args.name = info.ModelName + std::to_string(info.SerialNumber);
|
||||
m_args.firmware = CAEN_DGTZ_DPPFirmware_PSD;
|
||||
m_args.channels = info.Channels;
|
||||
|
||||
m_channelParams.resize(info.Channels);
|
||||
m_eventCountsPerChannel = new uint32_t[info.Channels];
|
||||
m_eventData = new CAEN_DGTZ_DPP_PSD_Event_t*[info.Channels];
|
||||
m_waveData = new CAEN_DGTZ_DPP_PSD_Waveforms_t*[info.Channels];
|
||||
LoadDigitizerParameters();
|
||||
LoadChannelParameters();
|
||||
//Must load default parameters here to generate a buffer
|
||||
AllocateMemory(); //More specifically: CAEN memory
|
||||
|
||||
for (int i = 0; i < info.Channels; i++)
|
||||
{
|
||||
m_scalars.push_back(ScalarDistributor::Bind(m_args.name + std::to_string(i)));
|
||||
}
|
||||
|
||||
m_samplingTime = GetSamplingPeriod(m_args.model);
|
||||
|
||||
m_isConnected = true;
|
||||
}
|
||||
|
||||
void DigitizerPSD::Close()
|
||||
{
|
||||
StopAquisition(); //Stop aquisition if needed
|
||||
|
||||
DeallocateMemory();
|
||||
|
||||
delete[] m_eventCountsPerChannel;
|
||||
|
||||
m_lowBuffer = nullptr;
|
||||
m_eventData = nullptr;
|
||||
m_eventCountsPerChannel = nullptr;
|
||||
m_waveData = nullptr;
|
||||
|
||||
m_isConnected = false;
|
||||
|
||||
//Explicitly wipe-out scalars
|
||||
for (auto& scalar : m_scalars)
|
||||
{
|
||||
ScalarDistributor::UnBind(scalar->name);
|
||||
}
|
||||
m_scalars.clear();
|
||||
}
|
||||
|
||||
void DigitizerPSD::SetDigitizerParameters(const DigitizerParameters& params)
|
||||
{
|
||||
if(!m_isConnected || m_isActive)
|
||||
return;
|
||||
|
||||
m_digitizerParams = params;
|
||||
}
|
||||
|
||||
void DigitizerPSD::InternalSetChannelParameters(const std::vector<PSDParameters>& params)
|
||||
{
|
||||
if(!m_isConnected || m_isActive)
|
||||
return;
|
||||
|
||||
m_channelParams = params;
|
||||
}
|
||||
|
||||
void DigitizerPSD::InternalSetWaveformParameters(const PSDWaveParameters& params)
|
||||
{
|
||||
if(!m_isConnected || m_isActive)
|
||||
return;
|
||||
|
||||
m_waveParams = params;
|
||||
}
|
||||
|
||||
//This cannot possibly be the correct method for cases where digitizers are chained.
|
||||
//July 2022: Is not. See Synchronize.h/.cpp
|
||||
void DigitizerPSD::StartAquisition()
|
||||
{
|
||||
if(!m_isActive && m_isConnected)
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SWStartAcquisition(m_args.handle);
|
||||
m_isActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
//This cannot possibly be the correct method for cases where digitizers are chained.
|
||||
//July 2022: Is not. See Synchronize.h/.cpp
|
||||
void DigitizerPSD::StopAquisition()
|
||||
{
|
||||
if(m_isActive && m_isConnected)
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SWStopAcquisition(m_args.handle);
|
||||
m_isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitizerPSD::LoadSettings()
|
||||
{
|
||||
DeallocateMemory();
|
||||
|
||||
LoadDigitizerParameters();
|
||||
LoadChannelParameters();
|
||||
LoadWaveformParameters();
|
||||
|
||||
AllocateMemory();
|
||||
}
|
||||
|
||||
//Set digitizer wide parameters
|
||||
void DigitizerPSD::LoadDigitizerParameters()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SetAcquisitionMode(m_args.handle, m_digitizerParams.acqMode);
|
||||
m_args.status |= CAEN_DGTZ_SetDPPAcquisitionMode(m_args.handle, m_digitizerParams.dppAcqMode, m_digitizerParams.dppSaveMode); //why would you ever not want one of these?
|
||||
m_args.status |= CAEN_DGTZ_SetRecordLength(m_args.handle, m_digitizerParams.recordLength / m_samplingTime);
|
||||
m_args.status |= CAEN_DGTZ_SetIOLevel(m_args.handle, m_digitizerParams.IOlevel);
|
||||
|
||||
m_args.status |= CAEN_DGTZ_SetExtTriggerInputMode(m_args.handle, m_digitizerParams.triggerMode);
|
||||
m_args.status |= CAEN_DGTZ_SetDPPEventAggregation(m_args.handle, m_digitizerParams.eventAggr, 0);
|
||||
m_args.status |= CAEN_DGTZ_SetRunSynchronizationMode(m_args.handle, m_digitizerParams.syncMode);
|
||||
m_args.status |= CAEN_DGTZ_SetChannelEnableMask(m_args.handle, m_digitizerParams.channelMask);
|
||||
}
|
||||
|
||||
//Set per channel data
|
||||
void DigitizerPSD::LoadChannelParameters()
|
||||
{
|
||||
m_digitizerParams.channelMask = 0;
|
||||
for(std::size_t i=0; i<m_channelParams.size(); i++)
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SetChannelDCOffset(m_args.handle, i, uint32_t(0xffff * m_channelParams[i].dcOffset)); //Max range is 0xffff
|
||||
m_args.status |= CAEN_DGTZ_SetDPPPreTriggerSize(m_args.handle, i, m_channelParams[i].preTriggerTime / m_samplingTime);
|
||||
m_args.status |= CAEN_DGTZ_SetChannelPulsePolarity(m_args.handle, i, m_channelParams[i].pulsePolarity);
|
||||
m_args.status |= CAEN_DGTZ_WriteRegister(m_args.handle, 0x1028 + (i<<8), m_channelParams[i].dynamicRange);
|
||||
|
||||
if(m_channelParams[i].isEnabled)
|
||||
{
|
||||
m_digitizerParams.channelMask |= (BIT(i)); //flip channel bit to 1 scince its enabled
|
||||
}
|
||||
|
||||
//Write data to garbage CAEN style structs
|
||||
m_caenParams.thr[i] = m_channelParams[i].triggerThreshold;
|
||||
m_caenParams.selft[i] = m_channelParams[i].selfTrigger;
|
||||
m_caenParams.csens[i] = m_channelParams[i].chargeSensitivity;
|
||||
m_caenParams.sgate[i] = m_channelParams[i].shortGate / m_samplingTime;
|
||||
m_caenParams.lgate[i] = m_channelParams[i].longGate / m_samplingTime;
|
||||
m_caenParams.pgate[i] = m_channelParams[i].preGate / m_samplingTime;
|
||||
m_caenParams.tvaw[i] = m_channelParams[i].triggerValidationWindow / m_samplingTime;
|
||||
m_caenParams.nsbl[i] = m_channelParams[i].samplesBasline;
|
||||
m_caenParams.discr[i] = m_channelParams[i].discrminatorType;
|
||||
m_caenParams.cfdf[i] = m_channelParams[i].cfdFraction;
|
||||
m_caenParams.cfdd[i] = m_channelParams[i].cfdDelay / m_samplingTime;
|
||||
m_caenParams.trgc[i] = (CAEN_DGTZ_DPP_TriggerConfig_t)1; // Deprecated, must be set to one according to docs
|
||||
}
|
||||
//These are like global digitizer params but PSD specific. Here we treat first channel as "global" setting (similar to CoMPASS)
|
||||
|
||||
|
||||
/*
|
||||
Something not immediately clear... there is no GetDPPParameters function
|
||||
I assume that if a value is passed that is invalid (which we know can happen) the value that is used by
|
||||
the digitizer is written to the data at the pointer passed to the SetDPPParameters function...
|
||||
I proceed under this assumption
|
||||
*/
|
||||
m_args.status |= CAEN_DGTZ_SetDPPParameters(m_args.handle, m_digitizerParams.channelMask, &m_caenParams);
|
||||
|
||||
//Retrieve corrected value for the channel parameters
|
||||
for (std::size_t i = 0; i < m_channelParams.size(); i++)
|
||||
{
|
||||
if (!m_channelParams[i].isEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_channelParams[i].triggerThreshold = m_caenParams.thr[i];
|
||||
m_channelParams[i].selfTrigger = m_caenParams.selft[i];
|
||||
m_channelParams[i].chargeSensitivity = m_caenParams.csens[i];
|
||||
m_channelParams[i].shortGate = m_caenParams.sgate[i] * m_samplingTime;
|
||||
m_channelParams[i].longGate = m_caenParams.lgate[i] * m_samplingTime;
|
||||
m_channelParams[i].preGate = m_caenParams.pgate[i] * m_samplingTime;
|
||||
m_channelParams[i].triggerValidationWindow = m_caenParams.tvaw[i] * m_samplingTime;
|
||||
m_channelParams[i].samplesBasline = m_caenParams.nsbl[i];
|
||||
m_channelParams[i].discrminatorType = m_caenParams.discr[i];
|
||||
m_channelParams[i].cfdFraction = m_caenParams.cfdf[i];
|
||||
m_channelParams[i].cfdDelay = m_caenParams.cfdd[i] * m_samplingTime;
|
||||
}
|
||||
m_channelParams[0].baselineThreshold = m_caenParams.blthr;
|
||||
m_channelParams[0].bltmo = m_caenParams.bltmo;
|
||||
m_channelParams[0].triggerHoldOff = m_caenParams.trgho;
|
||||
m_channelParams[0].pileUpRejection = m_caenParams.purh;
|
||||
m_channelParams[0].purgap = m_caenParams.purgap;
|
||||
}
|
||||
|
||||
void DigitizerPSD::LoadWaveformParameters()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_1, m_waveParams.analogProbe1);
|
||||
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, ANALOG_TRACE_2, m_waveParams.analogProbe2);
|
||||
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, DIGITAL_TRACE_1, m_waveParams.digitalProbe1);
|
||||
m_args.status |= CAEN_DGTZ_SetDPP_VirtualProbe(m_args.handle, DIGITAL_TRACE_2, m_waveParams.digitalProbe2);
|
||||
}
|
||||
|
||||
//Note: Even though function signatures of MallocDPPEvents and MallocDPPWaveforms are identical
|
||||
//they behave quite differently. MallocDPPEvents allocates the entire channel-buffer matrix in one call,
|
||||
//while MallocDPPWaveforms allocates only a single buffer for a single channel at a time.
|
||||
//This is why void* are dangerous!
|
||||
void DigitizerPSD::AllocateMemory()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_MallocReadoutBuffer(m_args.handle, &m_lowBuffer, &m_lowBufferSize);
|
||||
//void casts are soooo bad .... but required by CAEN API
|
||||
m_args.status |= CAEN_DGTZ_MallocDPPEvents(m_args.handle, (void**)(m_eventData), &m_eventBufferSize);
|
||||
for(int channel=0; channel<m_internalData.Channels; channel++)
|
||||
m_args.status |= CAEN_DGTZ_MallocDPPWaveforms(m_args.handle, (void**)(&m_waveData[channel]), &m_waveBufferSize);
|
||||
}
|
||||
|
||||
void DigitizerPSD::DeallocateMemory()
|
||||
{
|
||||
m_args.status |= CAEN_DGTZ_FreeReadoutBuffer(&m_lowBuffer);
|
||||
m_args.status |= CAEN_DGTZ_FreeDPPEvents(m_args.handle, (void**)(m_eventData));
|
||||
for(int i=0; i<m_internalData.Channels; i++)
|
||||
m_args.status |= CAEN_DGTZ_FreeDPPWaveforms(m_args.handle, (void*)(m_waveData[i]));
|
||||
}
|
||||
|
||||
void DigitizerPSD::ReadData(std::vector<DYData>& buffer)
|
||||
{
|
||||
if (!m_isActive || !m_isConnected)
|
||||
return;
|
||||
|
||||
m_args.status |= CAEN_DGTZ_ReadData(m_args.handle, CAEN_DGTZ_SLAVE_TERMINATED_READOUT_MBLT, m_lowBuffer, &m_lowBufferSize);
|
||||
if (m_lowBufferSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_args.status |= CAEN_DGTZ_GetDPPEvents(m_args.handle, m_lowBuffer, m_lowBufferSize, (void**)(&m_eventData), m_eventCountsPerChannel);
|
||||
size_t waveSize;
|
||||
DYData tempData;
|
||||
tempData.board = m_args.handle;
|
||||
for(int i=0; i<m_internalData.Channels; i++)
|
||||
{
|
||||
tempData.channel = i;
|
||||
//Increment scalars
|
||||
m_scalars[i]->value += m_eventCountsPerChannel[i];
|
||||
for(int j=0; j<m_eventCountsPerChannel[i]; j++)
|
||||
{
|
||||
tempData.energy = m_eventData[i][j].ChargeLong;
|
||||
tempData.energyShort = m_eventData[i][j].ChargeShort;
|
||||
tempData.timestamp = m_eventData[i][j].TimeTag;
|
||||
tempData.flags = m_eventData[i][j].Extras;
|
||||
|
||||
if(m_digitizerParams.dppAcqMode != CAEN_DGTZ_DPP_ACQ_MODE_List)
|
||||
{
|
||||
CAEN_DGTZ_DecodeDPPWaveforms(m_args.handle, (void*)&(m_eventData[i][j]), m_waveData);
|
||||
tempData.waveSize = m_waveData[i]->Ns;
|
||||
waveSize = m_waveData[i]->Ns;
|
||||
if(tempData.waveSize != 0)
|
||||
{
|
||||
//Copy the data to our vectors PHA supports 2 analog traces and 2 digital traces
|
||||
tempData.trace1Samples.assign(m_waveData[i]->Trace1, m_waveData[i]->Trace1 + waveSize);
|
||||
tempData.trace2Samples.assign(m_waveData[i]->Trace2, m_waveData[i]->Trace2 + waveSize); //This is all zero if in single analog trace mode
|
||||
tempData.digitalTrace1Samples.assign(m_waveData[i]->DTrace1, m_waveData[i]->DTrace1 + waveSize);
|
||||
tempData.digitalTrace2Samples.assign(m_waveData[i]->DTrace2, m_waveData[i]->DTrace2 + waveSize);
|
||||
}
|
||||
}
|
||||
buffer.push_back(tempData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
231
src/DAQ/Digitizer.h
Normal file
231
src/DAQ/Digitizer.h
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
Digitizer.h
|
||||
Abstract base class for CAEN digitizer
|
||||
|
||||
GWM -- June 2022
|
||||
*/
|
||||
#ifndef DIGITIZER_H
|
||||
#define DIGITIZER_H
|
||||
|
||||
#include "DigitizerDefs.h"
|
||||
#include "Core/ScalarDistributor.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//Abstract digitizer base class
|
||||
class Digitizer
|
||||
{
|
||||
public:
|
||||
|
||||
using Ref = std::shared_ptr<Digitizer>;
|
||||
|
||||
Digitizer() :
|
||||
m_isActive(false), m_isConnected(false), m_lowBuffer(NULL), m_lowBufferSize(0), m_eventBufferSize(0),
|
||||
m_eventCountsPerChannel(nullptr), m_waveBufferSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Digitizer() { delete[] m_lowBuffer; }
|
||||
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual void StartAquisition() = 0;
|
||||
virtual void StopAquisition() = 0;
|
||||
|
||||
virtual void SetDigitizerParameters(const DigitizerParameters& params) = 0;
|
||||
|
||||
/*
|
||||
These functions look scary but really aren't. We provide a public interface that accepts any type of argument, such that the user can have a digitizer
|
||||
and call digitizer->SetChannelParamters(params)/SetWaveformParameters(params) for any valid parameter type without having to cast pointers around.
|
||||
The handling of type cases is done by the InternalSetChannel/WaveformParamters functions, which are overloaded for each case. The default behavior of
|
||||
each setter should be to print a warning telling the user that the parameters were invalid. Then each specific derived class overrides it's specific internal
|
||||
setter and properly handles the parameters. Biggest advantage is that as new digitizer firmwares are added to the code base, the public interface basically
|
||||
doesn't change.
|
||||
|
||||
This also has the advantage of implicit error handling. If the user attempts to write code for which no internal setter exists, the compiler will complain.
|
||||
*/
|
||||
template<typename T>
|
||||
void SetChannelParameters(const T& params)
|
||||
{
|
||||
InternalSetChannelParameters(params);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SetWaveformParameters(const T& params)
|
||||
{
|
||||
InternalSetWaveformParameters(params);
|
||||
}
|
||||
|
||||
/*
|
||||
Similar to the setters, we have getters which allow for a simple access interface. The argument, paramBuffer, is the returned value of the state of the parameters.
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
void GetChannelParameters(T& paramBuffer) const
|
||||
{
|
||||
InternalGetChannelParameters(paramBuffer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void GetWaveformParameters(T& paramBuffer) const
|
||||
{
|
||||
InternalGetWaveformParameters(paramBuffer);
|
||||
}
|
||||
|
||||
virtual void LoadSettings() = 0;
|
||||
virtual void ReadData(std::vector<DYData>& buffer) = 0;
|
||||
|
||||
const DigitizerParameters& GetDigitizerParameters() const { return m_digitizerParams; }
|
||||
const DigitizerArgs& GetDigitizerArgs() const { return m_args; }
|
||||
const bool IsActive() const { return m_isActive; }
|
||||
const bool IsConnected() const { return m_isConnected; }
|
||||
|
||||
protected:
|
||||
virtual void Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) = 0;
|
||||
|
||||
/*
|
||||
Functions which actually set parameters. Should be an overload for each valid type. By default should always print an incompatibility warning,
|
||||
and then be overloaded by the correct derived class to actually set parameters.
|
||||
*/
|
||||
virtual void InternalSetChannelParameters(const std::vector<PHAParameters>& params)
|
||||
{
|
||||
|
||||
("Attempting to set PHA parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
virtual void InternalSetChannelParameters(const std::vector<PSDParameters>& params)
|
||||
{
|
||||
DY_WARN("Attempting to set PSD parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
virtual void InternalSetWaveformParameters(const PHAWaveParameters& params)
|
||||
{
|
||||
DY_WARN("Attempting to set PHA wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
virtual void InternalSetWaveformParameters(const PSDWaveParameters& params)
|
||||
{
|
||||
DY_WARN("Attempting to set PSD wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
|
||||
virtual void InternalGetChannelParameters(std::vector<PHAParameters>& paramBuffer) const
|
||||
{
|
||||
DY_WARN("Attempting to get PHA parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
virtual void InternalGetChannelParameters(std::vector<PSDParameters>& paramBuffer) const
|
||||
{
|
||||
DY_WARN("Attempting to get PSD parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
virtual void InternalGetWaveformParameters(PHAWaveParameters& paramBuffer) const
|
||||
{
|
||||
DY_WARN("Attempting to get PHA wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
virtual void InternalGetWaveformParameters(PSDWaveParameters& paramBuffer) const
|
||||
{
|
||||
DY_WARN("Attempting to get PSD wave parameters for unsupported digitizer firmware {0}", FirmwareTypeToString(m_args.firmware));
|
||||
}
|
||||
|
||||
DigitizerArgs m_args;
|
||||
DigitizerParameters m_digitizerParams;
|
||||
|
||||
bool m_isActive;
|
||||
bool m_isConnected;
|
||||
|
||||
char* m_lowBuffer;
|
||||
uint32_t m_lowBufferSize;
|
||||
|
||||
uint32_t m_eventBufferSize;
|
||||
uint32_t* m_eventCountsPerChannel;
|
||||
uint32_t m_waveBufferSize;
|
||||
|
||||
std::vector<Scalar::Ref> m_scalars; //Count rates for each channel. Indexed by channel number
|
||||
|
||||
double m_samplingTime; //Use this to convert from our UI friendly time parameters to the digitizer-friendly sample based parameters
|
||||
|
||||
CAEN_DGTZ_BoardInfo_t m_internalData; //internal use only
|
||||
};
|
||||
|
||||
//Creation function
|
||||
Digitizer::Ref OpenDigitizer(DigitizerArgs& args);
|
||||
|
||||
//Digitizers are better sliced by Firmware than by model. A lot of boards support multpile firmwares.
|
||||
|
||||
class DigitizerPHA : public Digitizer
|
||||
{
|
||||
public:
|
||||
DigitizerPHA(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec);
|
||||
virtual ~DigitizerPHA();
|
||||
|
||||
virtual void Close() override;
|
||||
|
||||
virtual void StartAquisition() override;
|
||||
virtual void StopAquisition() override;
|
||||
|
||||
virtual void LoadSettings() override;
|
||||
|
||||
virtual void SetDigitizerParameters(const DigitizerParameters& params) override;
|
||||
virtual void ReadData(std::vector<DYData>& buffer) override;
|
||||
|
||||
private:
|
||||
virtual void Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) override;
|
||||
|
||||
virtual void InternalSetChannelParameters(const std::vector<PHAParameters>& params) override;
|
||||
virtual void InternalSetWaveformParameters(const PHAWaveParameters& params) override;
|
||||
virtual void InternalGetChannelParameters(std::vector<PHAParameters>& paramBuffer) const override { paramBuffer = m_channelParams; }
|
||||
virtual void InternalGetWaveformParameters(PHAWaveParameters& paramBuffer) const override { paramBuffer = m_waveParams; }
|
||||
|
||||
void LoadDigitizerParameters();
|
||||
void LoadChannelParameters();
|
||||
void LoadWaveformParameters();
|
||||
void AllocateMemory();
|
||||
void DeallocateMemory();
|
||||
|
||||
//CAEN required data storage, does not interface to other parts of the program
|
||||
CAEN_DGTZ_DPP_PHA_Event_t** m_eventData;
|
||||
CAEN_DGTZ_DPP_PHA_Waveforms_t** m_waveData;
|
||||
CAEN_DGTZ_DPP_PHA_Params_t m_caenParams;
|
||||
|
||||
std::vector<PHAParameters> m_channelParams;
|
||||
PHAWaveParameters m_waveParams;
|
||||
};
|
||||
|
||||
class DigitizerPSD : public Digitizer
|
||||
{
|
||||
public:
|
||||
DigitizerPSD(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec);
|
||||
virtual ~DigitizerPSD();
|
||||
|
||||
virtual void Close() override;
|
||||
|
||||
virtual void StartAquisition() override;
|
||||
virtual void StopAquisition() override;
|
||||
|
||||
virtual void LoadSettings() override;
|
||||
|
||||
virtual void SetDigitizerParameters(const DigitizerParameters& params) override;
|
||||
virtual void ReadData(std::vector<DYData>& buffer) override;
|
||||
|
||||
private:
|
||||
virtual void Init(const DigitizerArgs& args, const CAEN_DGTZ_BoardInfo_t& info, int ec) override;
|
||||
|
||||
virtual void InternalSetChannelParameters(const std::vector<PSDParameters>& params) override;
|
||||
virtual void InternalSetWaveformParameters(const PSDWaveParameters& params) override;
|
||||
virtual void InternalGetChannelParameters(std::vector<PSDParameters>& paramBuffer) const override { paramBuffer = m_channelParams; }
|
||||
virtual void InternalGetWaveformParameters(PSDWaveParameters& paramBuffer) const override { paramBuffer = m_waveParams; }
|
||||
|
||||
void LoadDigitizerParameters();
|
||||
void LoadChannelParameters();
|
||||
void LoadWaveformParameters();
|
||||
void AllocateMemory();
|
||||
void DeallocateMemory();
|
||||
|
||||
//CAEN required data storage, does not interface to other parts of the program
|
||||
CAEN_DGTZ_DPP_PSD_Event_t** m_eventData;
|
||||
CAEN_DGTZ_DPP_PSD_Waveforms_t** m_waveData;
|
||||
CAEN_DGTZ_DPP_PSD_Params_t m_caenParams;
|
||||
|
||||
std::vector<PSDParameters> m_channelParams;
|
||||
PSDWaveParameters m_waveParams;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
210
src/DAQ/DigitizerChain.cpp
Normal file
210
src/DAQ/DigitizerChain.cpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
#include "DigitizerChain.h"
|
||||
#include "CAENDigitizer.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
static bool SortByHandle(const Digitizer::Ref& digi1, const Digitizer::Ref& digi2)
|
||||
{
|
||||
return digi1->GetDigitizerArgs().handle < digi2->GetDigitizerArgs().handle;
|
||||
}
|
||||
|
||||
DigitizerChain::DigitizerChain()
|
||||
{
|
||||
}
|
||||
|
||||
DigitizerChain::~DigitizerChain()
|
||||
{
|
||||
}
|
||||
|
||||
void DigitizerChain::DetectBoards()
|
||||
{
|
||||
m_chain.clear();
|
||||
|
||||
static int nLinks = 4;
|
||||
static int nNodes = 8;
|
||||
|
||||
DigitizerArgs args;
|
||||
for (int link = 0; link < nLinks; link++)
|
||||
{
|
||||
for (int node = 0; node < nNodes; node++)
|
||||
{
|
||||
args = DigitizerArgs();
|
||||
args.conetNode = node;
|
||||
args.linkNumber = link;
|
||||
Digitizer::Ref digi = OpenDigitizer(args);
|
||||
if (digi != nullptr)
|
||||
{
|
||||
args = digi->GetDigitizerArgs();
|
||||
m_chain.push_back(digi);
|
||||
DY_INFO("Found digitizer named {0} at link {1} on node {2}", args.name, args.linkNumber, args.conetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (size() == 0)
|
||||
|
||||
("No digitizers found... check to see that they are on and connected to the system via optical link");
|
||||
else
|
||||
std::sort(begin(), end(), SortByHandle); //in general seems to not be necessary, but just to be safe
|
||||
}
|
||||
|
||||
void DigitizerChain::DisconnectBoards()
|
||||
{
|
||||
//Maybe someday more complicated stuff here?
|
||||
clear();
|
||||
}
|
||||
|
||||
void DigitizerChain::SetDPPAcqMode(DPPAcqMode mode)
|
||||
{
|
||||
CAEN_DGTZ_DPP_AcqMode_t modeIn;
|
||||
CAEN_DGTZ_DPP_AcqMode_t modeOut;
|
||||
CAEN_DGTZ_DPP_SaveParam_t saveIn = CAEN_DGTZ_DPP_SAVE_PARAM_EnergyAndTime;
|
||||
CAEN_DGTZ_DPP_SaveParam_t saveOut;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case DPPAcqMode::List: modeIn = CAEN_DGTZ_DPP_ACQ_MODE_List; break;
|
||||
case DPPAcqMode::Waves: modeIn = CAEN_DGTZ_DPP_ACQ_MODE_Oscilloscope; break;
|
||||
case DPPAcqMode::None: DY_WARN("Attempted to set DPPAcqMode::None!"); return;
|
||||
}
|
||||
|
||||
int ec = 0;
|
||||
for (auto& digitizer : m_chain)
|
||||
{
|
||||
ec |= CAEN_DGTZ_SetDPPAcquisitionMode(digitizer->GetDigitizerArgs().handle, modeIn, saveIn);
|
||||
ec |= CAEN_DGTZ_GetDPPAcquisitionMode(digitizer->GetDigitizerArgs().handle, &modeOut, &saveOut);
|
||||
DigitizerParameters params = digitizer->GetDigitizerParameters();
|
||||
params.dppAcqMode = modeOut;
|
||||
params.dppSaveMode = saveOut;
|
||||
digitizer->SetDigitizerParameters(params);
|
||||
}
|
||||
if (ec != 0)
|
||||
{
|
||||
DY_ERROR("Error code {0} recieved from DigitizerChain::SetDPPAcqMode!", ec);
|
||||
}
|
||||
}
|
||||
|
||||
void DigitizerChain::SynchronizeBoards(const SyncArgs& args)
|
||||
{
|
||||
if (size() <= 1)
|
||||
{
|
||||
DY_WARN("Cannot synchronize digitizer chain of size {0}", size());
|
||||
return;
|
||||
}
|
||||
|
||||
CAEN_DGTZ_RunSyncMode_t value = CAEN_DGTZ_RUN_SYNC_Disabled;
|
||||
CAEN_DGTZ_RunSyncMode_t setSyncValue;
|
||||
CAEN_DGTZ_AcqMode_t setAcqMode;
|
||||
|
||||
switch (args.syncMethod)
|
||||
{
|
||||
case SyncMethod::SIn_TrigOut: value = CAEN_DGTZ_RUN_SYNC_TrgOutSinDaisyChain; break;
|
||||
case SyncMethod::None: value = CAEN_DGTZ_RUN_SYNC_Disabled; break;
|
||||
}
|
||||
|
||||
int ec = 0;
|
||||
for (auto& digitizer : m_chain)
|
||||
{
|
||||
ec |= CAEN_DGTZ_SetRunSynchronizationMode(digitizer->GetDigitizerArgs().handle, value);
|
||||
ec |= CAEN_DGTZ_GetRunSynchronizationMode(digitizer->GetDigitizerArgs().handle, &setSyncValue);
|
||||
ec |= CAEN_DGTZ_GetAcquisitionMode(digitizer->GetDigitizerArgs().handle, &setAcqMode);
|
||||
auto params = digitizer->GetDigitizerParameters();
|
||||
params.syncMode = setSyncValue;
|
||||
params.acqMode = setAcqMode;
|
||||
digitizer->SetDigitizerParameters(params);
|
||||
}
|
||||
if (ec != 0)
|
||||
{
|
||||
DY_ERROR("Error code {0} recieved from DigitizerChain::SynchronizeBoards!", ec);
|
||||
}
|
||||
}
|
||||
|
||||
bool DigitizerChain::Start()
|
||||
{
|
||||
if (size() == 0)
|
||||
{
|
||||
DY_ERROR("Cannot run data aquisition without any digitizers!");
|
||||
return false;
|
||||
}
|
||||
else if (size() == 1)
|
||||
{
|
||||
m_chain[0]->StartAquisition();
|
||||
if (!m_chain[0]->IsActive())
|
||||
{
|
||||
DY_ERROR("Unable to start single digitizer config!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return StartSynchronizedRun();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DigitizerChain::Stop()
|
||||
{
|
||||
if (size() == 0)
|
||||
{
|
||||
DY_ERROR("How the hell did you get here?");
|
||||
return;
|
||||
}
|
||||
else if (size() == 1)
|
||||
{
|
||||
m_chain[0]->StopAquisition();
|
||||
if (m_chain[0]->IsActive())
|
||||
{
|
||||
DY_ERROR("Single digitizer stop failed...");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
StopSynchronizedRun();
|
||||
}
|
||||
|
||||
bool DigitizerChain::StartSynchronizedRun()
|
||||
{
|
||||
static uint32_t softwareTrigger = 0x4;
|
||||
static uint32_t hardwareLevelTrigger = 0x5;
|
||||
|
||||
if (m_syncArgs.syncMethod != SyncMethod::SIn_TrigOut)
|
||||
{
|
||||
DY_WARN("Unsupported sync method {0} at StartSynchronizedRun!", SyncMethodToString(m_syncArgs.syncMethod));
|
||||
return false;
|
||||
}
|
||||
|
||||
int error_code = 0;
|
||||
switch (m_syncArgs.startType)
|
||||
{
|
||||
case SyncStartType::HardwareControlled:
|
||||
{
|
||||
error_code |= CAEN_DGTZ_WriteRegister(m_chain[0]->GetDigitizerArgs().handle, CAEN_DGTZ_ACQ_CONTROL_ADD, softwareTrigger);
|
||||
break;
|
||||
}
|
||||
case SyncStartType::SoftwareControlled:
|
||||
{
|
||||
error_code |= CAEN_DGTZ_WriteRegister(m_chain[0]->GetDigitizerArgs().handle, CAEN_DGTZ_ACQ_CONTROL_ADD, hardwareLevelTrigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error_code != 0)
|
||||
{
|
||||
DY_ERROR("Error code {0} recieved from DigitizerChain::StartSynchronizedRun!", error_code);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DigitizerChain::StopSynchronizedRun()
|
||||
{
|
||||
if (m_syncArgs.syncMethod != SyncMethod::SIn_TrigOut)
|
||||
{
|
||||
DY_WARN("Unsupported sync method {0} at StartSynchronizedRun!", SyncMethodToString(m_syncArgs.syncMethod));
|
||||
return;
|
||||
}
|
||||
|
||||
int ec = CAEN_DGTZ_SWStopAcquisition(m_chain[0]->GetDigitizerArgs().handle); //Seems pretty universal, for all methods 0 indicates stop
|
||||
if(ec != 0)
|
||||
DY_ERROR("Error code {0} recieved from DigitizerChain::StopSynchronizedRun!", ec);
|
||||
}
|
||||
}
|
58
src/DAQ/DigitizerChain.h
Normal file
58
src/DAQ/DigitizerChain.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifndef DIGITIZER_CHAIN_H
|
||||
#define DIGITIZER_CHAIN_H
|
||||
|
||||
#include "Digitizer.h"
|
||||
#include "Events/AcqEvent.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
/*
|
||||
Wrapper around std::vector list of digitizers in a setup
|
||||
*/
|
||||
class DigitizerChain
|
||||
{
|
||||
public:
|
||||
DigitizerChain();
|
||||
~DigitizerChain();
|
||||
|
||||
const Digitizer::Ref& operator[](std::size_t index) const
|
||||
{
|
||||
return m_chain[index];
|
||||
}
|
||||
|
||||
Digitizer::Ref& operator[](std::size_t index)
|
||||
{
|
||||
return m_chain[index];
|
||||
}
|
||||
|
||||
|
||||
//Mimic stl
|
||||
std::size_t size() const { return m_chain.size(); }
|
||||
bool empty() const { return m_chain.empty(); }
|
||||
void clear() { m_chain.clear(); }
|
||||
|
||||
void DetectBoards();
|
||||
void DisconnectBoards();
|
||||
|
||||
void SetDPPAcqMode(DPPAcqMode mode);
|
||||
void SynchronizeBoards(const SyncArgs& e);
|
||||
|
||||
bool Start();
|
||||
void Stop();
|
||||
|
||||
std::vector<Digitizer::Ref>::iterator begin() { return m_chain.begin(); }
|
||||
std::vector<Digitizer::Ref>::iterator end() { return m_chain.end(); }
|
||||
|
||||
std::vector<Digitizer::Ref>::const_iterator begin() const { return m_chain.begin(); }
|
||||
std::vector<Digitizer::Ref>::const_iterator end() const { return m_chain.end(); }
|
||||
|
||||
private:
|
||||
bool StartSynchronizedRun();
|
||||
void StopSynchronizedRun();
|
||||
|
||||
std::vector<Digitizer::Ref> m_chain;
|
||||
SyncArgs m_syncArgs;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
291
src/DAQ/DigitizerDefs.cpp
Normal file
291
src/DAQ/DigitizerDefs.cpp
Normal file
|
@ -0,0 +1,291 @@
|
|||
#include "DigitizerDefs.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
std::string BoardIOToString(int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case CAEN_DGTZ_IOLevel_NIM: return "NIM";
|
||||
case CAEN_DGTZ_IOLevel_TTL: return "TTL";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PolarityToString(int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case CAEN_DGTZ_PulsePolarityPositive: return "Positive";
|
||||
case CAEN_DGTZ_PulsePolarityNegative: return "Negative";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string DynamicRangeToString(DynamicRange range)
|
||||
{
|
||||
switch (range)
|
||||
{
|
||||
case DynamicRange::MilliVolt_500: return "0.5V";
|
||||
case DynamicRange::Volt_2: return "2.0V";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
//Simple integer power calc
|
||||
int Power(int base, int order)
|
||||
{
|
||||
int value = 1;
|
||||
for (int i = 1; i <= order; i++)
|
||||
value = value * base;
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string IntSwitchToString(int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0: return "Off";
|
||||
case 1: return "On";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string DiscriminatorModeToString(int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case CAEN_DGTZ_DPP_DISCR_MODE_CFD: return "CFD";
|
||||
case CAEN_DGTZ_DPP_DISCR_MODE_LED: return "Leading-Edge";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string CFDFractionToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case 0: return "0.25";
|
||||
case 1: return "0.5";
|
||||
case 2: return "0.75";
|
||||
case 3: return "1.0";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string TriggerConfigToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case CAEN_DGTZ_DPP_TriggerMode_Normal: return "Normal";
|
||||
case CAEN_DGTZ_DPP_TriggerMode_Coincidence: return "External Coinc.";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PileUpModeToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case CAEN_DGTZ_DPP_PSD_PUR_DetectOnly: return "Off";
|
||||
case CAEN_DGTZ_DPP_PSD_PUR_Enabled: return "On";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
//Requires both dynamic range and charge sens. value (different ranges = different values)
|
||||
std::string ChargeSensToString(int cs_val, DynamicRange range)
|
||||
{
|
||||
if (range == DynamicRange::MilliVolt_500)
|
||||
{
|
||||
switch (cs_val)
|
||||
{
|
||||
case 0: return "1.25 fC/LSB";
|
||||
case 1: return "5 fC/LSB";
|
||||
case 2: return "20 fC/LSB";
|
||||
case 3: return "80 fC/LSB";
|
||||
case 4: return "320 fC/LSB";
|
||||
case 5: return "1.28 pC/LSB";
|
||||
}
|
||||
}
|
||||
else if (range == DynamicRange::Volt_2)
|
||||
{
|
||||
switch (cs_val)
|
||||
{
|
||||
case 0: return "5 fC/LSB";
|
||||
case 1: return "20 fC/LSB";
|
||||
case 2: return "80 fC/LSB";
|
||||
case 3: return "320 fC/LSB";
|
||||
case 4: return "1.28 pC/LSB";
|
||||
case 5: return "5.12 pC/LSB";
|
||||
}
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PHAVirtualProbe1ToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case PHAVirtualProbe1Options::PHAVP1_Input: return "Input";
|
||||
case PHAVirtualProbe1Options::PHAVP1_Trapezoid: return "Trapezoid";
|
||||
case PHAVirtualProbe1Options::PHAVP1_Delta: return "Delta";
|
||||
case PHAVirtualProbe1Options::PHAVP1_Delta2: return "Delta2";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PHAVirtualProbe2ToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case PHAVirtualProbe2Options::PHAVP2_Input: return "Input";
|
||||
case PHAVirtualProbe2Options::PHAVP2_Baseline: return "Baseline";
|
||||
case PHAVirtualProbe2Options::PHAVP2_Threshold: return "Threshold";
|
||||
case PHAVirtualProbe2Options::PHAVP2_TrapezoidReduced: return "Trap. Reduced";
|
||||
case PHAVirtualProbe2Options::PHAVP2_None: return "None";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PHADigitalProbeToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case PHADigitalProbe1Options::PHADP_ACQVeto: return "Acq. Veto";
|
||||
case PHADigitalProbe1Options::PHADP_Armed: return "Armed";
|
||||
case PHADigitalProbe1Options::PHADP_BaselineFreeze: return "Baseline Freeze";
|
||||
case PHADigitalProbe1Options::PHADP_BFMVeto: return "BFM Veto";
|
||||
case PHADigitalProbe1Options::PHADP_Busy: return "Busy";
|
||||
case PHADigitalProbe1Options::PHADP_CoincidenceWindow: return "Coincidence Window";
|
||||
case PHADigitalProbe1Options::PHADP_ExternalTrig: return "External Trig.";
|
||||
case PHADigitalProbe1Options::PHADP_Peaking: return "Peaking";
|
||||
case PHADigitalProbe1Options::PHADP_PileUp: return "PileUp";
|
||||
case PHADigitalProbe1Options::PHADP_PrgVeto: return "PrgVeto";
|
||||
case PHADigitalProbe1Options::PHADP_TriggerHoldoff: return "Trig. Hold-Off";
|
||||
case PHADigitalProbe1Options::PHADP_TriggerValue: return "Trig. Value";
|
||||
case PHADigitalProbe1Options::PHADP_TriggerWindow: return "Trig. Window";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PSDVirtualProbe1ToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case PSDVirtualProbe1Options::PSDVP1_Input: return "Input";
|
||||
case PSDVirtualProbe1Options::PSDVP1_CFD: return "CFD";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PSDVirtualProbe2ToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case PSDVirtualProbe2Options::PSDVP2_Baseline: return "Baseline";
|
||||
case PSDVirtualProbe2Options::PSDVP2_CFD: return "CFD";
|
||||
case PSDVirtualProbe2Options::PSDVP2_None: return "None";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PSDDigitalProbe1ToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case PSDDigitalProbe1Options::PSDDP1_Coincidence: return "Coincidence";
|
||||
case PSDDigitalProbe1Options::PSDDP1_CoincidenceWindow: return "Coincidence Window";
|
||||
case PSDDigitalProbe1Options::PSDDP1_Gate: return "Gate";
|
||||
case PSDDigitalProbe1Options::PSDDP1_OverThreshold: return "Over Threshold";
|
||||
case PSDDigitalProbe1Options::PSDDP1_PileUp: return "PileUp";
|
||||
case PSDDigitalProbe1Options::PSDDP1_Trigger: return "Trigger";
|
||||
case PSDDigitalProbe1Options::PSDDP1_TriggerOut: return "TriggerOut";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string PSDDigitalProbe2ToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case PSDDigitalProbe2Options::PSDDP2_Coincidence: return "Coincidence";
|
||||
case PSDDigitalProbe2Options::PSDDP2_GateShort: return "Short Gate";
|
||||
case PSDDigitalProbe2Options::PSDDP2_OverThreshold: return "Over Threshold";
|
||||
case PSDDigitalProbe2Options::PSDDP2_PileUp: return "PileUp";
|
||||
case PSDDigitalProbe2Options::PSDDP2_Trigger: return "Trigger";
|
||||
case PSDDigitalProbe2Options::PSDDP2_TriggerHoldoff: return "Trig. Hold-Off";
|
||||
case PSDDigitalProbe2Options::PSDDP2_TriggerVal: return "Trig. Value";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string SyncModeToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_Disabled: return "Disabled";
|
||||
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_TrgOutSinDaisyChain: return "TrigOut-S In";
|
||||
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_SinFanout: return "S In Fanout";
|
||||
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_TrgOutTrgInDaisyChain: return "TrigOut-TrigIn";
|
||||
case CAEN_DGTZ_RunSyncMode_t::CAEN_DGTZ_RUN_SYNC_GpioGpioDaisyChain: return "Broken";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string AcqModeToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_FIRST_TRG_CONTROLLED: return "FirstTrg";
|
||||
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_LVDS_CONTROLLED: return "LVDS";
|
||||
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_SW_CONTROLLED: return "Software";
|
||||
case CAEN_DGTZ_AcqMode_t::CAEN_DGTZ_S_IN_CONTROLLED: return "S In";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string FirmwareTypeToString(int val)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case CAEN_DGTZ_DPPFirmware_PHA: return "PHA";
|
||||
case CAEN_DGTZ_DPPFirmware_PSD: return "PSD";
|
||||
case CAEN_DGTZ_DPPFirmware_CI: return "CI";
|
||||
case CAEN_DGTZ_DPPFirmware_DAW: return "DAW";
|
||||
case CAEN_DGTZ_DPPFirmware_QDC: return "QDC";
|
||||
case CAEN_DGTZ_DPPFirmware_ZLE: return "ZLE";
|
||||
case CAEN_DGTZ_DPPFirmwareNotSupported: return "NotSupported";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
std::string SyncMethodToString(SyncMethod method)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case SyncMethod::SIn_TrigOut: return "S-In TrigOut";
|
||||
case SyncMethod::None: return "None";
|
||||
}
|
||||
|
||||
return "None";
|
||||
}
|
||||
|
||||
double GetSamplingPeriod(CAEN_DGTZ_BoardModel_t model)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case CAEN_DGTZ_V1730: return 2.0; //ns -> 500 MSamples/s
|
||||
case CAEN_DGTZ_V1725: return 4.0; //ns -> 250 MSamples/s
|
||||
}
|
||||
|
||||
return 4.0; //Idk? This is a fairly common value...
|
||||
}
|
||||
}
|
291
src/DAQ/DigitizerDefs.h
Normal file
291
src/DAQ/DigitizerDefs.h
Normal file
|
@ -0,0 +1,291 @@
|
|||
#ifndef DIGITIZER_DEFS_H
|
||||
#define DIGITIZER_DEFS_H
|
||||
|
||||
#include <CAENDigitizerType.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
namespace Data
|
||||
{
|
||||
static constexpr uint64_t dataSize = 24; //size of writable data in bytes (no waveforms)
|
||||
}
|
||||
|
||||
enum class DigitizerAccessType
|
||||
{
|
||||
All,
|
||||
Single,
|
||||
None
|
||||
};
|
||||
|
||||
//Unified Daqromancy Data structure (similar to CAEN CoMPASS)
|
||||
struct DYData
|
||||
{
|
||||
uint16_t board;
|
||||
uint16_t channel;
|
||||
uint64_t timestamp;
|
||||
uint32_t energy;
|
||||
uint32_t energyShort;
|
||||
uint32_t flags;
|
||||
uint32_t waveSize;
|
||||
std::vector<uint16_t> trace1Samples; //For waves: traces are actual data to be saved potentially, digital is result of algorithms
|
||||
std::vector<uint16_t> trace2Samples;
|
||||
std::vector<uint8_t> digitalTrace1Samples;
|
||||
std::vector<uint8_t> digitalTrace2Samples; //Technically DPP supports 4 in docs? But all examples only seem to support 2...
|
||||
};
|
||||
|
||||
enum DPPAcqMode
|
||||
{
|
||||
List = 1,
|
||||
Waves = 2,
|
||||
None = 0
|
||||
};
|
||||
|
||||
enum class SyncStartType
|
||||
{
|
||||
HardwareControlled,
|
||||
SoftwareControlled,
|
||||
None
|
||||
};
|
||||
|
||||
//Add more later, right now only accept SPS standard
|
||||
enum class SyncMethod
|
||||
{
|
||||
SIn_TrigOut,
|
||||
None
|
||||
};
|
||||
|
||||
struct SyncArgs
|
||||
{
|
||||
SyncStartType startType = SyncStartType::SoftwareControlled;
|
||||
SyncMethod syncMethod = SyncMethod::SIn_TrigOut;
|
||||
};
|
||||
|
||||
//These need to be plain enums cause we're gonna cast them to int, so just be careful
|
||||
enum DynamicRange
|
||||
{
|
||||
Volt_2 = 0, //2Volt
|
||||
MilliVolt_500 = 1//0.5Volt
|
||||
};
|
||||
|
||||
enum PHAVirtualProbe1Options
|
||||
{
|
||||
PHAVP1_Input = CAEN_DGTZ_DPP_VIRTUALPROBE_Input,
|
||||
PHAVP1_Delta = CAEN_DGTZ_DPP_VIRTUALPROBE_Delta,
|
||||
PHAVP1_Delta2 = CAEN_DGTZ_DPP_VIRTUALPROBE_Delta2,
|
||||
PHAVP1_Trapezoid = CAEN_DGTZ_DPP_VIRTUALPROBE_Trapezoid
|
||||
};
|
||||
|
||||
enum PHAVirtualProbe2Options
|
||||
{
|
||||
PHAVP2_Input = CAEN_DGTZ_DPP_VIRTUALPROBE_Input,
|
||||
PHAVP2_Threshold = CAEN_DGTZ_DPP_VIRTUALPROBE_Threshold,
|
||||
PHAVP2_Baseline = CAEN_DGTZ_DPP_VIRTUALPROBE_Baseline,
|
||||
PHAVP2_TrapezoidReduced = CAEN_DGTZ_DPP_VIRTUALPROBE_TrapezoidReduced,
|
||||
PHAVP2_None = CAEN_DGTZ_DPP_VIRTUALPROBE_None
|
||||
};
|
||||
|
||||
enum PHADigitalProbe1Options
|
||||
{
|
||||
PHADP_TriggerWindow = CAEN_DGTZ_DPP_DIGITALPROBE_TRGWin,
|
||||
PHADP_Armed = CAEN_DGTZ_DPP_DIGITALPROBE_Armed,
|
||||
PHADP_PkRun = CAEN_DGTZ_DPP_DIGITALPROBE_PkRun,
|
||||
PHADP_PileUp = CAEN_DGTZ_DPP_DIGITALPROBE_PileUp,
|
||||
PHADP_Peaking = CAEN_DGTZ_DPP_DIGITALPROBE_Peaking,
|
||||
PHADP_CoincidenceWindow = CAEN_DGTZ_DPP_DIGITALPROBE_CoincWin,
|
||||
PHADP_BaselineFreeze = CAEN_DGTZ_DPP_DIGITALPROBE_BLFreeze,
|
||||
PHADP_TriggerHoldoff = CAEN_DGTZ_DPP_DIGITALPROBE_TRGHoldoff,
|
||||
PHADP_TriggerValue = CAEN_DGTZ_DPP_DIGITALPROBE_TRGVal,
|
||||
PHADP_ACQVeto = CAEN_DGTZ_DPP_DIGITALPROBE_ACQVeto,
|
||||
PHADP_BFMVeto = CAEN_DGTZ_DPP_DIGITALPROBE_BFMVeto,
|
||||
PHADP_ExternalTrig = CAEN_DGTZ_DPP_DIGITALPROBE_ExtTRG,
|
||||
PHADP_Busy = CAEN_DGTZ_DPP_DIGITALPROBE_Busy,
|
||||
PHADP_PrgVeto = CAEN_DGTZ_DPP_DIGITALPROBE_PrgVeto
|
||||
};
|
||||
|
||||
enum PSDVirtualProbe1Options
|
||||
{
|
||||
PSDVP1_Input = CAEN_DGTZ_DPP_VIRTUALPROBE_Input,
|
||||
PSDVP1_CFD = CAEN_DGTZ_DPP_VIRTUALPROBE_CFD
|
||||
};
|
||||
|
||||
enum PSDVirtualProbe2Options
|
||||
{
|
||||
PSDVP2_Baseline = CAEN_DGTZ_DPP_VIRTUALPROBE_Baseline,
|
||||
PSDVP2_CFD = CAEN_DGTZ_DPP_VIRTUALPROBE_CFD,
|
||||
PSDVP2_None = CAEN_DGTZ_DPP_VIRTUALPROBE_None
|
||||
};
|
||||
|
||||
enum PSDDigitalProbe1Options
|
||||
{
|
||||
PSDDP1_Gate = CAEN_DGTZ_DPP_DIGITALPROBE_Gate,
|
||||
PSDDP1_OverThreshold = CAEN_DGTZ_DPP_DIGITALPROBE_OverThr,
|
||||
PSDDP1_TriggerOut = CAEN_DGTZ_DPP_DIGITALPROBE_TRGOut,
|
||||
PSDDP1_CoincidenceWindow = CAEN_DGTZ_DPP_DIGITALPROBE_CoincWin,
|
||||
PSDDP1_PileUp = CAEN_DGTZ_DPP_DIGITALPROBE_PileUp,
|
||||
PSDDP1_Coincidence = CAEN_DGTZ_DPP_DIGITALPROBE_Coincidence,
|
||||
PSDDP1_Trigger = CAEN_DGTZ_DPP_DIGITALPROBE_Trigger
|
||||
};
|
||||
|
||||
enum PSDDigitalProbe2Options
|
||||
{
|
||||
PSDDP2_GateShort = CAEN_DGTZ_DPP_DIGITALPROBE_GateShort,
|
||||
PSDDP2_OverThreshold = CAEN_DGTZ_DPP_DIGITALPROBE_OverThr,
|
||||
PSDDP2_TriggerVal = CAEN_DGTZ_DPP_DIGITALPROBE_TRGVal,
|
||||
PSDDP2_TriggerHoldoff = CAEN_DGTZ_DPP_DIGITALPROBE_TRGHoldoff,
|
||||
PSDDP2_PileUp = CAEN_DGTZ_DPP_DIGITALPROBE_PileUp,
|
||||
PSDDP2_Coincidence = CAEN_DGTZ_DPP_DIGITALPROBE_Coincidence,
|
||||
PSDDP2_Trigger = CAEN_DGTZ_DPP_DIGITALPROBE_Trigger
|
||||
};
|
||||
|
||||
//Data vital to status/exsistance of digitizer
|
||||
struct DigitizerArgs
|
||||
{
|
||||
CAEN_DGTZ_ConnectionType type = CAEN_DGTZ_ConnectionType::CAEN_DGTZ_OpticalLink; //default (makes sense for FSU)
|
||||
int linkNumber = -1;
|
||||
int conetNode = -1;
|
||||
uint32_t vmeAddress = 0;
|
||||
int handle = -1;
|
||||
CAEN_DGTZ_BoardModel_t model = CAEN_DGTZ_V1730; //Find way for default?
|
||||
CAEN_DGTZ_DPPFirmware_t firmware = CAEN_DGTZ_NotDPPFirmware;
|
||||
std::string name = "None";
|
||||
int channels = 0; //number of channels in board
|
||||
int status = CAEN_DGTZ_NotYetImplemented;
|
||||
|
||||
bool operator==(const DigitizerArgs& rhs)
|
||||
{
|
||||
if (this->type == rhs.type && this->linkNumber == rhs.linkNumber && this->conetNode == rhs.conetNode && this->vmeAddress == rhs.vmeAddress
|
||||
&& this->handle == rhs.handle && this->model == rhs.model && this->firmware == rhs.firmware && this->name == rhs.name && this->channels == rhs.channels)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const DigitizerArgs& rhs)
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
//Parameters that are applied to digitizer as a whole
|
||||
struct DigitizerParameters
|
||||
{
|
||||
uint32_t recordLength = 600; //nanoseconds
|
||||
uint32_t channelMask = 0xffff;
|
||||
int eventAggr = 0; //Allow CAEN to optimize
|
||||
CAEN_DGTZ_AcqMode_t acqMode = CAEN_DGTZ_SW_CONTROLLED;
|
||||
CAEN_DGTZ_DPP_AcqMode_t dppAcqMode = CAEN_DGTZ_DPP_ACQ_MODE_List;
|
||||
CAEN_DGTZ_DPP_SaveParam_t dppSaveMode = CAEN_DGTZ_DPP_SAVE_PARAM_EnergyAndTime;
|
||||
CAEN_DGTZ_IOLevel_t IOlevel = CAEN_DGTZ_IOLevel_NIM;
|
||||
CAEN_DGTZ_TriggerMode_t triggerMode = CAEN_DGTZ_TRGMODE_ACQ_ONLY;
|
||||
CAEN_DGTZ_RunSyncMode_t syncMode = CAEN_DGTZ_RUN_SYNC_Disabled;
|
||||
};
|
||||
|
||||
//Per channel PHA settings
|
||||
struct PHAParameters
|
||||
{
|
||||
bool isEnabled = true;
|
||||
uint32_t preTriggerTime = 120; //nanoseconds
|
||||
float dcOffset = 0.2f;
|
||||
CAEN_DGTZ_PulsePolarity_t pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
|
||||
DynamicRange dynamicRange = DynamicRange::Volt_2;
|
||||
int decayTimeConst = 100000; //nanoseconds
|
||||
int trapFlatTop = 1984; //nanoseconds
|
||||
int trapRiseTime = 384; //nanoseconds
|
||||
int flatTopDelay = 1000; //nanoseconds
|
||||
int triggerFilterSmoothing = 16; // Valid powers of 2 up to 32
|
||||
int inputRiseTime = 192; //nanoseconds
|
||||
int triggerThreshold = 300; //LSB
|
||||
int samplesBaseLineMean = 3; //Options: 1->16 samples; 2->64 samples; 3->256 samples; 4->1024 samples; 5->4096 samples; 6->16384 samples
|
||||
int samplesPeakMean = 3; //Options: 0-> 1 sample; 1->4 samples; 2->16 samples; 3->64 samples
|
||||
int peakHoldOff = 1600; //nanoseconds
|
||||
int baseLineHoldOff = 2000; //nanoseconds
|
||||
int otrej= 0; // n/a
|
||||
int triggerHoldOff = 1984; //nanoseconds
|
||||
int riseTimeValidationWindow = 400;//nanoseconds
|
||||
int riseTimeDiscrimination = 0; // 0 off 1 on
|
||||
int digitalProbeGain = 0; //Options: 0->DigitalGain=1; 1->DigitalGain=2 (only with decimation >= 2samples); 2->DigitalGain=4 (only with decimation >= 4samples); 3->DigitalGain=8( only with decimation = 8samples)
|
||||
float energyNormalizationFactor = 1.0; //Default 1.0
|
||||
int inputDecimation = 0; // decimation (the input signal samples are averaged within this number of samples): 0 ->disabled; 1->2 samples; 2->4 samples; 3->8 samples
|
||||
};
|
||||
|
||||
//Per channel PSD settings
|
||||
struct PSDParameters
|
||||
{
|
||||
bool isEnabled = true;
|
||||
uint32_t preTriggerTime = 120; //nanoseconds
|
||||
float dcOffset = 0.2f;
|
||||
CAEN_DGTZ_PulsePolarity_t pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
|
||||
DynamicRange dynamicRange = DynamicRange::Volt_2;
|
||||
int baselineThreshold = 0; //Defined with fixed baseline only (LSB)
|
||||
int bltmo = 0;//n/a
|
||||
int triggerHoldOff = 1984; //nanoseconds
|
||||
int triggerThreshold = 300; //LSB
|
||||
int selfTrigger = 1; //0-Disabled 1-Enabled
|
||||
int chargeSensitivity = 1; //Comments only for 1720 & 1751? Check compass
|
||||
int shortGate = 96; //nanoseconds
|
||||
int longGate = 400; //nanoseconds
|
||||
int preGate = 32; //nanoseconds
|
||||
int triggerValidationWindow = 4000; //nanoseconds
|
||||
int samplesBasline = 4; //Again only 1720 and 1751
|
||||
int discrminatorType = CAEN_DGTZ_DPP_DISCR_MODE_CFD;
|
||||
int cfdFraction = 0; //0 25% 1 50% 2 75% 3 100%
|
||||
int cfdDelay = 16; //nanoseconds
|
||||
CAEN_DGTZ_DPP_TriggerConfig_t triggerConfig = CAEN_DGTZ_DPP_TriggerConfig_Threshold;
|
||||
CAEN_DGTZ_DPP_PUR_t pileUpRejection = CAEN_DGTZ_DPP_PSD_PUR_DetectOnly;
|
||||
int purgap = 100; //Purity Gap LSB
|
||||
};
|
||||
|
||||
//Option of dual analog, which types (digital probe 2 is always trigger for PHA)
|
||||
struct PHAWaveParameters
|
||||
{
|
||||
CAEN_DGTZ_DPP_VirtualProbe_t isDual = CAEN_DGTZ_DPP_VIRTUALPROBE_SINGLE; //Default to a single analog trace
|
||||
PHAVirtualProbe1Options analogProbe1 = PHAVirtualProbe1Options::PHAVP1_Input; //Main analog trace defaults to input signal;
|
||||
PHAVirtualProbe2Options analogProbe2 = PHAVirtualProbe2Options::PHAVP2_None; //Default val; in default config wont be displayed
|
||||
PHADigitalProbe1Options digitalProbe1 = PHADigitalProbe1Options::PHADP_TriggerWindow; //Idk guess this is good default
|
||||
};
|
||||
|
||||
struct PSDWaveParameters
|
||||
{
|
||||
CAEN_DGTZ_DPP_VirtualProbe_t isDual = CAEN_DGTZ_DPP_VIRTUALPROBE_SINGLE; //Default to a single analog trace
|
||||
PSDVirtualProbe1Options analogProbe1 = PSDVirtualProbe1Options::PSDVP1_Input; //Main trace defaults to input
|
||||
PSDVirtualProbe2Options analogProbe2 = PSDVirtualProbe2Options::PSDVP2_None; //Defaults to off
|
||||
PSDDigitalProbe1Options digitalProbe1 = PSDDigitalProbe1Options::PSDDP1_Gate; //Defaults to long gate
|
||||
PSDDigitalProbe2Options digitalProbe2 = PSDDigitalProbe2Options::PSDDP2_GateShort; //Defaults to short gate
|
||||
};
|
||||
|
||||
//String conversion helper functions//
|
||||
std::string BoardIOToString(int value);
|
||||
std::string PolarityToString(int value);
|
||||
std::string DynamicRangeToString(DynamicRange range);
|
||||
std::string IntSwitchToString(int value);
|
||||
std::string DiscriminatorModeToString(int mode);
|
||||
std::string CFDFractionToString(int val);
|
||||
std::string TriggerConfigToString(int val);
|
||||
std::string PileUpModeToString(int val);
|
||||
//Requires both dynamic range and charge sens. value (different ranges = different values)
|
||||
std::string ChargeSensToString(int cs_val, DynamicRange range);
|
||||
std::string PHAVirtualProbe1ToString(int val);
|
||||
std::string PHAVirtualProbe2ToString(int val);
|
||||
std::string PHADigitalProbeToString(int val);
|
||||
std::string PSDVirtualProbe1ToString(int val);
|
||||
std::string PSDVirtualProbe2ToString(int val);
|
||||
std::string PSDDigitalProbe1ToString(int val);
|
||||
std::string PSDDigitalProbe2ToString(int val);
|
||||
std::string SyncModeToString(int val);
|
||||
std::string AcqModeToString(int val);
|
||||
std::string FirmwareTypeToString(int val);
|
||||
std::string SyncMethodToString(SyncMethod method);
|
||||
|
||||
//Simple integer power calc
|
||||
int Power(int base, int order);
|
||||
|
||||
//Get the time per sample for a type of board (returned in nanoseconds)
|
||||
double GetSamplingPeriod(CAEN_DGTZ_BoardModel_t model);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
57
src/DYio/DYFile.cpp
Normal file
57
src/DYio/DYFile.cpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include "DYFile.h"
|
||||
#include "DYListData.h"
|
||||
#include <bit>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
DYFile::DYFile() :
|
||||
m_fileHandle(nullptr), m_currentLocation(0)
|
||||
{
|
||||
if (std::endian::native == std::endian::big)
|
||||
|
||||
("You are using a big endian system! Data will be encoded in reverse byte order on most machines");
|
||||
m_buffer.resize(s_bufferSize);
|
||||
}
|
||||
|
||||
DYFile::DYFile(const std::filesystem::path& path) :
|
||||
m_fileHandle(nullptr), m_currentLocation(0)
|
||||
{
|
||||
if (std::endian::native == std::endian::big)
|
||||
DY_WARN("You are using a big endian system! Data will be encoded in reverse byte order on most machines");
|
||||
m_buffer.resize(s_bufferSize);
|
||||
Open(path);
|
||||
}
|
||||
|
||||
DYFile::~DYFile()
|
||||
{
|
||||
if (IsOpen())
|
||||
Close();
|
||||
}
|
||||
|
||||
void DYFile::Open(const std::filesystem::path& path)
|
||||
{
|
||||
m_path = path;
|
||||
m_fileHandle = std::make_shared<std::ofstream>(path);
|
||||
}
|
||||
|
||||
void DYFile::Close()
|
||||
{
|
||||
if (m_currentLocation != 0)
|
||||
Flush();
|
||||
m_fileHandle->close();
|
||||
}
|
||||
|
||||
void DYFile::Flush()
|
||||
{
|
||||
m_fileHandle->write(m_buffer.data(), m_currentLocation);
|
||||
m_currentLocation = 0;
|
||||
}
|
||||
|
||||
//See BSListData.h for details on loading data to buffer.
|
||||
void DYFile::Write(const DYData& data)
|
||||
{
|
||||
m_currentLocation = LoadDYDataToBuffer(m_buffer, data);
|
||||
if (m_currentLocation == s_bufferSize)
|
||||
Flush();
|
||||
}
|
||||
}
|
35
src/DYio/DYFile.h
Normal file
35
src/DYio/DYFile.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef DY_FILE_H
|
||||
#define DY_FILE_H
|
||||
|
||||
#include <filesystem>
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class DYFile
|
||||
{
|
||||
public:
|
||||
DYFile();
|
||||
DYFile(const std::filesystem::path& path);
|
||||
~DYFile();
|
||||
|
||||
void Open(const std::filesystem::path& path);
|
||||
void Write(const DYData& data);
|
||||
void Close();
|
||||
bool IsOpen() const { return m_fileHandle == nullptr ? false : m_fileHandle->is_open(); }
|
||||
const std::filesystem::path& GetPath() const { return m_path; }
|
||||
|
||||
private:
|
||||
void Flush();
|
||||
|
||||
std::vector<char> m_buffer;
|
||||
std::shared_ptr<std::ofstream> m_fileHandle;
|
||||
std::filesystem::path m_path;
|
||||
std::size_t m_currentLocation;
|
||||
|
||||
static constexpr uint64_t s_bufferSize = Data::dataSize * 100; //Store 100 hits before flushing? Idk
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
106
src/DYio/DYListData.h
Normal file
106
src/DYio/DYListData.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
#ifndef BS_LIST_DATA_H
|
||||
#define BS_LIST_DATA_H
|
||||
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//This is the subset of DYData that will be transferred via binary writes/reads
|
||||
//on either network or file io
|
||||
struct BSListData
|
||||
{
|
||||
uint16_t board;
|
||||
uint16_t channel;
|
||||
uint64_t timestamp;
|
||||
uint32_t energy;
|
||||
uint32_t energyShort;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/*
|
||||
Load DYData to a binary buffer(in our implementation this is as std::vector<char>),
|
||||
stripping away any wave data, returns index after last written to.
|
||||
Quite nasty. Writing structs is dangerous for cross-platformness, even with pod's
|
||||
Cannot guarantee padding is equivalent from different compilers, architectures.
|
||||
So, convert each individual data member to binary, and write our own compact message.
|
||||
*/
|
||||
static std::size_t LoadDYDataToBuffer(std::vector<char>& buffer, const DYData& data)
|
||||
{
|
||||
char* dataPointer;
|
||||
int loopIndex; // predeclare to save allocation time
|
||||
int position = buffer.empty() ? 0 : buffer.size() - 1;
|
||||
|
||||
if (buffer.size() - position < Data::dataSize)
|
||||
{
|
||||
DY_ERROR("Could not load data to buffer; data size exceeded remaining buffer size {0}", buffer.size() - position);
|
||||
return position;
|
||||
}
|
||||
|
||||
//Write board (2 bytes)
|
||||
dataPointer = (char*)&(data.board);
|
||||
for (loopIndex = 0; loopIndex < sizeof(data.board); loopIndex++)
|
||||
buffer[position++] = dataPointer[loopIndex];
|
||||
|
||||
//Write channel (2 bytes)
|
||||
dataPointer = (char*)&(data.channel);
|
||||
for (loopIndex = 0; loopIndex < sizeof(data.channel); loopIndex++)
|
||||
buffer[position++] = dataPointer[loopIndex];
|
||||
|
||||
//Write timestamp (8 bytes)
|
||||
dataPointer = (char*)&(data.timestamp);
|
||||
for (loopIndex = 0; loopIndex < sizeof(data.timestamp); loopIndex++)
|
||||
buffer[position++] = dataPointer[loopIndex];
|
||||
|
||||
//Write energy (4 bytes)
|
||||
dataPointer = (char*)&(data.energy);
|
||||
for (loopIndex = 0; loopIndex < sizeof(data.energy); loopIndex++)
|
||||
buffer[position++] = dataPointer[loopIndex];
|
||||
|
||||
//Write energyShort (4 bytes)
|
||||
dataPointer = (char*)&(data.energyShort);
|
||||
for (loopIndex = 0; loopIndex < sizeof(data.energyShort); loopIndex++)
|
||||
buffer[position++] = dataPointer[loopIndex];
|
||||
|
||||
//Write flags (4 bytes)
|
||||
dataPointer = (char*)&(data.flags);
|
||||
for (loopIndex = 0; loopIndex < sizeof(data.flags); loopIndex++)
|
||||
buffer[position++] = dataPointer[loopIndex];
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/*
|
||||
Unload BSListData from a binary buffer(in our implementation this is as std::vector<char>),
|
||||
returns index after last pulled from. Writes to input parameter data
|
||||
Again, rather nasty. Here we C-style cast the data in the buffer to our types in the list data,
|
||||
write them to the input parameter reference, and then iterate forward the appropriate number of bytes.
|
||||
*/
|
||||
static std::size_t UnLoadBSListDataFromBuffer(const std::vector<char>& buffer, BSListData& data, std::size_t startPosition)
|
||||
{
|
||||
std::size_t position = startPosition;
|
||||
if (buffer.size() - startPosition < Data::dataSize)
|
||||
{
|
||||
DY_ERROR("Could not unload buffer to data; data size exceeded remaining buffer size {0}", buffer.size() - startPosition);
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
data.board = *(uint16_t*)&(buffer[position]);
|
||||
position += 2;
|
||||
data.channel = *(uint16_t*)&(buffer[position]);
|
||||
position += 2;
|
||||
data.timestamp = *(uint16_t*)&(buffer[position]);
|
||||
position += 8;
|
||||
data.timestamp = *(uint16_t*)&(buffer[position]);
|
||||
position += 8;
|
||||
data.energy = *(uint32_t*)&(buffer[position]);
|
||||
position += 4;
|
||||
data.energyShort = *(uint32_t*)&(buffer[position]);
|
||||
position += 4;
|
||||
data.flags = *(uint32_t*)&(buffer[position]);
|
||||
position += 4;
|
||||
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
61
src/DYio/DYMessage.h
Normal file
61
src/DYio/DYMessage.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef BS_MESSAGE_H
|
||||
#define BS_MESSAGE_H
|
||||
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
#include "DYListData.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
enum class BSMessageType : uint64_t
|
||||
{
|
||||
List,
|
||||
Mixed, //Maybe someday hahaha
|
||||
None
|
||||
};
|
||||
|
||||
struct BSHeader
|
||||
{
|
||||
BSMessageType type = BSMessageType::None;
|
||||
uint64_t size = 0;
|
||||
};
|
||||
|
||||
struct BSMessage
|
||||
{
|
||||
BSMessage() = default;
|
||||
BSMessage(const std::vector<DYData>& data)
|
||||
{
|
||||
header.type = BSMessageType::List;
|
||||
for (const auto& datum : data)
|
||||
LoadDYDataToBuffer(body, datum);
|
||||
}
|
||||
|
||||
BSHeader header;
|
||||
std::vector<char> body;
|
||||
|
||||
std::size_t Size() const
|
||||
{
|
||||
return header.size;
|
||||
}
|
||||
|
||||
//Nasty work. Convert header into a raw bytes array for transmission. Makes it so padding is no longer a concern.
|
||||
std::vector<char> GetHeaderRaw() const
|
||||
{
|
||||
std::vector<char> rawBytes(sizeof(header.type) + sizeof(header.size));
|
||||
std::size_t position = 0;
|
||||
int loopIndex;
|
||||
|
||||
char* dataPointer = (char*)&header.type;
|
||||
for (loopIndex = 0; loopIndex < sizeof(header.type); loopIndex++)
|
||||
rawBytes[position++] = dataPointer[loopIndex];
|
||||
|
||||
dataPointer = (char*)&header.size;
|
||||
for (loopIndex = 0; loopIndex < sizeof(header.size); loopIndex++)
|
||||
rawBytes[position++] = dataPointer[loopIndex];
|
||||
|
||||
return rawBytes;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
113
src/DYio/DYRun.cpp
Normal file
113
src/DYio/DYRun.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
#include "DYRun.h"
|
||||
#include "Core/ProjectSerializer.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//Maps two uint32_t's to a single uint32_t
|
||||
static uint32_t SzudzikPairingFunction(uint32_t v1, uint32_t v2)
|
||||
{
|
||||
return v1 >= v2 ? (v1 * v1 + v1 + v2) : (v2 * v2 + v1);
|
||||
}
|
||||
|
||||
DYRun::DYRun() :
|
||||
m_isRunning(false), m_processingThread(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DYRun::~DYRun()
|
||||
{
|
||||
StopRun();
|
||||
}
|
||||
|
||||
void DYRun::InitFiles(const DYProject::Ref& project)
|
||||
{
|
||||
std::filesystem::path runPath = project->CreateRunDirectory();
|
||||
|
||||
//Write a run local copy of the daq settings
|
||||
ProjectSerializer serializer(runPath / "settings.yaml");
|
||||
serializer.SerializeData(project);
|
||||
|
||||
const std::vector<DigitizerArgs>& argList = project->GetDigitizerArgsList();
|
||||
|
||||
std::string filename;
|
||||
uint32_t key;
|
||||
for (const auto& args : argList)
|
||||
{
|
||||
for (int i = 0; i < args.channels; i++)
|
||||
{
|
||||
filename = args.name + "_CH_" + std::to_string(i) + ".bsbin";
|
||||
key = SzudzikPairingFunction(args.handle, i);
|
||||
m_files.insert_or_assign(key, DYFile(runPath / filename));
|
||||
if (!(m_files[key].IsOpen()))
|
||||
{
|
||||
DY_WARN("File named {0} could not be opened!", filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DYRun::StartRun(const DYProject::Ref& project)
|
||||
{
|
||||
if (m_isRunning)
|
||||
StopRun();
|
||||
|
||||
//m_ringConsumerID = RingBuffer::Attach();
|
||||
m_dataHandle = DataDistributor::Connect();
|
||||
InitFiles(project);
|
||||
m_isRunning = true;
|
||||
|
||||
m_processingThread = new std::thread(&DYRun::ProcessData, std::ref(*this));
|
||||
}
|
||||
|
||||
void DYRun::StopRun()
|
||||
{
|
||||
if (m_dataHandle.dataQueue == nullptr)
|
||||
return;
|
||||
else if(!m_dataHandle.dataQueue->IsEmpty())
|
||||
{
|
||||
DY_INFO("Finishing writing data to file before stopping... Still have {0} events to process", m_dataHandle.dataQueue->Size());
|
||||
while (!m_dataHandle.dataQueue->IsEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
m_isRunning = false;
|
||||
m_dataHandle.dataQueue->ForceWakeup();
|
||||
if (m_processingThread != nullptr && m_processingThread->joinable())
|
||||
{
|
||||
m_processingThread->join();
|
||||
delete m_processingThread;
|
||||
m_processingThread = nullptr;
|
||||
DataDistributor::Disconnect(m_dataHandle);
|
||||
|
||||
//Close all files and clear the map to be ready for re-use
|
||||
for (auto& fileIter : m_files)
|
||||
fileIter.second.Close();
|
||||
m_files.clear();
|
||||
}
|
||||
else if(m_processingThread != nullptr)
|
||||
DY_WARN("Unable to destroy the processing thread for file writing!");
|
||||
m_dataHandle.dataQueue->ResetWakeup();
|
||||
}
|
||||
|
||||
void DYRun::ProcessData()
|
||||
{
|
||||
std::vector<DYData> dataBuffer;
|
||||
uint32_t key;
|
||||
while (m_isRunning)
|
||||
{
|
||||
m_dataHandle.dataQueue->Wait(); //Block for the queue to be full
|
||||
if (!m_dataHandle.dataQueue->IsEmpty()) //Protect for edge cases
|
||||
{
|
||||
dataBuffer = m_dataHandle.dataQueue->Front();
|
||||
for (auto& datum : dataBuffer)
|
||||
{
|
||||
key = SzudzikPairingFunction(datum.board, datum.channel);
|
||||
m_files[key].Write(datum);
|
||||
}
|
||||
m_dataHandle.dataQueue->PopFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
src/DYio/DYRun.h
Normal file
34
src/DYio/DYRun.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef DY_RUN_H
|
||||
#define DY_RUN_H
|
||||
|
||||
#include "DYFile.h"
|
||||
#include "Core/DYProject.h"
|
||||
#include "Core/DataDistributor.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class DYRun
|
||||
{
|
||||
public:
|
||||
DYRun();
|
||||
~DYRun();
|
||||
|
||||
void StartRun(const DYProject::Ref& project);
|
||||
void StopRun();
|
||||
bool IsRunning() const { return m_isRunning; }
|
||||
|
||||
private:
|
||||
void InitFiles(const DYProject::Ref& project);
|
||||
void ProcessData();
|
||||
|
||||
std::unordered_map<uint32_t, DYFile> m_files;
|
||||
DistributorClient m_dataHandle;
|
||||
std::atomic<bool> m_isRunning;
|
||||
std::thread* m_processingThread;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
203
src/DYio/TCPServer.cpp
Normal file
203
src/DYio/TCPServer.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
#include "TCPServer.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
TCPServerConnection::TCPServerConnection(asio::io_context& context, asio::ip::tcp::socket socket) :
|
||||
m_contextRef(context), m_socket(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
TCPServerConnection::~TCPServerConnection()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
The job we push to asio is slightly strange. If the queue is empty when we try to push a new job to it, it tells us
|
||||
that the loop of write calls and job submissions had stopped (or was not yet started), so we need to restart/start the
|
||||
cycle of writing data to the socket.
|
||||
*/
|
||||
void TCPServerConnection::Send(const BSMessage& message)
|
||||
{
|
||||
asio::post(m_contextRef,
|
||||
[this, message]()
|
||||
{
|
||||
bool wasEmpty = m_queue.IsEmpty();
|
||||
m_queue.PushBack(message);
|
||||
if (wasEmpty)
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void TCPServerConnection::Disconnect()
|
||||
{
|
||||
if (IsConnected())
|
||||
asio::post(m_contextRef, [this]() { m_socket.close(); });
|
||||
}
|
||||
|
||||
void TCPServerConnection::WriteHeader()
|
||||
{
|
||||
std::vector<char> headerData = m_queue.Front().GetHeaderRaw();
|
||||
asio::async_write(m_socket, asio::buffer(headerData, headerData.size()),
|
||||
[this](std::error_code ec, std::size_t length) //Callback upon completion
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
if (m_queue.Front().Size() > 0)
|
||||
WriteBody(); //submit next job to asio: write body data
|
||||
else
|
||||
{
|
||||
m_queue.PopFront();
|
||||
if (!m_queue.IsEmpty())
|
||||
WriteHeader(); //submit next job to asio: write the next message
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DY_WARN("TCPServerConnection -> Failure to write header of message, with error code: {0}", ec.message());
|
||||
DY_WARN("Closing connection...");
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void TCPServerConnection::WriteBody()
|
||||
{
|
||||
asio::async_write(m_socket, asio::buffer(m_queue.Front().body, m_queue.Front().Size()),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
m_queue.PopFront();
|
||||
if (!m_queue.IsEmpty())
|
||||
WriteHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
DY_WARN("TCPServerConnection -> Failure to write body of message, with error code: {0}", ec.message());
|
||||
DY_WARN("Closing connection...");
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TCPServer::TCPServer(uint16_t serverPort) :
|
||||
m_acceptor(m_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), serverPort)), m_isDataFeedRunning(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TCPServer::~TCPServer()
|
||||
{
|
||||
}
|
||||
|
||||
void TCPServer::PowerOn()
|
||||
{
|
||||
try
|
||||
{
|
||||
WaitForClient();
|
||||
m_acceptorThread = std::thread([this]() { m_context.run(); });
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
DY_ERROR("Server caught exception: {0}", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
DY_INFO("Server started and waiting for client connection");
|
||||
}
|
||||
|
||||
void TCPServer::Shutdown()
|
||||
{
|
||||
StopDataFeed();
|
||||
|
||||
m_context.stop();
|
||||
if (m_acceptorThread.joinable())
|
||||
m_acceptorThread.join();
|
||||
|
||||
DY_INFO("Sever has been shutdown");
|
||||
}
|
||||
|
||||
void TCPServer::StartDataFeed()
|
||||
{
|
||||
if (m_dataFeedThread.joinable())
|
||||
{
|
||||
DY_WARN("Server malfunction! You forgot to call StopDataFeed before calling StartDataFeed! Trying to recover...");
|
||||
StopDataFeed();
|
||||
}
|
||||
|
||||
if (m_dataHandle.dataQueue != nullptr)
|
||||
m_dataHandle = DataDistributor::Connect();
|
||||
|
||||
m_isDataFeedRunning = true;
|
||||
m_dataFeedThread = std::thread([this]()
|
||||
{
|
||||
while (m_isDataFeedRunning)
|
||||
{
|
||||
m_dataHandle.dataQueue->Wait();
|
||||
|
||||
if (!m_dataHandle.dataQueue->IsEmpty())
|
||||
continue;
|
||||
|
||||
BSMessage message(m_dataHandle.dataQueue->Front());
|
||||
MessageClients(message);
|
||||
m_dataHandle.dataQueue->PopFront();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void TCPServer::StopDataFeed()
|
||||
{
|
||||
if (m_isDataFeedRunning == false)
|
||||
return;
|
||||
|
||||
m_isDataFeedRunning = false;
|
||||
m_dataHandle.dataQueue->ForceWakeup();
|
||||
if (m_dataFeedThread.joinable())
|
||||
m_dataFeedThread.join();
|
||||
m_dataHandle.dataQueue->ResetWakeup();
|
||||
}
|
||||
|
||||
void TCPServer::MessageClients(const BSMessage& message)
|
||||
{
|
||||
bool isAnyClientInvalid = false;
|
||||
std::scoped_lock<std::mutex> guard(m_clientMutex);
|
||||
|
||||
for (auto& client : m_clients)
|
||||
{
|
||||
if (client->IsConnected())
|
||||
{
|
||||
client->Send(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
client->Disconnect();
|
||||
isAnyClientInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isAnyClientInvalid)
|
||||
m_clients.erase(std::remove_if(m_clients.begin(), m_clients.end(), [](TCPServerConnection::Ref& client) { return client->IsConnected(); }), m_clients.end());
|
||||
}
|
||||
|
||||
void TCPServer::WaitForClient()
|
||||
{
|
||||
m_acceptor.async_accept(
|
||||
[this](std::error_code ec, asio::ip::tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
DY_INFO("Server connection to new client: {0}", socket.remote_endpoint());
|
||||
{
|
||||
//TCPServerConnection::Ref newClient = std::make_shared<TCPServerConnection>(m_context, std::move(socket));
|
||||
std::scoped_lock<std::mutex> guard(m_clientMutex);
|
||||
//m_clients.push_back(newClient);
|
||||
m_clients.push_back(std::make_shared<TCPServerConnection>(m_context, std::move(socket)));
|
||||
}
|
||||
}
|
||||
else
|
||||
DY_ERROR("Server Connection Failure with error code: {0}", ec.message());
|
||||
});
|
||||
}
|
||||
}
|
108
src/DYio/TCPServer.h
Normal file
108
src/DYio/TCPServer.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
#ifndef TCP_CONNECTION_H
|
||||
#define TCP_CONNECTION_H
|
||||
|
||||
#include "DYMessage.h"
|
||||
#include "Core/ThreadSafeQueue.h"
|
||||
#include "Core/DataDistributor.h"
|
||||
|
||||
#include "asio.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class TCPServerConnection
|
||||
{
|
||||
public:
|
||||
using Ref = std::shared_ptr<TCPServerConnection>;
|
||||
|
||||
TCPServerConnection(asio::io_context& context, asio::ip::tcp::socket socket);
|
||||
~TCPServerConnection();
|
||||
|
||||
bool IsConnected() const { return m_socket.is_open(); }
|
||||
void Send(const BSMessage& message);
|
||||
|
||||
void Disconnect();
|
||||
|
||||
private:
|
||||
void WriteHeader();
|
||||
void WriteBody();
|
||||
|
||||
asio::ip::tcp::socket m_socket;
|
||||
asio::io_context& m_contextRef;
|
||||
|
||||
ThreadSafeQueue<BSMessage> m_queue;
|
||||
};
|
||||
|
||||
/*
|
||||
TCPServer
|
||||
Daqromancy specialized server implementation. First take at what is sure to evolve significantly as more testing is done
|
||||
Currently creates two threads, one for accepting clients, and one for posting data.
|
||||
|
||||
PowerOn()/Shutdown() pair are use to initialize the server and start up the client accepting thread. This pair is essentially intended to go with
|
||||
application start and stop.
|
||||
|
||||
StartDataFeed()/StopDataFeed() pair are used to tell the server when data is ready to be pulled from the ring buffer. This pari is intended to go with
|
||||
a data run start and stop.
|
||||
*/
|
||||
class TCPServer
|
||||
{
|
||||
public:
|
||||
TCPServer(uint16_t serverPort);
|
||||
~TCPServer();
|
||||
|
||||
/*
|
||||
PowerOn()
|
||||
Starts up the acceptor thread of the server. This allows clients to begin to connect
|
||||
to our service. This should be done more or less once, at application initialization.
|
||||
*/
|
||||
void PowerOn();
|
||||
|
||||
/*
|
||||
Shutdown()
|
||||
Shutdown the entire server, including both the acceptor thread and the data feed thread.
|
||||
This should be done essentially once, at application shutdown.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/*
|
||||
StartDataFeed()
|
||||
Starts the process to take data from the RingBuffer and feed it to clients (if they exist).
|
||||
This will be called many times, usually at the start of a new data taking run.
|
||||
*/
|
||||
void StartDataFeed();
|
||||
|
||||
/*
|
||||
StopDataFeed()
|
||||
Stops the process to take data from the RingBuffer and feed it to clients (if they exist).
|
||||
This will be called many times, usually at the stop of a data taking run.
|
||||
*/
|
||||
void StopDataFeed();
|
||||
|
||||
bool IsActive() { return m_acceptor.is_open(); }
|
||||
bool IsFeeding() { return m_isDataFeedRunning; }
|
||||
|
||||
|
||||
private:
|
||||
void MessageClients(const BSMessage& message);
|
||||
void WaitForClient();
|
||||
|
||||
asio::io_context m_context;
|
||||
asio::ip::tcp::acceptor m_acceptor;
|
||||
|
||||
std::mutex m_clientMutex;
|
||||
std::vector<TCPServerConnection::Ref> m_clients;
|
||||
|
||||
DistributorClient m_dataHandle;
|
||||
|
||||
std::thread m_acceptorThread;
|
||||
std::thread m_dataFeedThread;
|
||||
|
||||
std::atomic<bool> m_isDataFeedRunning;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
972
src/Editor/DigitizerPanel.cpp
Normal file
972
src/Editor/DigitizerPanel.cpp
Normal file
|
@ -0,0 +1,972 @@
|
|||
#include "DigitizerPanel.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
static std::string PanelTypeToString(DigitizerPanel::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DigitizerPanel::Type::PHA: return "DPP PHA";
|
||||
case DigitizerPanel::Type::PSD: return "DPP PSD";
|
||||
case DigitizerPanel::Type::None: return "Invalid firmware";
|
||||
}
|
||||
|
||||
return "Invalid firmware";
|
||||
}
|
||||
|
||||
DigitizerPanel::DigitizerPanel(const DigitizerArgs& args) :
|
||||
m_args(args)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
DigitizerPanel::~DigitizerPanel() {}
|
||||
|
||||
void DigitizerPanel::Init()
|
||||
{
|
||||
if (m_args.firmware == CAEN_DGTZ_DPPFirmware_PHA)
|
||||
{
|
||||
m_panelType = Type::PHA;
|
||||
m_panelName = m_args.name + "_PHA";
|
||||
m_phaChannels.resize(m_args.channels);
|
||||
m_digitizerEnabled = true;
|
||||
}
|
||||
else if (m_args.firmware == CAEN_DGTZ_DPPFirmware_PSD)
|
||||
{
|
||||
m_panelType = Type::PSD;
|
||||
m_panelName = m_args.name + "_PSD";
|
||||
m_psdChannels.resize(m_args.channels);
|
||||
m_digitizerEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_panelType = Type::None;
|
||||
m_panelName = m_args.name + "_None";
|
||||
m_digitizerEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DigitizerPanel::SetDigitizerParameters(const DigitizerParameters& params)
|
||||
{
|
||||
m_params = params;
|
||||
}
|
||||
|
||||
void DigitizerPanel::SetPHAParameters(const std::vector<PHAParameters>& ch_params, const PHAWaveParameters& wv_params)
|
||||
{
|
||||
if (ch_params.size() != m_phaChannels.size())
|
||||
{
|
||||
DY_ERROR("Trying to load channel parameters of size {0} to panel with {1} channels", ch_params.size(), m_phaChannels.size());
|
||||
return;
|
||||
}
|
||||
|
||||
m_phaChannels = ch_params;
|
||||
m_phaWaves = wv_params;
|
||||
}
|
||||
|
||||
void DigitizerPanel::SetPSDParameters(const std::vector<PSDParameters>& ch_params, const PSDWaveParameters& wv_params)
|
||||
{
|
||||
if (ch_params.size() != m_psdChannels.size())
|
||||
{
|
||||
DY_ERROR("Trying to load channel parameters of size {0} to panel with {1} channels", ch_params.size(), m_psdChannels.size());
|
||||
return;
|
||||
}
|
||||
|
||||
m_psdChannels = ch_params;
|
||||
m_psdWaves = wv_params;
|
||||
}
|
||||
|
||||
bool DigitizerPanel::OnImGuiRender()
|
||||
{
|
||||
bool changed = false;
|
||||
if (ImGui::BeginTabItem(m_panelName.c_str()))
|
||||
{
|
||||
ImGui::Text("%s", PanelTypeToString(m_panelType).c_str());
|
||||
ImGui::Text("%s", fmt::format("Optical Link: {0} CONET Node: {1}", m_args.linkNumber, m_args.conetNode).c_str());
|
||||
ImGui::Text("Settings file: ");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", m_settingsFile.c_str());
|
||||
ImGui::Button("Open...");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Save...");
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("Board Global Settings");
|
||||
changed |= RenderDigitizerParams();
|
||||
|
||||
switch (m_panelType)
|
||||
{
|
||||
case Type::PHA:
|
||||
{
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("PHA Channel Settings");
|
||||
changed |= RenderPHAParameters();
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("PHA Waves Settings");
|
||||
changed |= RenderPHAWaveParameters();
|
||||
break;
|
||||
}
|
||||
case Type::PSD:
|
||||
{
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("PSD Channel Settings");
|
||||
changed |= RenderPSDParameters();
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("PSD Waves Settings");
|
||||
changed |= RenderPSDWaveParameters();
|
||||
break;
|
||||
}
|
||||
case Type::None: break;
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool DigitizerPanel::RenderDigitizerParams()
|
||||
{
|
||||
bool changed = false;
|
||||
if (ImGui::BeginTable("Digitizer Parameters", 6, tableFlags))
|
||||
{
|
||||
ImGui::TableSetupColumn("Enable/Disable");
|
||||
ImGui::TableSetupColumn("Record Length (ns)");
|
||||
ImGui::TableSetupColumn("Event Aggregation");
|
||||
ImGui::TableSetupColumn("Acq. Mode");
|
||||
//ImGui::TableSetupColumn("DPP Acq. Mode");
|
||||
ImGui::TableSetupColumn("Board IO Level");
|
||||
//ImGui::TableSetupColumn("Trigger Mode");
|
||||
ImGui::TableSetupColumn("Sync Mode");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::RadioButton("##globalenable", m_digitizerEnabled))
|
||||
{
|
||||
changed |= true;
|
||||
m_digitizerEnabled = !m_digitizerEnabled;
|
||||
if (m_digitizerEnabled)
|
||||
m_params.channelMask = 0xffff;
|
||||
else
|
||||
m_params.channelMask = 0;
|
||||
}
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputScalar("##recordlength", ImGuiDataType_U32, &m_params.recordLength))
|
||||
{
|
||||
changed |= true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputScalar("##eventAggregate", ImGuiDataType_U32, &m_params.eventAggr))
|
||||
{
|
||||
changed |= true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", AcqModeToString(m_params.acqMode).c_str());
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::BeginCombo("##boardIO", BoardIOToString(m_params.IOlevel).c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("NIM", "NIM" == BoardIOToString(m_params.IOlevel)))
|
||||
{
|
||||
changed |= true;
|
||||
m_params.IOlevel = CAEN_DGTZ_IOLevel_NIM;
|
||||
}
|
||||
if (ImGui::Selectable("TTL", "TTL" == BoardIOToString(m_params.IOlevel)))
|
||||
{
|
||||
changed |= true;
|
||||
m_params.IOlevel = CAEN_DGTZ_IOLevel_TTL;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", SyncModeToString(m_params.syncMode).c_str());
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
//PHA options; given per channel in the digitizer
|
||||
bool DigitizerPanel::RenderPHAParameters()
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
static std::string tempString; //useful for comps in widgets
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::BeginDisabled();
|
||||
if (ImGui::BeginTable("PHA Channel Parameters", 22, tableFlags | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY, ImVec2(0, 300)))
|
||||
{
|
||||
ImGui::TableSetupColumn("Channel");
|
||||
ImGui::TableSetupColumn("Enable/Disable");
|
||||
ImGui::TableSetupColumn("Pre-Trigger Time (ns)");
|
||||
ImGui::TableSetupColumn("DC Offset (%)");
|
||||
ImGui::TableSetupColumn("Polarity");
|
||||
ImGui::TableSetupColumn("Dynamic Range");
|
||||
ImGui::TableSetupColumn("Decay Time Const (ns)");
|
||||
ImGui::TableSetupColumn("Trap. Flat Top (ns)");
|
||||
ImGui::TableSetupColumn("Trap. Rise Time (ns)");
|
||||
ImGui::TableSetupColumn("Flat Top Delay (ns)");
|
||||
ImGui::TableSetupColumn("Smoothing");
|
||||
ImGui::TableSetupColumn("Input Rise Time (ns)");
|
||||
ImGui::TableSetupColumn("Threshold (lsb)");
|
||||
ImGui::TableSetupColumn("Baseline Samples");
|
||||
ImGui::TableSetupColumn("Peak Samples");
|
||||
ImGui::TableSetupColumn("Peak Hold-off (ns)");
|
||||
ImGui::TableSetupColumn("Baseline Hold-off (ns)");
|
||||
ImGui::TableSetupColumn("Trigger Hold-off (ns)");
|
||||
ImGui::TableSetupColumn("Validation Window (ns)");
|
||||
ImGui::TableSetupColumn("Rise Time Disc.");
|
||||
ImGui::TableSetupColumn("Probe Gain");
|
||||
ImGui::TableSetupColumn("Input Samples");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (size_t i = 0; i < m_phaChannels.size(); i++)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%ld", i);
|
||||
ImGui::TableNextColumn();
|
||||
auto& channel = m_phaChannels[i];
|
||||
if (ImGui::RadioButton(fmt::format("##channelEnable_{0}", i).c_str(), channel.isEnabled))
|
||||
{
|
||||
changed |= true;
|
||||
channel.isEnabled = !channel.isEnabled;
|
||||
}
|
||||
if (!channel.isEnabled)
|
||||
ImGui::BeginDisabled();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputScalar(fmt::format("##pretrigger_{0}", i).c_str(), ImGuiDataType_U32, &channel.preTriggerTime))
|
||||
{
|
||||
changed |= true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputFloat(fmt::format("##dcoff_{0}", i).c_str(), &channel.dcOffset))
|
||||
{
|
||||
changed |= true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PolarityToString(channel.pulsePolarity);
|
||||
if (ImGui::BeginCombo(fmt::format("##polarity_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Positive", "Positive" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
|
||||
}
|
||||
if (ImGui::Selectable("Negative", "Negative" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityNegative;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = DynamicRangeToString(channel.dynamicRange);
|
||||
if (ImGui::BeginCombo(fmt::format("##dynamicRange_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("0.5V", "0.5V" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.dynamicRange = DynamicRange::MilliVolt_500;
|
||||
}
|
||||
if (ImGui::Selectable("2.0V", "2.0V" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.dynamicRange = DynamicRange::Volt_2;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##decayTC_{0}", i).c_str(), &channel.decayTimeConst, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##trapFlatTop_{0}", i).c_str(), &channel.trapFlatTop, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##trapRiseT_{0}", i).c_str(), &channel.trapRiseTime, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##flatTopDelay_{0}", i).c_str(), &channel.decayTimeConst, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = std::to_string(channel.triggerFilterSmoothing);
|
||||
if(ImGui::BeginCombo(fmt::format("##smoothing_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
int val;
|
||||
for (int pow = 0; pow < 6; pow++)
|
||||
{
|
||||
val = Power(2, pow);
|
||||
if (ImGui::Selectable(std::to_string(val).c_str(), val == channel.triggerFilterSmoothing))
|
||||
{
|
||||
changed = true;
|
||||
channel.triggerFilterSmoothing = val;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##inputRiseTime_{0}", i).c_str(), &channel.inputRiseTime, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##triggerThresh_{0}", i).c_str(), &channel.triggerThreshold, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = std::to_string(Power(4, 1+channel.samplesBaseLineMean));
|
||||
if (ImGui::BeginCombo(fmt::format("##samplesBase_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
int val;
|
||||
for (int pow = 1; pow < 7; pow++)
|
||||
{
|
||||
val = Power(4, pow + 1);
|
||||
if (ImGui::Selectable(std::to_string(val).c_str(), pow == channel.samplesBaseLineMean))
|
||||
{
|
||||
changed = true;
|
||||
channel.samplesBaseLineMean = pow;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = std::to_string(Power(4, channel.samplesPeakMean));
|
||||
if (ImGui::BeginCombo(fmt::format("##samplesPeak_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
int val;
|
||||
for (int pow = 0; pow < 4; pow++)
|
||||
{
|
||||
val = Power(4, pow);
|
||||
if (ImGui::Selectable(std::to_string(val).c_str(), pow == channel.samplesPeakMean))
|
||||
{
|
||||
changed = true;
|
||||
channel.samplesPeakMean = pow;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##peakHold_{0}", i).c_str(), &channel.peakHoldOff, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##baseHold_{0}", i).c_str(), &channel.baseLineHoldOff, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##trigHold_{0}", i).c_str(), &channel.triggerHoldOff, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##validWin_{0}", i).c_str(), &channel.riseTimeValidationWindow, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = IntSwitchToString(channel.riseTimeDiscrimination);
|
||||
if (ImGui::BeginCombo(fmt::format("##riseTimeDisc_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("On", "On" == tempString))
|
||||
{
|
||||
changed = true;
|
||||
channel.riseTimeDiscrimination = 1;
|
||||
}
|
||||
if (ImGui::Selectable("Off", "Off" == tempString))
|
||||
{
|
||||
changed = true;
|
||||
channel.riseTimeDiscrimination = 0;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = std::to_string(Power(2, channel.digitalProbeGain));
|
||||
if (ImGui::BeginCombo(fmt::format("##digitalGain_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
int value;
|
||||
for (int pow = 0; pow < 4; pow++)
|
||||
{
|
||||
value = Power(2, pow);
|
||||
if (ImGui::Selectable(std::to_string(value).c_str(), pow == channel.digitalProbeGain))
|
||||
{
|
||||
changed = true;
|
||||
channel.digitalProbeGain = pow;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = std::to_string(Power(2, channel.inputDecimation));
|
||||
if (ImGui::BeginCombo(fmt::format("##inputDeci_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
int value;
|
||||
for (int pow = 0; pow < 4; pow++)
|
||||
{
|
||||
value = Power(2, pow);
|
||||
if (ImGui::Selectable(std::to_string(value).c_str(), pow == channel.inputDecimation))
|
||||
{
|
||||
changed = true;
|
||||
channel.inputDecimation = pow;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (!channel.isEnabled)
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::EndDisabled();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool DigitizerPanel::RenderPSDParameters()
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
static std::string tempString; //useful for comps in widgets
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::BeginDisabled();
|
||||
if (ImGui::BeginTable("PSD Channel Parameters", 21, tableFlags | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY, ImVec2(0, 300)))
|
||||
{
|
||||
ImGui::TableSetupColumn("Channel");
|
||||
ImGui::TableSetupColumn("Enable/Disable");
|
||||
ImGui::TableSetupColumn("Pre-Trigger Time (ns)");
|
||||
ImGui::TableSetupColumn("DC Offset (%)");
|
||||
ImGui::TableSetupColumn("Polarity");
|
||||
ImGui::TableSetupColumn("Dynamic Range");
|
||||
ImGui::TableSetupColumn("Baseline Threshold (lsb)");
|
||||
ImGui::TableSetupColumn("Trigger Threshold (lsb)");
|
||||
ImGui::TableSetupColumn("Trigger Hold-Off (ns)");
|
||||
ImGui::TableSetupColumn("Self-Trigger");
|
||||
ImGui::TableSetupColumn("Charge Sensitivity");
|
||||
ImGui::TableSetupColumn("Short Gate (ns)");
|
||||
ImGui::TableSetupColumn("Long Gate (ns)");
|
||||
ImGui::TableSetupColumn("Pre-Gate (ns)");
|
||||
ImGui::TableSetupColumn("Validation Window (ns)");
|
||||
ImGui::TableSetupColumn("Baseline Samples");
|
||||
ImGui::TableSetupColumn("Discrimintaor Mode");
|
||||
ImGui::TableSetupColumn("CFD Fraction");
|
||||
ImGui::TableSetupColumn("CFD Delay (ns)");
|
||||
ImGui::TableSetupColumn("PileUp Reject.");
|
||||
ImGui::TableSetupColumn("Purity Gap");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (size_t i = 0; i < m_psdChannels.size(); i++)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%ld", i);
|
||||
ImGui::TableNextColumn();
|
||||
auto& channel = m_psdChannels[i];
|
||||
if (ImGui::RadioButton(fmt::format("##channelEnable_{0}", i).c_str(), channel.isEnabled))
|
||||
{
|
||||
changed |= true;
|
||||
channel.isEnabled = !channel.isEnabled;
|
||||
}
|
||||
if (!channel.isEnabled)
|
||||
ImGui::BeginDisabled();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputScalar(fmt::format("##pretrigger_{0}", i).c_str(), ImGuiDataType_U32, &channel.preTriggerTime))
|
||||
{
|
||||
changed |= true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputFloat(fmt::format("##dcoff_{0}", i).c_str(), &channel.dcOffset))
|
||||
{
|
||||
changed |= true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PolarityToString(channel.pulsePolarity);
|
||||
if (ImGui::BeginCombo(fmt::format("##polarity_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Positive", "Positive" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityPositive;
|
||||
}
|
||||
if (ImGui::Selectable("Negative", "Negative" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.pulsePolarity = CAEN_DGTZ_PulsePolarityNegative;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = DynamicRangeToString(channel.dynamicRange);
|
||||
if (ImGui::BeginCombo(fmt::format("##dynamicRange_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("0.5V", "0.5V" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.dynamicRange = DynamicRange::MilliVolt_500;
|
||||
}
|
||||
if (ImGui::Selectable("2.0V", "2.0V" == tempString))
|
||||
{
|
||||
changed |= true;
|
||||
channel.dynamicRange = DynamicRange::Volt_2;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##baseThresh_{0}", i).c_str(), &channel.baselineThreshold, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##triggerThresh_{0}", i).c_str(), &channel.triggerThreshold, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##trigHold_{0}", i).c_str(), &channel.triggerHoldOff, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = IntSwitchToString(channel.selfTrigger);
|
||||
if (ImGui::BeginCombo(fmt::format("##selfTrig_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("On", "On" == tempString))
|
||||
{
|
||||
changed = true;
|
||||
channel.selfTrigger = 1;
|
||||
}
|
||||
if (ImGui::Selectable("Off", "Off" == tempString))
|
||||
{
|
||||
changed = true;
|
||||
channel.selfTrigger = 0;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = ChargeSensToString(channel.chargeSensitivity, channel.dynamicRange);
|
||||
if (ImGui::BeginCombo(fmt::format("##charge_sens{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
for (int val = 0; val < 6; val++)
|
||||
{
|
||||
if (ImGui::Selectable(ChargeSensToString(val, channel.dynamicRange).c_str(), val == channel.chargeSensitivity))
|
||||
{
|
||||
changed = true;
|
||||
channel.chargeSensitivity = val;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##shortGate_{0}", i).c_str(), &channel.shortGate, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##longGate_{0}", i).c_str(), &channel.longGate, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##preGate_{0}", i).c_str(), &channel.preGate, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##trigValidWindow_{0}", i).c_str(), &channel.triggerValidationWindow, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = channel.samplesBasline == 0 ? "Absolute" : std::to_string(Power(4, 1 + channel.samplesBasline));
|
||||
if (ImGui::BeginCombo(fmt::format("##samplesBase_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
int val;
|
||||
if (ImGui::Selectable("Absolute", channel.samplesBasline == 0))
|
||||
{
|
||||
changed = true;
|
||||
channel.samplesBasline = 0;
|
||||
}
|
||||
for (int pow = 1; pow < 5; pow++)
|
||||
{
|
||||
val = Power(4, pow + 1);
|
||||
if (ImGui::Selectable(std::to_string(val).c_str(), pow == channel.samplesBasline))
|
||||
{
|
||||
changed = true;
|
||||
channel.samplesBasline = pow;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = DiscriminatorModeToString(channel.discrminatorType);
|
||||
if (ImGui::BeginCombo(fmt::format("##discrMode_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("CFD", CAEN_DGTZ_DPP_DISCR_MODE_CFD == channel.discrminatorType))
|
||||
{
|
||||
changed = true;
|
||||
channel.discrminatorType = CAEN_DGTZ_DPP_DISCR_MODE_CFD;
|
||||
}
|
||||
if (ImGui::Selectable("Leading-Edge", CAEN_DGTZ_DPP_DISCR_MODE_LED == channel.discrminatorType))
|
||||
{
|
||||
changed = true;
|
||||
channel.discrminatorType = CAEN_DGTZ_DPP_DISCR_MODE_LED;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = CFDFractionToString(channel.cfdFraction);
|
||||
if (ImGui::BeginCombo(fmt::format("##cfdFrac_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
for (int val = 0; val < 4; val++)
|
||||
{
|
||||
if (ImGui::Selectable(CFDFractionToString(val).c_str(), val = channel.cfdFraction))
|
||||
{
|
||||
changed = true;
|
||||
channel.cfdFraction = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##cfdDelay_{0}", i).c_str(), &channel.cfdDelay, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PileUpModeToString(channel.pileUpRejection);
|
||||
if (ImGui::BeginCombo(fmt::format("##purSwitch_{0}", i).c_str(), tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("On", CAEN_DGTZ_DPP_PSD_PUR_Enabled == channel.pileUpRejection))
|
||||
{
|
||||
changed = true;
|
||||
channel.discrminatorType = CAEN_DGTZ_DPP_PSD_PUR_Enabled;
|
||||
}
|
||||
if (ImGui::Selectable("Off", CAEN_DGTZ_DPP_PSD_PUR_DetectOnly == channel.pileUpRejection))
|
||||
{
|
||||
changed = true;
|
||||
channel.discrminatorType = CAEN_DGTZ_DPP_PSD_PUR_DetectOnly;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::InputInt(fmt::format("##purityGap_{0}", i).c_str(), &channel.purgap, 0, 0))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!channel.isEnabled)
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::EndDisabled();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool DigitizerPanel::RenderPHAWaveParameters()
|
||||
{
|
||||
bool changed = false;
|
||||
static std::string tempString;
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::BeginDisabled();
|
||||
if (ImGui::BeginTable("PHAWaveParams", 3, tableFlags))
|
||||
{
|
||||
ImGui::TableSetupColumn("VirtualProbe1");
|
||||
ImGui::TableSetupColumn("VirtualProbe2");
|
||||
ImGui::TableSetupColumn("DigitalProbe");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PHAVirtualProbe1ToString(m_phaWaves.analogProbe1);
|
||||
if (ImGui::BeginCombo("##Vprobe1", tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Input", m_phaWaves.analogProbe1 == PHAVP1_Input))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe1 = PHAVP1_Input;
|
||||
}
|
||||
if (ImGui::Selectable("Trapezoid", m_phaWaves.analogProbe1 == PHAVP1_Trapezoid))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe1 = PHAVP1_Trapezoid;
|
||||
}
|
||||
if (ImGui::Selectable("Delta", m_phaWaves.analogProbe1 == PHAVP1_Delta))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe1 = PHAVP1_Delta;
|
||||
}
|
||||
if (ImGui::Selectable("Delta2", m_phaWaves.analogProbe1 == PHAVP1_Delta2))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe1 = PHAVP1_Delta2;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PHAVirtualProbe2ToString(m_phaWaves.analogProbe2);
|
||||
if (ImGui::BeginCombo("##Vprobe2", tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Input", m_phaWaves.analogProbe2 == PHAVP2_Input))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe2 = PHAVP2_Input;
|
||||
}
|
||||
if (ImGui::Selectable("Baseline", m_phaWaves.analogProbe2 == PHAVP2_Baseline))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe2 = PHAVP2_Baseline;
|
||||
}
|
||||
if (ImGui::Selectable("Threshold", m_phaWaves.analogProbe2 == PHAVP2_Threshold))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe2 = PHAVP2_Threshold;
|
||||
}
|
||||
if (ImGui::Selectable("Trap. Reduced", m_phaWaves.analogProbe2 == PHAVP2_TrapezoidReduced))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe2 = PHAVP2_TrapezoidReduced;
|
||||
}
|
||||
if (ImGui::Selectable("None", m_phaWaves.analogProbe2 == PHAVP2_None))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.analogProbe2 = PHAVP2_None;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PHADigitalProbeToString(m_phaWaves.digitalProbe1);
|
||||
if (ImGui::BeginCombo("##Dprobe1", tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Acq. Veto", m_phaWaves.digitalProbe1 == PHADP_ACQVeto))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_ACQVeto;
|
||||
}
|
||||
if (ImGui::Selectable("Armed", m_phaWaves.digitalProbe1 == PHADP_Armed))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_Armed;
|
||||
}
|
||||
if (ImGui::Selectable("Baseline Freeze", m_phaWaves.digitalProbe1 == PHADP_BaselineFreeze))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_BaselineFreeze;
|
||||
}
|
||||
if (ImGui::Selectable("BFM Veto", m_phaWaves.digitalProbe1 == PHADP_BFMVeto))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_BFMVeto;
|
||||
}
|
||||
if (ImGui::Selectable("Busy", m_phaWaves.digitalProbe1 == PHADP_Busy))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_Busy;
|
||||
}
|
||||
if (ImGui::Selectable("Coincidence Window", m_phaWaves.digitalProbe1 == PHADP_CoincidenceWindow))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_CoincidenceWindow;
|
||||
}
|
||||
if (ImGui::Selectable("Ext. Trigger", m_phaWaves.digitalProbe1 == PHADP_ExternalTrig))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_ExternalTrig;
|
||||
}
|
||||
if (ImGui::Selectable("Peaking", m_phaWaves.digitalProbe1 == PHADP_Peaking))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_Peaking;
|
||||
}
|
||||
if (ImGui::Selectable("PileUp", m_phaWaves.digitalProbe1 == PHADP_PileUp))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_PileUp;
|
||||
}
|
||||
if (ImGui::Selectable("PrgVeto", m_phaWaves.digitalProbe1 == PHADP_PrgVeto))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_PrgVeto;
|
||||
}
|
||||
if (ImGui::Selectable("Trig. Hold-Off", m_phaWaves.digitalProbe1 == PHADP_TriggerHoldoff))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_TriggerHoldoff;
|
||||
}
|
||||
if (ImGui::Selectable("Trig. Value", m_phaWaves.digitalProbe1 == PHADP_TriggerValue))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_TriggerValue;
|
||||
}
|
||||
if (ImGui::Selectable("Trig. Window", m_phaWaves.digitalProbe1 == PHADP_TriggerWindow))
|
||||
{
|
||||
changed = true;
|
||||
m_phaWaves.digitalProbe1 = PHADP_TriggerWindow;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::EndDisabled();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool DigitizerPanel::RenderPSDWaveParameters()
|
||||
{
|
||||
bool changed = false;
|
||||
static std::string tempString;
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::BeginDisabled();
|
||||
if (ImGui::BeginTable("PSDWaveParams", 4, tableFlags))
|
||||
{
|
||||
ImGui::TableSetupColumn("VirtualProbe1");
|
||||
ImGui::TableSetupColumn("VirtualProbe2");
|
||||
ImGui::TableSetupColumn("DigitalProbe1");
|
||||
ImGui::TableSetupColumn("DigitalProbe2");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PSDVirtualProbe1ToString(m_psdWaves.analogProbe1);
|
||||
if (ImGui::BeginCombo("##virtprobe1", tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Input", m_psdWaves.analogProbe1 == PSDVP1_Input))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.analogProbe1 = PSDVP1_Input;
|
||||
}
|
||||
if (ImGui::Selectable("CFD", m_psdWaves.analogProbe1 == PSDVP1_CFD))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.analogProbe1 = PSDVP1_CFD;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PSDVirtualProbe2ToString(m_psdWaves.analogProbe2);
|
||||
if (ImGui::BeginCombo("##virtprobe2", tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Baseline", m_psdWaves.analogProbe2 == PSDVP2_Baseline))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.analogProbe2 = PSDVP2_Baseline;
|
||||
}
|
||||
if (ImGui::Selectable("CFD", m_psdWaves.analogProbe2 == PSDVP2_CFD))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.analogProbe2 = PSDVP2_CFD;
|
||||
}
|
||||
if (ImGui::Selectable("None", m_psdWaves.analogProbe2 == PSDVP2_None))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.analogProbe2 = PSDVP2_None;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PSDDigitalProbe1ToString(m_psdWaves.digitalProbe1);
|
||||
if (ImGui::BeginCombo("##digiprobe1", tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Coincidence", m_psdWaves.digitalProbe1 == PSDDP1_Coincidence))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe1 = PSDDP1_Coincidence;
|
||||
}
|
||||
if (ImGui::Selectable("Coincidence Window", m_psdWaves.digitalProbe1 == PSDDP1_CoincidenceWindow))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe1 = PSDDP1_CoincidenceWindow;
|
||||
}
|
||||
if (ImGui::Selectable("Gate", m_psdWaves.digitalProbe1 == PSDDP1_Gate))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe1 = PSDDP1_Gate;
|
||||
}
|
||||
if (ImGui::Selectable("Over Threshold", m_psdWaves.digitalProbe1 == PSDDP1_OverThreshold))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe1 = PSDDP1_OverThreshold;
|
||||
}
|
||||
if (ImGui::Selectable("PileUp", m_psdWaves.digitalProbe1 == PSDDP1_PileUp))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe1 = PSDDP1_PileUp;
|
||||
}
|
||||
if (ImGui::Selectable("Trigger", m_psdWaves.digitalProbe1 == PSDDP1_Trigger))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe1 = PSDDP1_Trigger;
|
||||
}
|
||||
if (ImGui::Selectable("TriggerOut", m_psdWaves.digitalProbe1 == PSDDP1_TriggerOut))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe1 = PSDDP1_TriggerOut;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
tempString = PSDDigitalProbe2ToString(m_psdWaves.digitalProbe2);
|
||||
if (ImGui::BeginCombo("##digiprobe2", tempString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Coincidence", m_psdWaves.digitalProbe2 == PSDDP2_Coincidence))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe2 = PSDDP2_Coincidence;
|
||||
}
|
||||
if (ImGui::Selectable("Short Gate", m_psdWaves.digitalProbe2 == PSDDP2_GateShort))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe2 = PSDDP2_GateShort;
|
||||
}
|
||||
if (ImGui::Selectable("Over Threshold", m_psdWaves.digitalProbe2 == PSDDP2_OverThreshold))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe2 = PSDDP2_OverThreshold;
|
||||
}
|
||||
if (ImGui::Selectable("PileUp", m_psdWaves.digitalProbe2 == PSDDP2_PileUp))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe2 = PSDDP2_PileUp;
|
||||
}
|
||||
if (ImGui::Selectable("Trigger", m_psdWaves.digitalProbe2 == PSDDP2_Trigger))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe2 = PSDDP2_Trigger;
|
||||
}
|
||||
if (ImGui::Selectable("Trig. Hold-Off", m_psdWaves.digitalProbe2 == PSDDP2_TriggerHoldoff))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe2 = PSDDP2_TriggerHoldoff;
|
||||
}
|
||||
if (ImGui::Selectable("Trig. Value", m_psdWaves.digitalProbe2 == PSDDP2_TriggerVal))
|
||||
{
|
||||
changed = true;
|
||||
m_psdWaves.digitalProbe2 = PSDDP2_TriggerVal;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (!m_digitizerEnabled)
|
||||
ImGui::EndDisabled();
|
||||
return changed;
|
||||
}
|
||||
}
|
64
src/Editor/DigitizerPanel.h
Normal file
64
src/Editor/DigitizerPanel.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
#ifndef DIGITIZER_PANEL_H
|
||||
#define DIGITIZER_PANEL_H
|
||||
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class DigitizerPanel
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
PHA,
|
||||
PSD,
|
||||
None
|
||||
};
|
||||
|
||||
DigitizerPanel(const DigitizerArgs& args);
|
||||
~DigitizerPanel();
|
||||
|
||||
void SetDigitizerParameters(const DigitizerParameters& params);
|
||||
void SetPHAParameters(const std::vector<PHAParameters>& ch_params, const PHAWaveParameters& wv_params);
|
||||
void SetPSDParameters(const std::vector<PSDParameters>& ch_params, const PSDWaveParameters& wv_params);
|
||||
|
||||
//Retrieve settings
|
||||
const DigitizerParameters& GetDigitizerParameters() const { return m_params; }
|
||||
const std::vector<PHAParameters>& GetPHAChannelParameters() const { return m_phaChannels; }
|
||||
const std::vector<PSDParameters>& GetPSDChannelParameters() const { return m_psdChannels; }
|
||||
const PHAWaveParameters& GetPHAWaveParameters() const { return m_phaWaves; }
|
||||
const PSDWaveParameters& GetPSDWaveParameters() const { return m_psdWaves; }
|
||||
|
||||
Type GetPanelType() const { return m_panelType; }
|
||||
int GetDigitizerHandle() { return m_args.handle; }
|
||||
|
||||
bool OnImGuiRender();
|
||||
|
||||
private:
|
||||
void Init();
|
||||
bool RenderDigitizerParams();
|
||||
bool RenderPHAParameters();
|
||||
bool RenderPSDParameters();
|
||||
bool RenderPHAWaveParameters();
|
||||
bool RenderPSDWaveParameters();
|
||||
|
||||
DigitizerArgs m_args;
|
||||
DigitizerParameters m_params;
|
||||
std::vector<PHAParameters> m_phaChannels;
|
||||
std::vector<PSDParameters> m_psdChannels;
|
||||
PHAWaveParameters m_phaWaves;
|
||||
PSDWaveParameters m_psdWaves;
|
||||
Type m_panelType;
|
||||
|
||||
std::string m_panelName;
|
||||
std::string m_settingsFile;
|
||||
|
||||
bool m_digitizerEnabled;
|
||||
|
||||
const ImGuiTableFlags tableFlags = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Hideable;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
364
src/Editor/EditorLayer.cpp
Normal file
364
src/Editor/EditorLayer.cpp
Normal file
|
@ -0,0 +1,364 @@
|
|||
#include "EditorLayer.h"
|
||||
#include "Core/Application.h"
|
||||
#include "Events/AcqEvent.h"
|
||||
#include "Core/ProjectSerializer.h"
|
||||
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
EditorLayer::EditorLayer(const DYProject::Ref& project) :
|
||||
Layer("EditorLayer"), m_project(project), m_scopePanel(nullptr)
|
||||
{
|
||||
m_projectPath = m_project->GetProjectPath().string();
|
||||
m_runNumber = m_project->GetRunNumber();
|
||||
m_digitizerArgList = m_project->GetDigitizerArgsList();
|
||||
|
||||
//temp
|
||||
m_digiPanels.emplace_back(DigitizerArgs());
|
||||
}
|
||||
|
||||
EditorLayer::~EditorLayer()
|
||||
{
|
||||
delete m_scopePanel;
|
||||
}
|
||||
|
||||
void EditorLayer::OnAttach() {}
|
||||
|
||||
void EditorLayer::OnDetach() {}
|
||||
|
||||
void EditorLayer::OnUpdate()
|
||||
{
|
||||
if (m_scopePanel)
|
||||
m_scopePanel->OnUpdate();
|
||||
}
|
||||
|
||||
void EditorLayer::OnEvent(Event& e)
|
||||
{
|
||||
}
|
||||
|
||||
void EditorLayer::UpdateDigitizerPanels()
|
||||
{
|
||||
if (m_project->GetNumberOfBoards() != m_digiPanels.size())
|
||||
{
|
||||
m_digitizerArgList = m_project->GetDigitizerArgsList();
|
||||
m_digiPanels.clear();
|
||||
for (auto& args : m_digitizerArgList)
|
||||
m_digiPanels.emplace_back(args);
|
||||
}
|
||||
|
||||
std::vector<DigitizerParameters> boardParams = m_project->GetDigitizerParameterList();
|
||||
int handle;
|
||||
for (auto& panel : m_digiPanels)
|
||||
{
|
||||
handle = panel.GetDigitizerHandle();
|
||||
if (handle == -1)
|
||||
continue;
|
||||
panel.SetDigitizerParameters(boardParams[handle]);
|
||||
switch (panel.GetPanelType())
|
||||
{
|
||||
case DigitizerPanel::Type::PHA: panel.SetPHAParameters(m_project->GetPHAParameters(handle), m_project->GetPHAWaveParameters(handle)); break;
|
||||
case DigitizerPanel::Type::PSD: panel.SetPSDParameters(m_project->GetPSDParameters(handle), m_project->GetPSDWaveParameters(handle)); break;
|
||||
case DigitizerPanel::Type::None: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorLayer::OnImGuiRender(double timestep)
|
||||
{
|
||||
static uint32_t stepSize = 1;
|
||||
static uint32_t fastStepSize = 5;
|
||||
static bool autoIncrFlag = true;
|
||||
static std::string dppModeString = "";
|
||||
static bool dataServer = false;
|
||||
static bool writeDataToDisk = false;
|
||||
static bool showScalars = false;
|
||||
static std::string runString = "Start Run";
|
||||
static bool disableAll = false;
|
||||
|
||||
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
|
||||
// because it would be confusing to have two docking targets within each others.
|
||||
if (opt_fullscreen)
|
||||
{
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->Pos);
|
||||
ImGui::SetNextWindowSize(viewport->Size);
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
||||
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
||||
}
|
||||
|
||||
// When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background.
|
||||
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
|
||||
window_flags |= ImGuiWindowFlags_NoBackground;
|
||||
|
||||
// Important: note that we proceed even if Begin() returns false (aka window is collapsed).
|
||||
// This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
|
||||
// all active windows docked into it will lose their parent and become undocked.
|
||||
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
|
||||
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::Begin("MyTestSpace", &dockspaceOpen, window_flags);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
if (opt_fullscreen)
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
// DockSpace
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
float minWinSizeX = style.WindowMinSize.x;
|
||||
style.WindowMinSize.x = 370.0f;
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
||||
{
|
||||
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
|
||||
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
|
||||
}
|
||||
|
||||
style.WindowMinSize.x = minWinSizeX;
|
||||
|
||||
if (disableAll)
|
||||
ImGui::BeginDisabled();
|
||||
|
||||
//Menu bar
|
||||
if (ImGui::BeginMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("File"))
|
||||
{
|
||||
if (ImGui::MenuItem("Open Project..."))
|
||||
{
|
||||
m_fileDialog.OpenDialog(FileDialog::Type::OpenDir);
|
||||
}
|
||||
if (ImGui::MenuItem("Save Project"))
|
||||
{
|
||||
SaveProject();
|
||||
}
|
||||
if (ImGui::MenuItem("Save Project As..."))
|
||||
{
|
||||
m_fileDialog.OpenDialog(FileDialog::Type::SaveDir);
|
||||
}
|
||||
if (ImGui::MenuItem("Exit"))
|
||||
{
|
||||
WindowCloseEvent wc_event;
|
||||
m_eventCallback(wc_event);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Digitizer"))
|
||||
{
|
||||
if (ImGui::MenuItem("Scan for boards..."))
|
||||
{
|
||||
AcqDetectBoardsEvent db_event;
|
||||
m_eventCallback(db_event);
|
||||
UpdateDigitizerPanels();
|
||||
}
|
||||
if (ImGui::MenuItem("Disconnect boards..."))
|
||||
{
|
||||
AcqDisconnectBoardsEvent db_event;
|
||||
m_eventCallback(db_event);
|
||||
UpdateDigitizerPanels();
|
||||
}
|
||||
if (ImGui::MenuItem("Synchronize boards..."))
|
||||
{
|
||||
m_syncDialog.OpenDialog();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
if (disableAll)
|
||||
ImGui::EndDisabled();
|
||||
//End of menu bar
|
||||
|
||||
//Begin main ImGui window
|
||||
if (ImGui::Begin("Controls"))
|
||||
{
|
||||
//Begin internal tab bar
|
||||
if (ImGui::BeginTabBar("BS_TabBar", ImGuiTabBarFlags_None))
|
||||
{
|
||||
if (ImGui::BeginTabItem("Project"))
|
||||
{
|
||||
if (ImGui::Button(runString.c_str()))
|
||||
{
|
||||
if (runString == "Start Run")
|
||||
{
|
||||
AcqStartEvent e(writeDataToDisk, dataServer);
|
||||
m_eventCallback(e);
|
||||
runString = "Stop Run";
|
||||
disableAll = true;
|
||||
}
|
||||
else if (runString == "Stop Run")
|
||||
{
|
||||
AcqStopEvent e;
|
||||
m_eventCallback(e);
|
||||
runString = "Start Run";
|
||||
disableAll = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (disableAll)
|
||||
ImGui::BeginDisabled();
|
||||
|
||||
ImGui::Text("Project Directory: ");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", m_projectPath.c_str());
|
||||
|
||||
ImGui::InputScalar("Run Number", ImGuiDataType_U32, &m_runNumber, &stepSize, &fastStepSize);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Auto-increment", &autoIncrFlag);
|
||||
|
||||
if (ImGui::BeginCombo("DPP Acquisition Mode", dppModeString.c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("List", dppModeString == "List"))
|
||||
{
|
||||
dppModeString = "List";
|
||||
m_project->SetDPPAcqMode(DPPAcqMode::List);
|
||||
AcqDPPModeEvent e;
|
||||
m_eventCallback(e);
|
||||
|
||||
delete m_scopePanel;
|
||||
m_scopePanel = nullptr;
|
||||
}
|
||||
if (ImGui::Selectable("Waves", dppModeString == "Waves"))
|
||||
{
|
||||
dppModeString = "Waves";
|
||||
m_project->SetDPPAcqMode(DPPAcqMode::Waves);
|
||||
AcqDPPModeEvent e;
|
||||
m_eventCallback(e);
|
||||
|
||||
m_scopePanel = new ScopePanel(m_digiPanels.size());
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
//These don't really do anything right now, to be implemented
|
||||
ImGui::Checkbox("Write data to disk", &writeDataToDisk);
|
||||
ImGui::Checkbox("Write data to server", &dataServer);
|
||||
|
||||
|
||||
if (ImGui::Checkbox("Show Scalars", &showScalars))
|
||||
{
|
||||
if (showScalars)
|
||||
m_scalarPanel.ResetScalars();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
bool panel_status;
|
||||
for (auto& panel : m_digiPanels)
|
||||
{
|
||||
panel_status = panel.OnImGuiRender();
|
||||
//handle state change
|
||||
if (panel_status)
|
||||
{
|
||||
//Update project
|
||||
m_project->SetDigitizerParameters(panel.GetDigitizerHandle(), panel.GetDigitizerParameters());
|
||||
if (panel.GetPanelType() == DigitizerPanel::Type::PHA)
|
||||
{
|
||||
m_project->SetPHAParameters(panel.GetDigitizerHandle(), panel.GetPHAChannelParameters());
|
||||
m_project->SetPHAWaveParameters(panel.GetDigitizerHandle(), panel.GetPHAWaveParameters());
|
||||
}
|
||||
else if (panel.GetPanelType() == DigitizerPanel::Type::PSD)
|
||||
{
|
||||
m_project->SetPSDParameters(panel.GetDigitizerHandle(), panel.GetPSDChannelParameters());
|
||||
m_project->SetPSDWaveParameters(panel.GetDigitizerHandle(), panel.GetPSDWaveParameters());
|
||||
}
|
||||
else
|
||||
{
|
||||
DY_WARN("Unidentified digitizer type attempting to communicate with project");
|
||||
}
|
||||
//Emit event to update aquisition side
|
||||
AcqParametersEvent e(DigitizerAccessType::Single, panel.GetDigitizerHandle());
|
||||
m_eventCallback(e);
|
||||
}
|
||||
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
//End internal tab bar
|
||||
|
||||
if (disableAll)
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
//Render the scope if needed
|
||||
if (m_scopePanel)
|
||||
m_scopePanel->OnImGuiRender();
|
||||
|
||||
if (showScalars)
|
||||
m_scalarPanel.OnImGuiRender(m_digitizerArgList, timestep);
|
||||
|
||||
//Render file dialog if needed
|
||||
auto fd_result = m_fileDialog.RenderFileDialog(".yaml");
|
||||
if (!fd_result.first.empty())
|
||||
{
|
||||
switch (fd_result.second)
|
||||
{
|
||||
case FileDialog::Type::OpenDir:
|
||||
{
|
||||
std::filesystem::path dir(fd_result.first);
|
||||
if (!std::filesystem::exists(dir / "settings.yaml"))
|
||||
{
|
||||
DY_ERROR("Project settings file {0} does not exist! Cannot be opened!", dir.string());
|
||||
break;
|
||||
}
|
||||
ProjectSerializer serializer(dir / "settings.yaml");
|
||||
serializer.DeserializeData(m_project);
|
||||
m_projectPath = m_project->GetProjectPath().string();
|
||||
UpdateDigitizerPanels();
|
||||
AcqParametersEvent e(DigitizerAccessType::All);
|
||||
m_eventCallback(e);
|
||||
break;
|
||||
}
|
||||
case FileDialog::Type::SaveDir:
|
||||
{
|
||||
bool status = m_project->SetProjectPath(fd_result.first);
|
||||
std::filesystem::path path(fd_result.first);
|
||||
if (!status)
|
||||
{
|
||||
DY_ERROR("Project directory {0} could not be created! Settings not saved.", fd_result.first);
|
||||
break;
|
||||
}
|
||||
ProjectSerializer serializer(m_project->GetProjectPath() / "settings.yaml");
|
||||
serializer.SerializeData(m_project);
|
||||
m_projectPath = m_project->GetProjectPath().string();
|
||||
break;
|
||||
}
|
||||
case FileDialog::Type::OpenFile: break; //Unused
|
||||
case FileDialog::Type::SaveFile: break; //Unused
|
||||
case FileDialog::Type::None: break; //Null result
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//render sync dialog if needed
|
||||
bool test = m_syncDialog.OnImGuiRender();
|
||||
if (test)
|
||||
{
|
||||
AcqSyncArgsEvent e = m_syncDialog.GetSyncEvent();
|
||||
m_eventCallback(e);
|
||||
UpdateDigitizerPanels();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void EditorLayer::SaveProject()
|
||||
{
|
||||
const std::filesystem::path& path = m_project->GetProjectPath();
|
||||
if (!path.empty())
|
||||
{
|
||||
ProjectSerializer serializer(m_project->GetProjectPath() / "settings.yaml");
|
||||
serializer.SerializeData(m_project);
|
||||
}
|
||||
else
|
||||
m_fileDialog.OpenDialog(FileDialog::Type::SaveDir);
|
||||
}
|
||||
}
|
60
src/Editor/EditorLayer.h
Normal file
60
src/Editor/EditorLayer.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef EDITOR_LAYER_H
|
||||
#define EDITOR_LAYER_H
|
||||
|
||||
#include "Core/DYCore.h"
|
||||
#include "Core/Layer.h"
|
||||
#include "FileDialog.h"
|
||||
#include "Events/AcqEvent.h"
|
||||
#include "Core/DYProject.h"
|
||||
#include "DigitizerPanel.h"
|
||||
#include "SyncDialog.h"
|
||||
#include "ScopePanel.h"
|
||||
#include "ScalarPanel.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class EditorLayer : public Layer
|
||||
{
|
||||
public:
|
||||
using EventCallbackFunc = std::function<void(Event&)>;
|
||||
|
||||
EditorLayer(const DYProject::Ref& project);
|
||||
~EditorLayer();
|
||||
|
||||
void SetEventCallback(const EventCallbackFunc& func) { m_eventCallback = func; }
|
||||
|
||||
virtual void OnAttach() override;
|
||||
virtual void OnDetach() override;
|
||||
virtual void OnUpdate() override;
|
||||
virtual void OnEvent(Event& e) override;
|
||||
virtual void OnImGuiRender(double timestep) override;
|
||||
|
||||
private:
|
||||
void UpdateDigitizerPanels();
|
||||
void SaveProject(); //For call to save project to current path
|
||||
|
||||
EventCallbackFunc m_eventCallback;
|
||||
|
||||
DYProject::Ref m_project;
|
||||
std::string m_projectPath;
|
||||
uint32_t m_runNumber;
|
||||
std::vector<DigitizerArgs> m_digitizerArgList;
|
||||
|
||||
FileDialog m_fileDialog;
|
||||
std::vector<DigitizerPanel> m_digiPanels;
|
||||
SyncDialog m_syncDialog;
|
||||
ScopePanel* m_scopePanel;
|
||||
ScalarPanel m_scalarPanel;
|
||||
|
||||
//ImGui Settings
|
||||
bool dockspaceOpen = true;
|
||||
bool opt_fullscreen = true;
|
||||
bool opt_fullscreen_persistant = true;
|
||||
ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
|
||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
281
src/Editor/FileDialog.cpp
Normal file
281
src/Editor/FileDialog.cpp
Normal file
|
@ -0,0 +1,281 @@
|
|||
#include "FileDialog.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//Helper function to handle file size printing
|
||||
std::string ConvertFileSystemSizeToString(std::uintmax_t value)
|
||||
{
|
||||
int i = 0;
|
||||
double mantissa = (double)value;
|
||||
for (; mantissa >= 1024.0; ++i)
|
||||
mantissa /= 1024.0;
|
||||
mantissa = std::ceil(mantissa * 10.0) / 10.0;
|
||||
return std::to_string(int(mantissa)) + "BKMGTPE"[i];
|
||||
}
|
||||
|
||||
FileDialog::FileDialog() :
|
||||
m_currentPath(std::filesystem::current_path()), m_type(Type::None), m_selectedItem(""), m_openDialogFlag(false)
|
||||
{
|
||||
table_flags = ImGuiTableFlags_BordersH | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_RowBg;
|
||||
select_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups;
|
||||
}
|
||||
|
||||
FileDialog::~FileDialog() {}
|
||||
|
||||
//Each type of action has its own render function
|
||||
|
||||
std::pair<std::string, FileDialog::Type> FileDialog::RenderFileDialog(const std::string& ext)
|
||||
{
|
||||
if (m_openDialogFlag)
|
||||
{
|
||||
m_selectedItem = "";
|
||||
m_openDialogFlag = false;
|
||||
m_currentPath = std::filesystem::current_path();
|
||||
ImGui::OpenPopup("File Dialog");
|
||||
}
|
||||
|
||||
//Open & Save Dir are equivalent operations, but can be useful to specify for handling later
|
||||
std::string result = "";
|
||||
if (ImGui::BeginPopupModal("File Dialog"))
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::OpenFile:
|
||||
{
|
||||
result = ImGuiRenderOpenFile(ext);
|
||||
break;
|
||||
}
|
||||
case Type::SaveFile:
|
||||
{
|
||||
result = ImGuiRenderSaveFile(ext);
|
||||
break;
|
||||
}
|
||||
case Type::OpenDir:
|
||||
{
|
||||
result = ImGuiRenderOpenDir();
|
||||
break;
|
||||
}
|
||||
case Type::SaveDir:
|
||||
{
|
||||
result = ImGuiRenderOpenDir();
|
||||
break;
|
||||
}
|
||||
case Type::None: break;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
return std::make_pair(result, m_type);
|
||||
}
|
||||
|
||||
std::string FileDialog::ImGuiRenderOpenFile(const std::string& ext)
|
||||
{
|
||||
std::string result = "";
|
||||
std::string text = "";
|
||||
|
||||
ImGui::Text("%s", ("Current Directory: " + m_currentPath.lexically_normal().string()).c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", ("Extension Filter: " + ext).c_str());
|
||||
ImGui::InputText("Selected", &m_selectedItem);
|
||||
if (ImGui::Button("Ok"))
|
||||
{
|
||||
std::filesystem::path filepath = m_currentPath / m_selectedItem;
|
||||
result = filepath.string();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
|
||||
{
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Size");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(" ..", false, select_flags))
|
||||
{
|
||||
m_selectedItem.clear();
|
||||
m_currentPath.append("..");
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("N/A");
|
||||
for (auto& entry : std::filesystem::directory_iterator(m_currentPath, std::filesystem::directory_options::skip_permission_denied))
|
||||
{
|
||||
if (entry.is_directory())
|
||||
{
|
||||
try //Windows C:/ errors can only be try caught.
|
||||
{
|
||||
text = std::filesystem::relative(entry.path(), m_currentPath).string();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(text.c_str(), false, select_flags))
|
||||
{
|
||||
m_selectedItem.clear();
|
||||
m_currentPath /= entry.path();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("N/A");
|
||||
}
|
||||
else if (entry.path().filename().extension() == ext)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
text = entry.path().filename().string();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(text.c_str(), false, select_flags))
|
||||
m_selectedItem = entry.path().filename().string();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string FileDialog::ImGuiRenderSaveFile(const std::string& ext)
|
||||
{
|
||||
std::string result = "";
|
||||
std::string text = "";
|
||||
|
||||
ImGui::Text("%s", ("Current Directory: " + m_currentPath.lexically_normal().string()).c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", ("Extension Filter: " + ext).c_str());
|
||||
ImGui::InputText("Selected", &m_selectedItem);
|
||||
if (ImGui::Button("Ok"))
|
||||
{
|
||||
std::filesystem::path filepath = m_currentPath / m_selectedItem;
|
||||
result = filepath.string();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
|
||||
{
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Size");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(" ..", false, select_flags))
|
||||
{
|
||||
m_selectedItem.clear();
|
||||
m_currentPath.append("..");
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("N/A");
|
||||
for (auto& entry : std::filesystem::directory_iterator(m_currentPath, std::filesystem::directory_options::skip_permission_denied))
|
||||
{
|
||||
if (entry.is_directory())
|
||||
{
|
||||
try //Windows C:/ errors can only be try caught.
|
||||
{
|
||||
text = std::filesystem::relative(entry.path(), m_currentPath).string();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(text.c_str(), false, select_flags))
|
||||
{
|
||||
m_selectedItem.clear();
|
||||
m_currentPath /= entry.path();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("N/A");
|
||||
}
|
||||
else if (entry.path().filename().extension() == ext)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
text = entry.path().filename().string();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(text.c_str(), false, select_flags))
|
||||
m_selectedItem = entry.path().filename().string();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string FileDialog::ImGuiRenderOpenDir()
|
||||
{
|
||||
std::string result = "";
|
||||
std::string text = "";
|
||||
|
||||
ImGui::Text("%s", ("Current Directory: " + m_currentPath.lexically_normal().string()).c_str());
|
||||
ImGui::InputText("Selected", &m_selectedItem);
|
||||
if (ImGui::Button("Ok"))
|
||||
{
|
||||
result = m_selectedItem;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
|
||||
{
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Size");
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(" ..", false, select_flags))
|
||||
{
|
||||
m_currentPath.append("..");
|
||||
m_selectedItem = m_currentPath.string();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("N/A");
|
||||
for (auto& entry : std::filesystem::directory_iterator(m_currentPath, std::filesystem::directory_options::skip_permission_denied))
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
if (entry.is_directory())
|
||||
{
|
||||
try //Windows C:/ errors can only be try caught.
|
||||
{
|
||||
text = std::filesystem::relative(entry.path(), m_currentPath).string();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(text.c_str(), false, select_flags))
|
||||
{
|
||||
m_currentPath /= entry.path();
|
||||
m_selectedItem = m_currentPath.string();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("N/A");
|
||||
}
|
||||
else
|
||||
{
|
||||
text = entry.path().filename().string();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
46
src/Editor/FileDialog.h
Normal file
46
src/Editor/FileDialog.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef FILE_DIALOG_H
|
||||
#define FILE_DIALOG_H
|
||||
|
||||
#include <filesystem>
|
||||
#include "imgui.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class FileDialog
|
||||
{
|
||||
public:
|
||||
|
||||
enum class Type
|
||||
{
|
||||
OpenFile,
|
||||
SaveFile,
|
||||
OpenDir,
|
||||
SaveDir,
|
||||
None
|
||||
};
|
||||
|
||||
FileDialog();
|
||||
~FileDialog();
|
||||
|
||||
inline void OpenDialog(Type type) { m_type = type; m_openDialogFlag = true; }
|
||||
std::pair<std::string, Type> RenderFileDialog(const std::string& ext = "");
|
||||
|
||||
private:
|
||||
std::string ImGuiRenderOpenFile(const std::string& ext);
|
||||
std::string ImGuiRenderSaveFile(const std::string& ext);
|
||||
std::string ImGuiRenderOpenDir();
|
||||
|
||||
std::filesystem::path m_currentPath;
|
||||
Type m_type;
|
||||
|
||||
std::string m_selectedItem;
|
||||
|
||||
bool m_openDialogFlag;
|
||||
|
||||
ImGuiTableFlags table_flags;
|
||||
ImGuiSelectableFlags select_flags;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
71
src/Editor/ScalarPanel.cpp
Normal file
71
src/Editor/ScalarPanel.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "ScalarPanel.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
ScalarPanel::ScalarPanel() :
|
||||
m_elapsedTime(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
ScalarPanel::~ScalarPanel()
|
||||
{
|
||||
}
|
||||
|
||||
void ScalarPanel::OnImGuiRender(const std::vector<DigitizerArgs>& argList, double timestep)
|
||||
{
|
||||
static bool changedBoard = false;
|
||||
m_elapsedTime += timestep;
|
||||
if (m_elapsedTime >= 60.0f) //Update the rates once a minute
|
||||
{
|
||||
ScalarDistributor::CalculateRates(m_elapsedTime);
|
||||
m_elapsedTime = 0.0f; //Reset it
|
||||
}
|
||||
|
||||
if (ImGui::Begin("ScalarPanel"))
|
||||
{
|
||||
if (ImGui::BeginCombo("Selected Board", m_selectedArgs.name.c_str()))
|
||||
{
|
||||
for (auto& args : argList)
|
||||
{
|
||||
if (ImGui::Selectable(args.name.c_str(), m_selectedArgs.name == args.name))
|
||||
{
|
||||
m_selectedArgs = args;
|
||||
changedBoard = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (changedBoard)
|
||||
{
|
||||
m_scalarNames.resize(m_selectedArgs.channels);
|
||||
for (int i = 0; i < m_selectedArgs.channels; i++)
|
||||
m_scalarNames[i] = m_selectedArgs.name + std::to_string(i);
|
||||
|
||||
changedBoard = false;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("ScalarTable", 2))
|
||||
{
|
||||
ImGui::TableSetupColumn("Channel Number");
|
||||
ImGui::TableSetupColumn("Throughput Rate");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (int i = 0; i < m_selectedArgs.channels; i++)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", i);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%f", ScalarDistributor::GetRate(m_scalarNames[i]));
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
27
src/Editor/ScalarPanel.h
Normal file
27
src/Editor/ScalarPanel.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef SCALAR_PANEL_H
|
||||
#define SCALAR_PANEL_H
|
||||
|
||||
#include "Core/ScalarDistributor.h"
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class ScalarPanel
|
||||
{
|
||||
public:
|
||||
ScalarPanel();
|
||||
~ScalarPanel();
|
||||
|
||||
void ResetScalars() { ScalarDistributor::ClearScalars(); m_elapsedTime = 0.0; }
|
||||
|
||||
void OnImGuiRender(const std::vector<DigitizerArgs>& argList, double timestep);
|
||||
|
||||
private:
|
||||
DigitizerArgs m_selectedArgs;
|
||||
std::vector<std::string> m_scalarNames;
|
||||
double m_elapsedTime;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
90
src/Editor/ScopePanel.cpp
Normal file
90
src/Editor/ScopePanel.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include "ScopePanel.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "implot.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
ScopePanel::ScopePanel(int nboards) :
|
||||
m_dataReady(false), m_selectedBoard(0), m_selectedChannel(0), m_maxNumBoards(nboards)
|
||||
{
|
||||
m_dataHandle = DataDistributor::Connect();
|
||||
for (int i = 0; i < nboards; i++)
|
||||
m_boardListForImGui.push_back(fmt::format("{0}", i));
|
||||
for (int i = 0; i < 16; i++) //bad hardcode, fix later
|
||||
m_channelListForImGui.push_back(fmt::format("{0}", i));
|
||||
}
|
||||
|
||||
ScopePanel::~ScopePanel()
|
||||
{
|
||||
DataDistributor::Disconnect(m_dataHandle);
|
||||
}
|
||||
|
||||
void ScopePanel::OnUpdate()
|
||||
{
|
||||
if (!m_dataHandle.dataQueue->IsEmpty())
|
||||
{
|
||||
m_buffer = m_dataHandle.dataQueue->Front();
|
||||
m_dataHandle.dataQueue->PopFront();
|
||||
for (auto& hit : m_buffer)
|
||||
{
|
||||
if (hit.board == m_selectedBoard && hit.channel == m_selectedChannel)
|
||||
{
|
||||
m_selectedHit = hit;
|
||||
m_selectedXAxis.clear();
|
||||
for (int i = 0; i < hit.waveSize; i++)
|
||||
m_selectedXAxis.push_back(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScopePanel::OnImGuiRender()
|
||||
{
|
||||
static std::string selectedBoardString = fmt::format("{0}", m_selectedBoard);
|
||||
static std::string selectedChannelString = fmt::format("{0}", m_selectedChannel);
|
||||
|
||||
if (ImGui::Begin("Oscilloscope"))
|
||||
{
|
||||
if (ImGui::BeginCombo("Board", selectedBoardString.c_str()))
|
||||
{
|
||||
for (int board=0; board<m_maxNumBoards; board++)
|
||||
{
|
||||
if (ImGui::Selectable(m_boardListForImGui[board].c_str(), board == m_selectedBoard))
|
||||
{
|
||||
m_selectedBoard = board;
|
||||
selectedBoardString = fmt::format("{0}", m_selectedBoard);
|
||||
m_selectedXAxis.clear();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::BeginCombo("Channel", selectedChannelString.c_str()))
|
||||
{
|
||||
for (int channel = 0; channel < 16; channel++) //hardcoded bad, fix later
|
||||
{
|
||||
if (ImGui::Selectable(m_channelListForImGui[channel].c_str(), channel == m_selectedChannel))
|
||||
{
|
||||
m_selectedChannel = channel;
|
||||
selectedChannelString = fmt::format("{0}", m_selectedChannel);
|
||||
m_selectedXAxis.clear();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImPlot::BeginPlot("ScopeView", ImVec2(-1,-1)))
|
||||
{
|
||||
if (m_selectedXAxis.size() != 0)
|
||||
{
|
||||
ImPlot::PlotLine("AnalogProbe1", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.trace1Samples.data(), m_selectedXAxis.size());
|
||||
ImPlot::PlotLine("AnalogProbe2", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.trace2Samples.data(), m_selectedXAxis.size());
|
||||
ImPlot::PlotLine("DigitialProbe1", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.digitalTrace1Samples.data(), m_selectedXAxis.size());
|
||||
ImPlot::PlotLine("DigitialProbe2", (ImU16*)m_selectedXAxis.data(), (ImU16*)m_selectedHit.digitalTrace2Samples.data(), m_selectedXAxis.size());
|
||||
}
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
35
src/Editor/ScopePanel.h
Normal file
35
src/Editor/ScopePanel.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef SCOPE_PANEL_H
|
||||
#define SCOPE_PANEL_H
|
||||
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
#include "Core/DataDistributor.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class ScopePanel
|
||||
{
|
||||
public:
|
||||
ScopePanel(int nboards);
|
||||
~ScopePanel();
|
||||
|
||||
void OnUpdate();
|
||||
void OnImGuiRender();
|
||||
|
||||
private:
|
||||
//uint64_t m_consumerID;
|
||||
DistributorClient m_dataHandle;
|
||||
std::vector<DYData> m_buffer; //Buffered data retrieved from ring
|
||||
DYData m_selectedHit; //Hit associated with selected board/channel
|
||||
std::vector<int16_t> m_selectedXAxis; //X data is not given by DAQ, has to be made based on number of wave samples
|
||||
|
||||
bool m_dataReady;
|
||||
int m_selectedBoard;
|
||||
int m_selectedChannel;
|
||||
int m_maxNumBoards;
|
||||
|
||||
std::vector<std::string> m_boardListForImGui;
|
||||
std::vector<std::string> m_channelListForImGui;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
78
src/Editor/SyncDialog.cpp
Normal file
78
src/Editor/SyncDialog.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "SyncDialog.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
static std::string SyncMethodToString(SyncMethod method)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case SyncMethod::SIn_TrigOut: return "SyncIn-TrigOut";
|
||||
case SyncMethod::None: return "None";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
static std::string StartTypeToString(SyncStartType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SyncStartType::HardwareControlled: return "Hardware-Controlled";
|
||||
case SyncStartType::SoftwareControlled: return "Software-Controlled";
|
||||
case SyncStartType::None: return "None";
|
||||
}
|
||||
return "None";
|
||||
}
|
||||
|
||||
SyncDialog::SyncDialog() :
|
||||
m_open(false)
|
||||
{
|
||||
}
|
||||
|
||||
SyncDialog::~SyncDialog() {}
|
||||
|
||||
bool SyncDialog::OnImGuiRender()
|
||||
{
|
||||
bool status = false;
|
||||
if (m_open)
|
||||
{
|
||||
ImGui::OpenPopup("SyncDialog");
|
||||
status = false;
|
||||
m_open = false;
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal("SyncDialog"))
|
||||
{
|
||||
if (ImGui::BeginCombo("Method", SyncMethodToString(m_syncArgs.syncMethod).c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("SyncIn-TrigOut", SyncMethod::SIn_TrigOut == m_syncArgs.syncMethod, ImGuiSelectableFlags_DontClosePopups))
|
||||
m_syncArgs.syncMethod = SyncMethod::SIn_TrigOut;
|
||||
if (ImGui::Selectable("None", SyncMethod::None == m_syncArgs.syncMethod, ImGuiSelectableFlags_DontClosePopups))
|
||||
m_syncArgs.syncMethod = SyncMethod::None;
|
||||
}
|
||||
if (ImGui::BeginCombo("Start type", StartTypeToString(m_syncArgs.startType).c_str()))
|
||||
{
|
||||
if (ImGui::Selectable("Hardware-Controlled", SyncStartType::HardwareControlled == m_syncArgs.startType, ImGuiSelectableFlags_DontClosePopups))
|
||||
m_syncArgs.startType = SyncStartType::HardwareControlled;
|
||||
if (ImGui::Selectable("Software-Controlled", SyncStartType::SoftwareControlled == m_syncArgs.startType, ImGuiSelectableFlags_DontClosePopups))
|
||||
m_syncArgs.startType = SyncStartType::SoftwareControlled;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Ok"))
|
||||
{
|
||||
status = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel"))
|
||||
{
|
||||
status = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
25
src/Editor/SyncDialog.h
Normal file
25
src/Editor/SyncDialog.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef SYNC_DIALOG_H
|
||||
#define SYNC_DIALOG_H
|
||||
|
||||
#include "Events/AcqEvent.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class SyncDialog
|
||||
{
|
||||
public:
|
||||
SyncDialog();
|
||||
~SyncDialog();
|
||||
|
||||
void OpenDialog() { m_open = true; }
|
||||
|
||||
bool OnImGuiRender();
|
||||
AcqSyncArgsEvent GetSyncEvent() { return m_syncArgs; }
|
||||
|
||||
private:
|
||||
bool m_open;
|
||||
SyncArgs m_syncArgs;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
111
src/Events/AcqEvent.h
Normal file
111
src/Events/AcqEvent.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
#ifndef ACQ_EVENT_H
|
||||
#define ACQ_EVENT_H
|
||||
|
||||
#include "Event.h"
|
||||
#include "DAQ/DigitizerDefs.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//For now all pure message, not sure what exactly needed yet
|
||||
|
||||
class AcqStartEvent : public Event
|
||||
{
|
||||
public:
|
||||
AcqStartEvent(bool writeToDisk, bool writeToServer) :
|
||||
m_isWriteToDisk(writeToDisk), m_isWriteToServer(writeToServer)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsWriteToDisk() const { return m_isWriteToDisk; }
|
||||
bool IsWriteToServer() const { return m_isWriteToServer; }
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryAcq);
|
||||
EVENT_TYPE_SETUP(AcqStart);
|
||||
|
||||
private:
|
||||
bool m_isWriteToDisk;
|
||||
bool m_isWriteToServer;
|
||||
};
|
||||
|
||||
class AcqStopEvent : public Event
|
||||
{
|
||||
public:
|
||||
AcqStopEvent() = default;
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryAcq);
|
||||
EVENT_TYPE_SETUP(AcqStop);
|
||||
};
|
||||
|
||||
class AcqParametersEvent : public Event
|
||||
{
|
||||
public:
|
||||
AcqParametersEvent(DigitizerAccessType type, int boardHandle = -1) :
|
||||
m_boardHandle(boardHandle)
|
||||
{
|
||||
}
|
||||
|
||||
int GetBoardHandle() { return m_boardHandle; }
|
||||
DigitizerAccessType GetAccessType() { return m_type; }
|
||||
|
||||
virtual std::string ToString() const override
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << GetName() << " board handle: " << m_boardHandle;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryAcq);
|
||||
EVENT_TYPE_SETUP(AcqParameters);
|
||||
|
||||
private:
|
||||
int m_boardHandle;
|
||||
DigitizerAccessType m_type;
|
||||
};
|
||||
|
||||
class AcqSyncArgsEvent : public Event
|
||||
{
|
||||
public:
|
||||
AcqSyncArgsEvent(SyncArgs& args) :
|
||||
m_args(args)
|
||||
{
|
||||
}
|
||||
|
||||
const SyncArgs& GetArgs() const { return m_args; }
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryAcq);
|
||||
EVENT_TYPE_SETUP(AcqSyncArgs);
|
||||
|
||||
private:
|
||||
SyncArgs m_args;
|
||||
};
|
||||
|
||||
class AcqDetectBoardsEvent : public Event
|
||||
{
|
||||
public:
|
||||
AcqDetectBoardsEvent() = default;
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryAcq);
|
||||
EVENT_TYPE_SETUP(AcqDetectBoards);
|
||||
};
|
||||
|
||||
class AcqDisconnectBoardsEvent : public Event
|
||||
{
|
||||
public:
|
||||
AcqDisconnectBoardsEvent() = default;
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryAcq);
|
||||
EVENT_TYPE_SETUP(AcqDisconnectBoards);
|
||||
};
|
||||
|
||||
class AcqDPPModeEvent : public Event
|
||||
{
|
||||
public:
|
||||
AcqDPPModeEvent() = default;
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryAcq);
|
||||
EVENT_TYPE_SETUP(AcqDPPMode);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
53
src/Events/AppEvent.h
Normal file
53
src/Events/AppEvent.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef APP_EVENT_H
|
||||
#define APP_EVENT_H
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//Pure message, no data transferred
|
||||
class WindowCloseEvent : public Event
|
||||
{
|
||||
public:
|
||||
WindowCloseEvent() = default;
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryApp);
|
||||
EVENT_TYPE_SETUP(WindowClose);
|
||||
};
|
||||
|
||||
class WindowResizeEvent : public Event
|
||||
{
|
||||
public:
|
||||
WindowResizeEvent(uint32_t width, uint32_t height) :
|
||||
m_width(width), m_height(height)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t GetWidth() const { return m_width; }
|
||||
uint32_t GetHeight() const { return m_height; }
|
||||
|
||||
virtual std::string ToString() const override
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << GetName() << "(" << m_width << ", " << m_height << ")";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryApp);
|
||||
EVENT_TYPE_SETUP(WindowResize);
|
||||
|
||||
private:
|
||||
uint32_t m_width, m_height;
|
||||
};
|
||||
|
||||
class AppUpdateEvent : public Event
|
||||
{
|
||||
public:
|
||||
AppUpdateEvent() = default;
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryApp);
|
||||
EVENT_TYPE_SETUP(AppUpdate);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
82
src/Events/Event.h
Normal file
82
src/Events/Event.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
|
||||
#include "Core/DYCore.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
enum class EventType
|
||||
{
|
||||
None=0,
|
||||
WindowClose,WindowResize,
|
||||
KeyPressed, KeyReleased, KeyTyped,
|
||||
MouseButtonPressed, MouseButtonReleased, MouseScrolled, MouseMoved,
|
||||
AppUpdate,
|
||||
AcqStart, AcqStop, AcqParameters, AcqSyncArgs, AcqDetectBoards, AcqDisconnectBoards, AcqDPPMode
|
||||
};
|
||||
|
||||
enum EventCategory
|
||||
{
|
||||
EventCategoryNone=0,
|
||||
EventCategoryKey = BIT(1),
|
||||
EventCategoryMouse = BIT(2),
|
||||
EventCategoryApp = BIT(3),
|
||||
EventCategoryAcq = BIT(4),
|
||||
EventCategoryInput = BIT(5)
|
||||
};
|
||||
|
||||
//Some ease of code generation macros
|
||||
#define EVENT_CATEGORY_SETUP(cat) virtual int GetCategoryFlags() const override { return cat; }
|
||||
|
||||
#define EVENT_TYPE_SETUP(type) static EventType GetStaticType() { return EventType::type; } \
|
||||
virtual EventType GetEventType() const override { return GetStaticType(); } \
|
||||
virtual const char* GetName() const override { return #type; }
|
||||
|
||||
//Event base class
|
||||
class Event
|
||||
{
|
||||
friend class EventDispatcher;
|
||||
public:
|
||||
virtual EventType GetEventType() const = 0;
|
||||
virtual const char* GetName() const = 0;
|
||||
virtual int GetCategoryFlags() const = 0;
|
||||
virtual std::string ToString() const { return GetName(); }
|
||||
bool IsCategory(EventCategory cat) const { return cat & GetCategoryFlags(); }
|
||||
bool handled = false;
|
||||
};
|
||||
|
||||
//Class which wraps event messaging.
|
||||
//When an event is created, a dispatcher is also created. The dispatcher takes the created event,
|
||||
//and passes it to handling callbacks via the Dispatch function. If the event is handled, Dispatch returns true.
|
||||
//If one wants the event to only be handled by a single case, have the callback return true to set the event internal status as handled.
|
||||
class EventDispatcher
|
||||
{
|
||||
public:
|
||||
EventDispatcher(Event& e) :
|
||||
m_event(e)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
bool Dispatch(const F& function)
|
||||
{
|
||||
if (m_event.GetEventType() == T::GetStaticType())
|
||||
{
|
||||
m_event.handled = function(static_cast<T&>(m_event));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
Event& m_event;
|
||||
};
|
||||
|
||||
//So that events can be logged easily
|
||||
inline std::ostream& operator<<(std::ostream& os, const Event& e)
|
||||
{
|
||||
return os << e.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
89
src/Events/KeyEvent.h
Normal file
89
src/Events/KeyEvent.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
#ifndef KEY_EVENT_H
|
||||
#define KEY_EVENT_H
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
//Since so many key related events, have a base
|
||||
class KeyEvent : public Event
|
||||
{
|
||||
public:
|
||||
int GetKeycode() const { return m_keycode; }
|
||||
EVENT_CATEGORY_SETUP(EventCategoryKey | EventCategoryInput)
|
||||
protected:
|
||||
KeyEvent(int code) :
|
||||
m_keycode(code)
|
||||
{
|
||||
}
|
||||
|
||||
int m_keycode;
|
||||
|
||||
};
|
||||
|
||||
class KeyPressedEvent : public KeyEvent
|
||||
{
|
||||
public:
|
||||
KeyPressedEvent(int code, int count) :
|
||||
KeyEvent(code), m_repeatCount(count)
|
||||
{
|
||||
}
|
||||
|
||||
EVENT_TYPE_SETUP(KeyPressed)
|
||||
|
||||
int GetRepeatCount() const { return m_repeatCount; }
|
||||
|
||||
std::string ToString() const override
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << GetName() << " with code " << m_keycode << " pressed " << m_repeatCount << " times.";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
int m_repeatCount;
|
||||
};
|
||||
|
||||
class KeyReleasedEvent : public KeyEvent
|
||||
{
|
||||
public:
|
||||
KeyReleasedEvent(int code) :
|
||||
KeyEvent(code)
|
||||
{
|
||||
};
|
||||
|
||||
EVENT_TYPE_SETUP(KeyReleased)
|
||||
|
||||
std::string ToString() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << GetName() << " with code " << m_keycode;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class KeyTypedEvent : public KeyEvent
|
||||
{
|
||||
public:
|
||||
KeyTypedEvent(int code) :
|
||||
KeyEvent(code)
|
||||
{
|
||||
};
|
||||
|
||||
EVENT_TYPE_SETUP(KeyTyped)
|
||||
|
||||
unsigned int GetCharacter() { return (unsigned int)m_keycode; }
|
||||
|
||||
std::string ToString() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << GetName() << " with code " << m_keycode;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
103
src/Events/MouseEvent.h
Normal file
103
src/Events/MouseEvent.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
#ifndef MOUSE_EVENT_H
|
||||
#define MOUSE_EVENT_H
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class MouseMovedEvent : public Event
|
||||
{
|
||||
public:
|
||||
MouseMovedEvent(float x, float y) :
|
||||
m_xPos(x), m_yPos(y)
|
||||
{
|
||||
}
|
||||
|
||||
float GetXPosition() { return m_xPos; }
|
||||
float GetYPosition() { return m_yPos; }
|
||||
std::string ToString() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << GetName() << " to position (" << m_xPos << ", " << m_yPos << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
|
||||
EVENT_TYPE_SETUP(MouseMoved)
|
||||
|
||||
private:
|
||||
float m_xPos, m_yPos;
|
||||
};
|
||||
|
||||
class MouseScrolledEvent : public Event
|
||||
{
|
||||
public:
|
||||
MouseScrolledEvent(float x, float y) :
|
||||
m_xOffset(x), m_yOffset(y)
|
||||
{
|
||||
}
|
||||
|
||||
float GetXOffset() { return m_xOffset; }
|
||||
float GetYOffset() { return m_yOffset; }
|
||||
std::string ToString() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << GetName() << " to offset (" << m_xOffset << ", " << m_yOffset << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
|
||||
EVENT_TYPE_SETUP(MouseScrolled)
|
||||
|
||||
private:
|
||||
float m_xOffset, m_yOffset;
|
||||
};
|
||||
|
||||
class MouseButtonPressedEvent : public Event
|
||||
{
|
||||
public:
|
||||
MouseButtonPressedEvent(int code) :
|
||||
m_buttonCode(code)
|
||||
{
|
||||
}
|
||||
|
||||
int GetButtonCode() { return m_buttonCode; }
|
||||
std::string ToString() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << GetName() << " with button code " << m_buttonCode;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
|
||||
EVENT_TYPE_SETUP(MouseButtonPressed)
|
||||
|
||||
private:
|
||||
int m_buttonCode;
|
||||
};
|
||||
|
||||
class MouseButtonReleasedEvent : public Event
|
||||
{
|
||||
public:
|
||||
MouseButtonReleasedEvent(int code) :
|
||||
m_buttonCode(code)
|
||||
{
|
||||
}
|
||||
|
||||
int GetButtonCode() { return m_buttonCode; }
|
||||
std::string ToString() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << GetName() << " with button code " << m_buttonCode;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
|
||||
EVENT_TYPE_SETUP(MouseButtonReleased)
|
||||
|
||||
private:
|
||||
int m_buttonCode;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
4
src/ImGui/ImGuiBuild.cpp
Normal file
4
src/ImGui/ImGuiBuild.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#include "backends/imgui_impl_opengl3.cpp"
|
||||
#include "backends/imgui_impl_glfw.cpp"
|
||||
|
||||
#include "misc/cpp/imgui_stdlib.cpp" //Enable cpp stl enhancements
|
98
src/ImGui/ImGuiLayer.cpp
Normal file
98
src/ImGui/ImGuiLayer.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include "ImGuiLayer.h"
|
||||
#include "Core/Application.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "backends/imgui_impl_glfw.h"
|
||||
#include "backends/imgui_impl_opengl3.h"
|
||||
#include "implot.h"
|
||||
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "glad/glad.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
ImGuiLayer::ImGuiLayer() :
|
||||
Layer("ImGuiLayer")
|
||||
{
|
||||
}
|
||||
|
||||
ImGuiLayer::~ImGuiLayer() {}
|
||||
|
||||
|
||||
void ImGuiLayer::OnAttach()
|
||||
{
|
||||
IMGUI_CHECKVERSION();
|
||||
|
||||
DY_INFO("Creating ImGui Context...");
|
||||
ImGui::CreateContext();
|
||||
ImPlot::CreateContext();
|
||||
|
||||
//enable docking and viewports
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
||||
|
||||
ImGui::StyleColorsDark(); //Hacker mode
|
||||
ImPlot::StyleColorsDark();
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
{
|
||||
style.WindowRounding = 0.0f;
|
||||
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
|
||||
}
|
||||
|
||||
Application& app = Application::GetInstance();
|
||||
GLFWwindow* window = static_cast<GLFWwindow*>(app.GetWindow().GetNativeWindow());
|
||||
|
||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
ImGui_ImplOpenGL3_Init("#version 410"); //GLSL version
|
||||
DY_INFO("Finished creating ImGui Context");
|
||||
}
|
||||
|
||||
void ImGuiLayer::OnDetach()
|
||||
{
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImPlot::DestroyContext();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
void ImGuiLayer::Begin()
|
||||
{
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void ImGuiLayer::End()
|
||||
{
|
||||
Application& app = Application::GetInstance();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
io.DisplaySize = ImVec2((float)app.GetWindow().GetWidth(), (float)app.GetWindow().GetHeight());
|
||||
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||
{
|
||||
GLFWwindow* backup_current_context = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backup_current_context);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiLayer::OnImGuiRender(double timestep)
|
||||
{
|
||||
//Demo's used to figure out how to do things.
|
||||
//Should not be on for actual NavProject for
|
||||
//real use
|
||||
//static bool show = true;
|
||||
//ImGui::ShowDemoWindow(&show);
|
||||
}
|
||||
|
||||
}
|
24
src/ImGui/ImGuiLayer.h
Normal file
24
src/ImGui/ImGuiLayer.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef IMGUI_LAYER_H
|
||||
#define IMGUI_LAYER_H
|
||||
|
||||
#include "Core/Layer.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class ImGuiLayer : public Layer
|
||||
{
|
||||
public:
|
||||
ImGuiLayer();
|
||||
virtual ~ImGuiLayer();
|
||||
|
||||
virtual void OnAttach() override;
|
||||
virtual void OnDetach() override;
|
||||
virtual void OnImGuiRender(double timestep) override;
|
||||
|
||||
void Begin();
|
||||
void End();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
33
src/Platform/OpenGL/OpenGLContext.cpp
Normal file
33
src/Platform/OpenGL/OpenGLContext.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "OpenGLContext.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "glad/glad.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
OpenGLContext::OpenGLContext(GLFWwindow* window) :
|
||||
m_window(window)
|
||||
{
|
||||
}
|
||||
|
||||
OpenGLContext::~OpenGLContext()
|
||||
{
|
||||
}
|
||||
|
||||
void OpenGLContext::Init()
|
||||
{
|
||||
glfwMakeContextCurrent(m_window);
|
||||
int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
||||
|
||||
DY_TRACE("Loaded OpenGL with glad Init status {0}", status);
|
||||
|
||||
DY_INFO("Loaded OpenGL renderer");
|
||||
DY_INFO("Vendor: {0}", glGetString(GL_VENDOR));
|
||||
DY_INFO("Renderer: {0}", glGetString(GL_RENDERER));
|
||||
DY_INFO("Version: {0}", glGetString(GL_VERSION));
|
||||
}
|
||||
|
||||
void OpenGLContext::SwapBuffers()
|
||||
{
|
||||
glfwSwapBuffers(m_window);
|
||||
}
|
||||
}
|
24
src/Platform/OpenGL/OpenGLContext.h
Normal file
24
src/Platform/OpenGL/OpenGLContext.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef OPENGL_CONTEXT_H
|
||||
#define OPENGL_CONTEXT_H
|
||||
|
||||
#include "Renderer/GraphicsContext.h"
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class OpenGLContext : public GraphicsContext
|
||||
{
|
||||
public:
|
||||
OpenGLContext(GLFWwindow* window);
|
||||
~OpenGLContext();
|
||||
|
||||
virtual void Init() override;
|
||||
virtual void SwapBuffers() override;
|
||||
|
||||
private:
|
||||
GLFWwindow* m_window;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
21
src/Platform/OpenGL/OpenGLRendererAPI.cpp
Normal file
21
src/Platform/OpenGL/OpenGLRendererAPI.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include "OpenGLRendererAPI.h"
|
||||
#include "glad/glad.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
void OpenGLRendererAPI::Clear()
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void OpenGLRendererAPI::SetClearColor(float color[4])
|
||||
{
|
||||
glClearColor(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
double OpenGLRendererAPI::GetFrameTime()
|
||||
{
|
||||
return glfwGetTime();
|
||||
}
|
||||
}
|
18
src/Platform/OpenGL/OpenGLRendererAPI.h
Normal file
18
src/Platform/OpenGL/OpenGLRendererAPI.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef OPENGL_RENDERER_API_H
|
||||
#define OPENGL_RENDERER_API_H
|
||||
|
||||
#include "Renderer/RendererAPI.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class OpenGLRendererAPI : public RendererAPI
|
||||
{
|
||||
public:
|
||||
virtual void SetClearColor(float color[4]) override;
|
||||
virtual void Clear() override;
|
||||
virtual double GetFrameTime() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
164
src/Platform/OpenGL/OpenGLWindow.cpp
Normal file
164
src/Platform/OpenGL/OpenGLWindow.cpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include "OpenGLWindow.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "Events/AppEvent.h"
|
||||
#include "Events/KeyEvent.h"
|
||||
#include "Events/MouseEvent.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
static bool s_glfwInitialized = false;
|
||||
static void GLFWErrorCallback(int error, const char* description)
|
||||
{
|
||||
DY_ERROR("GLFW Error! Code: {0} Message: {1}", error, description);
|
||||
}
|
||||
|
||||
Window* Window::Create(const WindowProperties& props) { return new OpenGLWindow(props); }
|
||||
|
||||
OpenGLWindow::OpenGLWindow(const WindowProperties& props)
|
||||
{
|
||||
Init(props);
|
||||
}
|
||||
|
||||
OpenGLWindow::~OpenGLWindow()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void OpenGLWindow::Init(const WindowProperties& props)
|
||||
{
|
||||
m_data.width = props.width;
|
||||
m_data.height = props.height;
|
||||
m_data.name = props.title;
|
||||
|
||||
DY_INFO("Creating OpenGL Window with width {0}, height {1}, and name {2}", m_data.width, m_data.height, m_data.name);
|
||||
|
||||
if (!s_glfwInitialized)
|
||||
{
|
||||
int passed = glfwInit();
|
||||
DY_INFO("Initializing GLFW ... status {0}", passed);
|
||||
glfwSetErrorCallback(GLFWErrorCallback);
|
||||
}
|
||||
|
||||
//Apple in their effort to force Metal on everyone, has deprecated OpenGL. We have to be
|
||||
//A little more agressive to make OpenGL and GLFW play well together.
|
||||
#ifdef BS_APPLE
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
|
||||
#endif
|
||||
|
||||
m_window = glfwCreateWindow((int)m_data.width, (int)m_data.height, m_data.name.c_str(), nullptr, nullptr);
|
||||
|
||||
m_context = new OpenGLContext(m_window); //This is the only seriously OpenGL specific code
|
||||
m_context->Init();
|
||||
|
||||
glfwSetWindowUserPointer(m_window, &m_data);
|
||||
SetVSync(true);
|
||||
|
||||
//Set all of the callback functions for the window.
|
||||
glfwSetWindowSizeCallback(m_window, [](GLFWwindow* window, int width, int height)
|
||||
{
|
||||
Data& data = *(Data*)glfwGetWindowUserPointer(window);
|
||||
data.width = width;
|
||||
data.height = height;
|
||||
WindowResizeEvent event(width, height);
|
||||
data.callbackFunc(event);
|
||||
});
|
||||
|
||||
glfwSetWindowCloseCallback(m_window, [](GLFWwindow* window)
|
||||
{
|
||||
Data& data = *(Data*)glfwGetWindowUserPointer(window);
|
||||
WindowCloseEvent event;
|
||||
data.callbackFunc(event);
|
||||
});
|
||||
|
||||
glfwSetKeyCallback(m_window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||
{
|
||||
Data& data = *(Data*)glfwGetWindowUserPointer(window);
|
||||
switch (action)
|
||||
{
|
||||
case GLFW_PRESS:
|
||||
{
|
||||
KeyPressedEvent event(key, 0);
|
||||
data.callbackFunc(event);
|
||||
break;
|
||||
}
|
||||
case GLFW_RELEASE:
|
||||
{
|
||||
KeyReleasedEvent event(key);
|
||||
data.callbackFunc(event);
|
||||
break;
|
||||
}
|
||||
case GLFW_REPEAT:
|
||||
{
|
||||
//GLFW doesnt have a hold count, so here we just pass 1 to indicate a hold is happening.
|
||||
KeyPressedEvent event(key, 1);
|
||||
data.callbackFunc(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
glfwSetCharCallback(m_window, [](GLFWwindow* window, unsigned int character)
|
||||
{
|
||||
Data& data = *(Data*)glfwGetWindowUserPointer(window);
|
||||
KeyTypedEvent event(character);
|
||||
data.callbackFunc(event);
|
||||
});
|
||||
|
||||
glfwSetMouseButtonCallback(m_window, [](GLFWwindow* window, int button, int action, int mods)
|
||||
{
|
||||
Data& data = *(Data*)glfwGetWindowUserPointer(window);
|
||||
switch (action)
|
||||
{
|
||||
case GLFW_PRESS:
|
||||
{
|
||||
MouseButtonPressedEvent event(button);
|
||||
data.callbackFunc(event);
|
||||
}
|
||||
case GLFW_RELEASE:
|
||||
{
|
||||
MouseButtonReleasedEvent event(button);
|
||||
data.callbackFunc(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
glfwSetScrollCallback(m_window, [](GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
Data& data = *(Data*)glfwGetWindowUserPointer(window);
|
||||
MouseScrolledEvent event((float)xoffset, (float)yoffset);
|
||||
data.callbackFunc(event);
|
||||
});
|
||||
|
||||
glfwSetCursorPosCallback(m_window, [](GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
Data& data = *(Data*)glfwGetWindowUserPointer(window);
|
||||
MouseMovedEvent event((float)xpos, (float)ypos);
|
||||
data.callbackFunc(event);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLWindow::Shutdown()
|
||||
{
|
||||
glfwDestroyWindow(m_window);
|
||||
}
|
||||
|
||||
void OpenGLWindow::OnUpdate()
|
||||
{
|
||||
glfwPollEvents();
|
||||
m_context->SwapBuffers();
|
||||
}
|
||||
|
||||
void OpenGLWindow::SetVSync(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
glfwSwapInterval(1);
|
||||
else
|
||||
glfwSwapInterval(0);
|
||||
|
||||
m_data.isVSync = enabled;
|
||||
}
|
||||
|
||||
}
|
47
src/Platform/OpenGL/OpenGLWindow.h
Normal file
47
src/Platform/OpenGL/OpenGLWindow.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
#ifndef OPENGL_WINDOW_H
|
||||
#define OPENGL_WINDOW_H
|
||||
|
||||
#include "Core/Window.h"
|
||||
#include "Renderer/GraphicsContext.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class OpenGLWindow : public Window
|
||||
{
|
||||
public:
|
||||
OpenGLWindow(const WindowProperties& props);
|
||||
virtual ~OpenGLWindow();
|
||||
|
||||
virtual void OnUpdate() override;
|
||||
virtual uint32_t GetWidth() override { return m_data.width; }
|
||||
virtual uint32_t GetHeight() override { return m_data.height; }
|
||||
virtual std::string GetName() override { return m_data.name; }
|
||||
|
||||
virtual void SetEventCallback(EventCallbackFunc function) override { m_data.callbackFunc = function; }
|
||||
virtual void SetVSync(bool enabled) override;
|
||||
virtual bool IsVSync() override { return m_data.isVSync; }
|
||||
|
||||
virtual void* GetNativeWindow() const override { return m_window; } //dirty
|
||||
|
||||
private:
|
||||
virtual void Init(const WindowProperties& props);
|
||||
virtual void Shutdown();
|
||||
|
||||
GraphicsContext* m_context;
|
||||
GLFWwindow* m_window;
|
||||
|
||||
struct Data
|
||||
{
|
||||
int width, height;
|
||||
std::string name;
|
||||
bool isVSync;
|
||||
EventCallbackFunc callbackFunc;
|
||||
};
|
||||
|
||||
Data m_data;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
14
src/Renderer/GraphicsContext.h
Normal file
14
src/Renderer/GraphicsContext.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef GRAPHICS_CONTEXT_H
|
||||
#define GRAPHICS_CONTEXT_H
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class GraphicsContext
|
||||
{
|
||||
public:
|
||||
virtual void Init() = 0;
|
||||
virtual void SwapBuffers() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
8
src/Renderer/RenderCommand.cpp
Normal file
8
src/Renderer/RenderCommand.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "RenderCommand.h"
|
||||
#include "Platform/OpenGL/OpenGLRendererAPI.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
RendererAPI* RenderCommand::s_api = new OpenGLRendererAPI();
|
||||
|
||||
}
|
21
src/Renderer/RenderCommand.h
Normal file
21
src/Renderer/RenderCommand.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef RENDER_COMMAND_H
|
||||
#define RENDER_COMMAND_H
|
||||
|
||||
#include "RendererAPI.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class RenderCommand
|
||||
{
|
||||
public:
|
||||
|
||||
static void SetClearColor(float color[4]) { s_api->SetClearColor(color); }
|
||||
static void Clear() { s_api->Clear(); }
|
||||
static double GetFrameTime() { return s_api->GetFrameTime(); }
|
||||
|
||||
private:
|
||||
static RendererAPI* s_api;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
7
src/Renderer/RendererAPI.cpp
Normal file
7
src/Renderer/RendererAPI.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "RendererAPI.h"
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL;
|
||||
|
||||
}
|
26
src/Renderer/RendererAPI.h
Normal file
26
src/Renderer/RendererAPI.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef RENDERER_API_H
|
||||
#define RENDERER_API_H
|
||||
|
||||
namespace Daqromancy {
|
||||
|
||||
class RendererAPI
|
||||
{
|
||||
public:
|
||||
enum class API
|
||||
{
|
||||
None=0,
|
||||
OpenGL
|
||||
};
|
||||
|
||||
virtual void Clear() = 0;
|
||||
virtual void SetClearColor(float color[4]) = 0;
|
||||
virtual double GetFrameTime() = 0;
|
||||
|
||||
static API GetAPI() { return s_api; }
|
||||
|
||||
private:
|
||||
static API s_api;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
21
src/dypch.h
Normal file
21
src/dypch.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef BXSCPCH_H
|
||||
#define BXSCPCH_H
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Core/Logger.h"
|
||||
|
||||
#endif
|
11
vendor/glad/CMakeLists.txt
vendored
Normal file
11
vendor/glad/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
project(Glad)
|
||||
|
||||
add_library(glad STATIC)
|
||||
|
||||
target_include_directories(glad SYSTEM PUBLIC include)
|
||||
|
||||
target_sources(glad
|
||||
PRIVATE src/glad.c
|
||||
PRIVATE include/glad/glad.h
|
||||
PRIVATE include/KHR/khrplatform.h
|
||||
)
|
311
vendor/glad/include/KHR/khrplatform.h
vendored
Normal file
311
vendor/glad/include/KHR/khrplatform.h
vendored
Normal file
|
@ -0,0 +1,311 @@
|
|||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
3694
vendor/glad/include/glad/glad.h
vendored
Normal file
3694
vendor/glad/include/glad/glad.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1833
vendor/glad/src/glad.c
vendored
Normal file
1833
vendor/glad/src/glad.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user