diff --git a/hal/build.gradle b/hal/build.gradle index c349a61f91..63717842c8 100644 --- a/hal/build.gradle +++ b/hal/build.gradle @@ -108,6 +108,11 @@ model { } } } + binaries.all { binary -> + if (binary.targetPlatform.operatingSystem.linux) { + linker.args "-ldl" + } + } } if (project.hasProperty('buildHalStaticDeps')) { halSimStaticDeps(NativeLibrarySpec) { diff --git a/hal/src/main/native/include/HAL/Extensions.h b/hal/src/main/native/include/HAL/Extensions.h new file mode 100644 index 0000000000..bf35b7b7fe --- /dev/null +++ b/hal/src/main/native/include/HAL/Extensions.h @@ -0,0 +1,25 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +/** + * HAL Simulator Extensions are libraries that provide additional simulator + * functionality, such as a Gazebo interface, or a more light weight simulation. + * + * An extension must expose the HALSIM_InitExtension entry point which is + * invoked after the library is loaded. + * + * The entry point is expected to return < 0 for errors that should stop + * the HAL completely, 0 for success, and > 0 for a non fatal error. + */ +typedef int halsim_extension_init_func_t(void); + +extern "C" { +int HAL_LoadOneExtension(const char* library); +int HAL_LoadExtensions(void); +} diff --git a/hal/src/main/native/sim/Extensions.cpp b/hal/src/main/native/sim/Extensions.cpp new file mode 100644 index 0000000000..66293e6676 --- /dev/null +++ b/hal/src/main/native/sim/Extensions.cpp @@ -0,0 +1,83 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "HAL/Extensions.h" + +#include +#include + +#include "HAL/HAL.h" + +#if defined(WIN32) || defined(_WIN32) +#include +#else +#include +#endif + +#if defined(WIN32) || defined(_WIN32) +#define DELIM ';' +#define HTYPE HMODULE +#define DLOPEN(a) LoadLibrary(a) +#define DLSYM GetProcAddress +#define DLCLOSE FreeLibrary +#else +#define DELIM ':' +#define HTYPE void* +#define PREFIX "lib" +#define DLOPEN(a) dlopen(a, RTLD_LAZY) +#define DLSYM dlsym +#define DLCLOSE dlclose +#endif + +extern "C" { + +int HAL_LoadOneExtension(const char* library) { + int rc = 1; // It is expected and reasonable not to find an extra simulation + HTYPE handle = DLOPEN(library); +#if !defined(WIN32) && !defined(_WIN32) + if (!handle) { + llvm::SmallString<128> libraryName("lib"); + libraryName += library; +#if defined(__APPLE__) + libraryName += ".dylib"; +#else + libraryName += ".so"; +#endif + handle = DLOPEN(libraryName.c_str()); + } +#endif + if (!handle) return rc; + + auto init = reinterpret_cast( + DLSYM(handle, "HALSIM_InitExtension")); + + if (init) rc = (*init)(); + + if (rc != 0) DLCLOSE(handle); + return rc; +} + +/** + * Load any extra halsim libraries provided in the HALSIM_EXTENSIONS + * environment variable. + */ +int HAL_LoadExtensions(void) { + int rc = 1; + llvm::SmallVector libraries; + const char* e = std::getenv("HALSIM_EXTENSIONS"); + if (!e) return rc; + llvm::StringRef env{e}; + env.split(libraries, DELIM, -1, false); + for (auto& libref : libraries) { + llvm::SmallString<128> library(libref); + rc = HAL_LoadOneExtension(library.c_str()); + if (rc < 0) break; + } + return rc; +} + +} // extern "C" diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp index 4d1519a90c..a9f08a1d83 100644 --- a/hal/src/main/native/sim/HAL.cpp +++ b/hal/src/main/native/sim/HAL.cpp @@ -10,6 +10,7 @@ #include "ErrorsInternal.h" #include "HAL/DriverStation.h" #include "HAL/Errors.h" +#include "HAL/Extensions.h" #include "HAL/handles/HandlesInternal.h" #include "MockData/RoboRioDataInternal.h" #include "MockHooksInternal.h" @@ -203,6 +204,7 @@ HAL_Bool HAL_Initialize(int32_t timeout, int32_t mode) { // Second check in case another thread was waiting if (initialized) return true; + if (HAL_LoadExtensions() < 0) return false; hal::RestartTiming(); HAL_InitializeDriverStation(); diff --git a/settings.gradle b/settings.gradle index aabb6df315..da3ce6061c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,3 +8,4 @@ include 'wpilibj' include 'wpilibjIntegrationTests' include 'wpilibjExamples' include 'myRobot' +include 'simulation:halsim_print' diff --git a/simulation/halsim_print/build.gradle b/simulation/halsim_print/build.gradle new file mode 100644 index 0000000000..dec160a6e8 --- /dev/null +++ b/simulation/halsim_print/build.gradle @@ -0,0 +1,26 @@ +description = "A simulation shared object that simply prints robot behaviors" + +apply plugin: 'edu.wpi.first.NativeUtils' +apply plugin: 'cpp' + +ext.skipAthena = true + +apply from: "../../config.gradle" + + +model { + components { + halsim_print(NativeLibrarySpec) + } + + binaries { + all { + project(':hal').addHalCompilerArguments(it) + project(':hal').addHalToLinker(it) + } + withType(StaticLibraryBinarySpec) { + it.buildable = false + } + } +} +apply from: 'publish.gradle' diff --git a/simulation/halsim_print/publish.gradle b/simulation/halsim_print/publish.gradle new file mode 100644 index 0000000000..ceee83e604 --- /dev/null +++ b/simulation/halsim_print/publish.gradle @@ -0,0 +1,43 @@ +apply plugin: 'maven-publish' +apply plugin: 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' + +if (!hasProperty('releaseType')) { + WPILibVersion { + releaseType = 'dev' + } +} + +def pubVersion = '' +if (project.hasProperty("publishVersion")) { + pubVersion = project.publishVersion +} else { + pubVersion = WPILibVersion.version +} + +def baseArtifactId = 'halsim-print' +def artifactGroupId = 'edu.wpi.first.halsim' + + +model { + publishing { + def libSpec + $.components.each { + if (it in NativeLibrarySpec) { + $.binaries.each { + if (it in SharedLibraryBinarySpec) { + libSpec = it.sharedLibraryFile + } + } + } + } + + publications { + cpp(MavenPublication) { + artifact libSpec + artifactId = baseArtifactId + groupId artifactGroupId + version pubVersion + } + } + } +} diff --git a/simulation/halsim_print/src/halsim_print/cpp/PrintPWM.cpp b/simulation/halsim_print/src/halsim_print/cpp/PrintPWM.cpp new file mode 100644 index 0000000000..382d88189f --- /dev/null +++ b/simulation/halsim_print/src/halsim_print/cpp/PrintPWM.cpp @@ -0,0 +1,29 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "PrintPWM.h" + +#include + +#include "MockData/HAL_Value.h" +#include "MockData/NotifyListener.h" +#include "MockData/PWMData.h" + +static void PWMCallback(const char* name, void* param, + const struct HAL_Value* value) { + auto pwm = static_cast(param); + pwm->Publish(value->data.v_double); +} + +PrintPWM::PrintPWM(int port) { + m_port = port; + HALSIM_RegisterPWMSpeedCallback(port, PWMCallback, this, false); +} + +void PrintPWM::Publish(double value) { + std::cout << "PWM " << m_port << ": " << value << std::endl; +} diff --git a/simulation/halsim_print/src/halsim_print/cpp/main.cpp b/simulation/halsim_print/src/halsim_print/cpp/main.cpp new file mode 100644 index 0000000000..c026760adc --- /dev/null +++ b/simulation/halsim_print/src/halsim_print/cpp/main.cpp @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include + +#include + +#include "HALSimPrint.h" +#include "PrintPWM.h" + +/** + * Currently, robots never terminate, so we keep a single static object + * and it is never properly released or cleaned up. + */ +static HALSimPrint halsim; + +extern "C" { +#if defined(WIN32) || defined(_WIN32) +__declspec(dllexport) +#endif + int HALSIM_InitExtension(void) { + std::cout << "Print Simulator Initializing." << std::endl; + + int pwmCount = HAL_GetNumPWMChannels(); + halsim.m_pwms.reserve(pwmCount); + for (int i = 0; i < pwmCount; i++) halsim.m_pwms.emplace_back(i); + + return 0; +} +} // extern "C" diff --git a/simulation/halsim_print/src/halsim_print/headers/HALSimPrint.h b/simulation/halsim_print/src/halsim_print/headers/HALSimPrint.h new file mode 100644 index 0000000000..c1cfc4f0f5 --- /dev/null +++ b/simulation/halsim_print/src/halsim_print/headers/HALSimPrint.h @@ -0,0 +1,17 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +class PrintPWM; + +class HALSimPrint { + public: + std::vector m_pwms; +}; diff --git a/simulation/halsim_print/src/halsim_print/headers/PrintPWM.h b/simulation/halsim_print/src/halsim_print/headers/PrintPWM.h new file mode 100644 index 0000000000..31afa60403 --- /dev/null +++ b/simulation/halsim_print/src/halsim_print/headers/PrintPWM.h @@ -0,0 +1,19 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include "HALSimPrint.h" + +class PrintPWM { + public: + explicit PrintPWM(int port); + void Publish(double value); + + private: + int m_port; +};