diff --git a/dpsim-villas/examples/cxx/CMakeLists.txt b/dpsim-villas/examples/cxx/CMakeLists.txt index 4ea80b857f..a767829a38 100644 --- a/dpsim-villas/examples/cxx/CMakeLists.txt +++ b/dpsim-villas/examples/cxx/CMakeLists.txt @@ -10,11 +10,14 @@ list(APPEND LIBRARIES ${VILLASNODE_LIBRARIES}) list(APPEND LIBRARIES "jansson") list(APPEND LIBRARIES "uuid") list(APPEND LIBRARIES "villas-common") +list(APPEND LIBRARIES "villas-fpga") +list(APPEND LIBRARIES "xil") list(APPEND INCLUDE_DIRS ${VILLASNODE_INCLUDE_DIRS}) set(SHMEM_SOURCES FileExample.cpp MqttExample.cpp + FpgaExample.cpp SharedMemExample.cpp #ShmemExample.cpp #ShmemDistributedReference.cpp @@ -27,11 +30,7 @@ set(SHMEM_SOURCES ) if(WITH_CIM) - set(CIM_SHMEM_SOURCES - #Shmem_WSCC-9bus.cpp - #Shmem_WSCC-9bus_Ctrl.cpp - #Shmem_WSCC-9bus_CtrlDist.cpp - ) + list(APPEND INCLUDE_DIRS ${CIMPP_INCLUDE_DIRS}) endif() foreach(SOURCE ${SHMEM_SOURCES} ${CIM_SHMEM_SOURCES}) diff --git a/dpsim-villas/examples/cxx/FpgaExample.cpp b/dpsim-villas/examples/cxx/FpgaExample.cpp index 08cb887e39..8953ccf12e 100644 --- a/dpsim-villas/examples/cxx/FpgaExample.cpp +++ b/dpsim-villas/examples/cxx/FpgaExample.cpp @@ -1,91 +1,189 @@ -// SPDX-License-Identifier: Apache-2.0 +/* Simple test circuit for testing connection to a FPGA via VILLASnode + * + * Author: Niklas Eiling + * SPDX-FileCopyrightText: 2024 Niklas Eiling + * SPDX-License-Identifier: Apache-2.0 + */ +#include #include #include +#include +#include #include +#include using namespace DPsim; using namespace CPS::DP; using namespace CPS::DP::Ph1; -int main(int argc, char *argv[]) { - // Very simple test circuit. Just a few resistors and an inductance. - // Voltage is read from VILLASnode and current through everything is written back. - String simName = "Fpga_example"; - CPS::Logger::setLogDir("logs/" + simName); - Real timeStep = 0.01; +const std::string buildFpgaConfig(CommandLineArgs &args) { + std::filesystem::path fpgaIpPath = + "/usr/local/etc/villas/node/etc/fpga/vc707-xbar-pcie-dino/" + "vc707-xbar-pcie-dino.json"; + + if (args.options.find("ips") != args.options.end()) { + fpgaIpPath = std::filesystem::path(args.getOptionString("ips")); + } + std::string cardConfig = fmt::format( + R"STRING("card": {{ + "interface": "pcie", + "id": "10ee:7021", + "slot": "0000:88:00.0", + "do_reset": true, + "ips": "{}", + "polling": true + }}, + "connect": [ + "0->3", + "dino<-dma", + "dino->dma" + ], + "low_latency_mode": true, + "timestep": {})STRING", + fpgaIpPath.string(), args.timeStep); + std::string signalOutConfig = fmt::format( + R"STRING("out": {{ + "signals": [{{ + "name": "from_dpsim", + "type": "complex", + "unit": "V" + }}], + "hooks": [{{ + "type": "dp", + "signal": "from_dpsim", + "f0": {}, + "dt": {}, + "harmonics": [0], + "inverse": true + }}] + }})STRING", + args.sysFreq, args.timeStep); + std::string signalInConfig = fmt::format( + R"STRING("in": {{ + "signals": [{{ + "name": "to_dpsim", + "type": "float", + "unit": "V", + "builtin": false + }}], + "hooks": ["print", {{ + "type": "dp", + "signal": "to_dpsim", + "f0": {}, + "dt": {}, + "harmonics": [0], + "inverse": false + }}] + }})STRING", + args.sysFreq, args.timeStep); + const std::string config = fmt::format( + R"STRING({{ + "type": "fpga", + {}, + {}, + {} + }})STRING", + cardConfig, signalOutConfig, signalInConfig); + DPsim::Logger::get("FpgaExample")->debug("Config for Node:\n{}", config); + return config; +} + +SystemTopology loopbackTopology(CommandLineArgs &args, + std::shared_ptr intf, + std::shared_ptr logger) { + // Nodes + auto n1 = SimNode::make("n1"); + + // Components + auto vs = VoltageSource::make("v_s"); + vs->setParameters(Complex(10, 0), args.sysFreq); + auto rl = Resistor::make("r_l"); + rl->setParameters(1); + + // Topology + vs->connect({SimNode::GND, n1}); + rl->connect({n1, SimNode::GND}); + + // Interface + intf->importAttribute(vs->mVoltageRef, 0, false, false, "from_dino", "A"); + intf->exportAttribute(n1->mVoltage->deriveCoeff(0, 0), 0, true, + "to_dino", "V"); + intf->printVillasSignals(); + + // Logger + logger->logAttribute("v1", n1->mVoltage); + logger->logAttribute("rl_i", rl->mIntfCurrent); + + return SystemTopology(args.sysFreq, SystemNodeList{SimNode::GND, n1}, + SystemComponentList{vs, rl}); +} +SystemTopology hilTopology(CommandLineArgs &args, + std::shared_ptr intf, + std::shared_ptr logger) { // Nodes auto n1 = SimNode::make("n1"); auto n2 = SimNode::make("n2"); - auto n3 = SimNode::make("n3"); - auto n4 = SimNode::make("n4"); // Components - auto evs = VoltageSource::make("v_s"); - evs->setParameters(Complex(5, 0)); + auto vs = VoltageSource::make("v_s"); + vs->setParameters(Complex(10, 0), args.sysFreq); auto rs = Resistor::make("r_s"); rs->setParameters(1); - auto rl = Resistor::make("r_line"); - rl->setParameters(1); - auto ll = Inductor::make("l_line"); - ll->setParameters(1); - auto rL = Resistor::make("r_load"); - rL->setParameters(1000); + + auto cs = CurrentSource::make("i_l"); + cs->setParameters(Complex(0, 0)); // Topology - evs->connect({SimNode::GND, n1}); + vs->connect({SimNode::GND, n1}); rs->connect({n1, n2}); - rl->connect({n2, n3}); - ll->connect({n3, n4}); - rL->connect({n4, SimNode::GND}); - - auto sys = SystemTopology(50, SystemNodeList{SimNode::GND, n1, n2, n3, n4}, - SystemComponentList{evs, rs, rl, ll, rL}); - - RealTimeSimulation sim(simName); - sim.setSystem(sys); - sim.setTimeStep(timeStep); - sim.setFinalTime(10.0); - - std::string fpgaConfig = R"STRING({ - type = "fpga", - card = { - interface = "pcie", - id = "10ee:7021", - slot = "0000:88:00.0", - do_reset = true, - ips = "../../fpga/vc707-xbar-pcie-dino/vc707-xbar-pcie-dino-v2.json", - polling = true, - }, - connect = ["0->3", "dino<-dma", "dino->dma"], - signals = ({ name = "dino", unit = "Volts", type = "float"}), - builtin = false, - lowLatencyMode = true, - })STRING"; - - auto intf = std::make_shared(fpgaConfig); + cs->connect({n2, SimNode::GND}); // Interface - sim.addInterface(intf); - intf->importAttribute(evs->mVoltageRef, 0, true, true); - intf->exportAttribute(evs->mIntfVoltage->deriveCoeff(0, 0), 0, true, - "v_src"); + intf->importAttribute(cs->mCurrentRef, 0, false, false, "from_dino", "A"); + intf->exportAttribute(n2->mVoltage->deriveCoeff(0, 0), 0, true, + "to_dino", "V"); + intf->printVillasSignals(); // Logger - auto logger = DataLogger::make(simName); logger->logAttribute("v1", n1->mVoltage); logger->logAttribute("v2", n2->mVoltage); - logger->logAttribute("v3", n3->mVoltage); - logger->logAttribute("v4", n4->mVoltage); - logger->logAttribute("v_src", evs->mVoltageRef); - logger->logAttribute("i_r", rl->mIntfCurrent, 1, 1); - logger->logAttribute("i_evs", evs->mIntfCurrent, 1, 1); - logger->logAttribute("v_evs", evs->mIntfVoltage, 1, 1); - sim.addLogger(logger); + logger->logAttribute("cs_i", cs->mIntfCurrent); + + return SystemTopology(args.sysFreq, SystemNodeList{SimNode::GND, n1, n2}, + SystemComponentList{vs, rs, cs}); +} - sim.run(1); +SystemTopology getTopology(CommandLineArgs &args, + std::shared_ptr intf, + std::shared_ptr logger) { + if (args.options.find("topology") != args.options.end()) { + std::string topology = args.getOptionString("topology"); + if (topology == "hil") { + return hilTopology(args, intf, logger); + } else if (topology == "loopback") { + return loopbackTopology(args, intf, logger); + } + } + return hilTopology(args, intf, logger); +} + +int main(int argc, char *argv[]) { + CommandLineArgs args(argc, argv, "FpgaExample", 0.01, 10 * 60, 5.); + CPS::Logger::setLogDir("logs/" + args.name); + + auto intf = std::make_shared(buildFpgaConfig(args)); + auto logger = DataLogger::make(args.name); + + auto sys = getTopology(args, intf, logger); + + RealTimeSimulation sim(args.name, args); + sim.setSystem(sys); + sim.addInterface(intf); + sim.addLogger(logger); + sim.run(); //std::ofstream of("task_dependencies.svg"); //sim.dependencyGraph().render(of); diff --git a/dpsim-villas/include/dpsim-villas/InterfaceVillas.h b/dpsim-villas/include/dpsim-villas/InterfaceVillas.h index dfd409faa8..66ee20dde4 100644 --- a/dpsim-villas/include/dpsim-villas/InterfaceVillas.h +++ b/dpsim-villas/include/dpsim-villas/InterfaceVillas.h @@ -5,10 +5,10 @@ #include #include -#include #include -#include #include +#include +#include #include #include #include @@ -39,7 +39,8 @@ class InterfaceVillas : public Interface, /// @param syncOnSimulationStart Whether the simulation should block before the first timestep until this attribute has been updated void importAttribute(CPS::AttributeBase::Ptr attr, UInt idx, Bool blockOnRead = false, - Bool syncOnSimulationStart = true); + Bool syncOnSimulationStart = true, + const String &name = "", const String &unit = ""); /// @brief configure an attribute export /// @param attr the attribute which's value should be exported @@ -50,5 +51,7 @@ class InterfaceVillas : public Interface, void exportAttribute(CPS::AttributeBase::Ptr attr, UInt idx, Bool waitForOnWrite, const String &name = "", const String &unit = ""); + + void printVillasSignals() const; }; } // namespace DPsim diff --git a/dpsim-villas/include/dpsim-villas/InterfaceWorkerVillas.h b/dpsim-villas/include/dpsim-villas/InterfaceWorkerVillas.h index 713b2c590e..24a04a176d 100644 --- a/dpsim-villas/include/dpsim-villas/InterfaceWorkerVillas.h +++ b/dpsim-villas/include/dpsim-villas/InterfaceWorkerVillas.h @@ -5,10 +5,10 @@ #include #include -#include #include -#include #include +#include +#include #include #include #include @@ -65,12 +65,15 @@ class InterfaceWorkerVillas : public InterfaceWorker, std::vector &updatedAttrs) override; virtual void configureImport(UInt attributeId, const std::type_info &type, - UInt idx); + UInt idx, const String &name = "", + const String &unit = ""); virtual void configureExport(UInt attributeId, const std::type_info &type, UInt idx, Bool waitForOnWrite, const String &name = "", const String &unit = ""); + void printSignals() const; + private: void prepareNode(); void setupNodeSignals(); diff --git a/dpsim-villas/src/InterfaceVillas.cpp b/dpsim-villas/src/InterfaceVillas.cpp index c28c410288..5feb144bd8 100644 --- a/dpsim-villas/src/InterfaceVillas.cpp +++ b/dpsim-villas/src/InterfaceVillas.cpp @@ -16,11 +16,12 @@ InterfaceVillas::InterfaceVillas(const String &nodeConfig, UInt queueLength, void InterfaceVillas::importAttribute(CPS::AttributeBase::Ptr attr, UInt idx, Bool blockOnRead, - Bool syncOnSimulationStart) { + Bool syncOnSimulationStart, + const String &name, const String &unit) { Interface::addImport(attr, blockOnRead, syncOnSimulationStart); std::dynamic_pointer_cast(mInterfaceWorker) ->configureImport((UInt)mImportAttrsDpsim.size() - 1, attr->getType(), - idx); + idx, name, unit); } void InterfaceVillas::exportAttribute(CPS::AttributeBase::Ptr attr, UInt idx, @@ -32,4 +33,9 @@ void InterfaceVillas::exportAttribute(CPS::AttributeBase::Ptr attr, UInt idx, idx, waitForOnWrite, name, unit); } -} // namespace DPsim \ No newline at end of file +void InterfaceVillas::printVillasSignals() const { + std::dynamic_pointer_cast(mInterfaceWorker) + ->printSignals(); +} + +} // namespace DPsim diff --git a/dpsim-villas/src/InterfaceWorkerVillas.cpp b/dpsim-villas/src/InterfaceWorkerVillas.cpp index 7326e76331..b3efbf3d78 100644 --- a/dpsim-villas/src/InterfaceWorkerVillas.cpp +++ b/dpsim-villas/src/InterfaceWorkerVillas.cpp @@ -109,6 +109,7 @@ void InterfaceWorkerVillas::prepareNode() { ret); std::exit(1); } + SPDLOG_LOGGER_INFO(mLog, "Node: {}", mNode->getNameFull()); mNode->getFactory()->start( nullptr); //We have no SuperNode, so just hope type_start doesnt use it... @@ -141,8 +142,12 @@ void InterfaceWorkerVillas::setupNodeSignals() { idx++; } - node::SignalList::Ptr nodeInputSignals = mNode->getInputSignals(false); - nodeInputSignals->clear(); + node::SignalList::Ptr nodeInputSignals = mNode->getInputSignals(true); + if (nodeInputSignals == nullptr) { + nodeInputSignals = std::make_shared(); + } else { + nodeInputSignals->clear(); + } idx = 0; for (const auto &[id, signal] : mImportSignals) { while (id > idx) { @@ -488,7 +493,8 @@ void InterfaceWorkerVillas::configureExport(UInt attributeId, void InterfaceWorkerVillas::configureImport(UInt attributeId, const std::type_info &type, - UInt idx) { + UInt idx, const String &name, + const String &unit) { if (mOpened) { if (mLog != nullptr) { SPDLOG_LOGGER_WARN(mLog, "InterfaceVillas has already been opened! " @@ -518,7 +524,7 @@ void InterfaceWorkerVillas::configureImport(UInt attributeId, }, 0); mImportSignals[idx] = - std::make_shared("", "", node::SignalType::INTEGER); + std::make_shared(name, unit, node::SignalType::INTEGER); } else if (type == typeid(Real)) { mImports.emplace_back( [idx, log](Sample *smp) -> AttributeBase::Ptr { @@ -531,7 +537,7 @@ void InterfaceWorkerVillas::configureImport(UInt attributeId, }, 0); mImportSignals[idx] = - std::make_shared("", "", node::SignalType::FLOAT); + std::make_shared(name, unit, node::SignalType::FLOAT); } else if (type == typeid(Complex)) { mImports.emplace_back( [idx, log](Sample *smp) -> AttributeBase::Ptr { @@ -544,7 +550,7 @@ void InterfaceWorkerVillas::configureImport(UInt attributeId, }, 0); mImportSignals[idx] = - std::make_shared("", "", node::SignalType::COMPLEX); + std::make_shared(name, unit, node::SignalType::COMPLEX); } else if (type == typeid(Bool)) { mImports.emplace_back( [idx, log](Sample *smp) -> AttributeBase::Ptr { @@ -557,7 +563,7 @@ void InterfaceWorkerVillas::configureImport(UInt attributeId, }, 0); mImportSignals[idx] = - std::make_shared("", "", node::SignalType::BOOLEAN); + std::make_shared(name, unit, node::SignalType::BOOLEAN); } else { if (mLog != nullptr) { SPDLOG_LOGGER_WARN(mLog, "Unsupported attribute type! Interface " @@ -565,3 +571,23 @@ void InterfaceWorkerVillas::configureImport(UInt attributeId, } } } + +void InterfaceWorkerVillas::printSignals() const { + SPDLOG_LOGGER_INFO( + mLog, + "InterfaceWorkerVillas Settings: Queue Length: {}, Sample Length: {}", + mQueueLength, mSampleLength); + SPDLOG_LOGGER_INFO(mLog, "Export signals:"); + for (const auto &[id, signal] : mExportSignals) { + SPDLOG_LOGGER_INFO(mLog, "ID: {}, Name: {}, Unit: {}, Type: {}", id, + signal->name, signal->unit, + node::signalTypeToString(signal->type)); + } + + SPDLOG_LOGGER_INFO(mLog, "Import signals:"); + for (const auto &[id, signal] : mImportSignals) { + SPDLOG_LOGGER_INFO(mLog, "ID: {}, Name: {}, Unit: {}, Type: {}", id, + signal->name, signal->unit, + node::signalTypeToString(signal->type)); + } +} diff --git a/dpsim-villas/src/pybind-dpsim-villas.cpp b/dpsim-villas/src/pybind-dpsim-villas.cpp index dc33e2534d..067ce5c614 100644 --- a/dpsim-villas/src/pybind-dpsim-villas.cpp +++ b/dpsim-villas/src/pybind-dpsim-villas.cpp @@ -43,7 +43,8 @@ PYBIND11_MODULE(dpsimpyvillas, m) { "name"_a = "", "downsampling"_a = 1) .def("import_attribute", &PyInterfaceVillas::importAttribute, "attr"_a, // cppcheck-suppress assignBoolToPointer - "idx"_a, "block_on_read"_a = false, "sync_on_start"_a = true) + "idx"_a, "block_on_read"_a = false, "sync_on_start"_a = true, + "name"_a = "", "unit"_a = "") .def("export_attribute", &PyInterfaceVillas::exportAttribute, "attr"_a, // cppcheck-suppress assignBoolToPointer "idx"_a, "wait_for_on_write"_a = true, "name"_a = "", "unit"_a = ""); diff --git a/dpsim/include/dpsim/InterfaceWorker.h b/dpsim/include/dpsim/InterfaceWorker.h index fa5ce10df7..e169d6b546 100644 --- a/dpsim/include/dpsim/InterfaceWorker.h +++ b/dpsim/include/dpsim/InterfaceWorker.h @@ -57,4 +57,4 @@ class InterfaceWorker { */ virtual void close() = 0; }; -} // namespace DPsim \ No newline at end of file +} // namespace DPsim diff --git a/dpsim/include/dpsim/RealTimeSimulation.h b/dpsim/include/dpsim/RealTimeSimulation.h index ff3246d3e5..5829f0497d 100644 --- a/dpsim/include/dpsim/RealTimeSimulation.h +++ b/dpsim/include/dpsim/RealTimeSimulation.h @@ -24,6 +24,7 @@ class RealTimeSimulation : public Simulation { Timer mTimer; public: + RealTimeSimulation(String name, CommandLineArgs &args); /// Standard constructor RealTimeSimulation(String name, CPS::Logger::Level logLevel = CPS::Logger::Level::info); diff --git a/dpsim/src/RealTimeSimulation.cpp b/dpsim/src/RealTimeSimulation.cpp index 03fbe8209d..ba94868ecd 100644 --- a/dpsim/src/RealTimeSimulation.cpp +++ b/dpsim/src/RealTimeSimulation.cpp @@ -14,6 +14,9 @@ using namespace CPS; using namespace DPsim; +RealTimeSimulation::RealTimeSimulation(String name, CommandLineArgs &args) + : Simulation(name, args), mTimer(){}; + RealTimeSimulation::RealTimeSimulation(String name, Logger::Level logLevel) : Simulation(name, logLevel), mTimer() { diff --git a/packaging/Docker/Dockerfile.dev b/packaging/Docker/Dockerfile.dev index 4eb81ea9ed..f52fb3e436 100644 --- a/packaging/Docker/Dockerfile.dev +++ b/packaging/Docker/Dockerfile.dev @@ -1,7 +1,7 @@ FROM fedora:36 AS base ARG CIM_VERSION=CGMES_2.4.15_16FEB2016 -ARG VILLAS_VERSION=66569cf9c43d2d7a4626b9a84321c4e340d3fe18 +ARG VILLAS_VERSION=411b0ad49e2629ad41c6918d2a6c51e9a72220b4 ARG CMAKE_OPTS ARG MAKE_OPTS=-j4 diff --git a/packaging/Docker/Dockerfile.dev-debian b/packaging/Docker/Dockerfile.dev-debian index 46dff10309..800dbef43a 100644 --- a/packaging/Docker/Dockerfile.dev-debian +++ b/packaging/Docker/Dockerfile.dev-debian @@ -1,7 +1,7 @@ FROM debian:11 ARG CIM_VERSION=CGMES_2.4.15_16FEB2016 -ARG VILLAS_VERSION=66569cf9c43d2d7a4626b9a84321c4e340d3fe18 +ARG VILLAS_VERSION=411b0ad49e2629ad41c6918d2a6c51e9a72220b4 ARG CMAKE_OPTS ARG MAKE_OPTS=-j4 diff --git a/packaging/Docker/Dockerfile.manylinux b/packaging/Docker/Dockerfile.manylinux index 756b7d8cd9..5f425c31c0 100644 --- a/packaging/Docker/Dockerfile.manylinux +++ b/packaging/Docker/Dockerfile.manylinux @@ -6,7 +6,7 @@ FROM quay.io/pypa/manylinux_2_28_x86_64 ARG CIM_VERSION=CGMES_2.4.15_16FEB2016 -ARG VILLAS_VERSION=66569cf9c43d2d7a4626b9a84321c4e340d3fe18 +ARG VILLAS_VERSION=411b0ad49e2629ad41c6918d2a6c51e9a72220b4 ARG CMAKE_OPTS ARG MAKE_OPTS=-j4