Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,442 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "isolation_monitorImpl.hpp"
|
||||
#include "everest/logging.hpp"
|
||||
#include <chrono>
|
||||
#include <fmt/core.h>
|
||||
#include <thread>
|
||||
#include <utils/date.hpp>
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
void isolation_monitorImpl::init() {
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::configure_device() {
|
||||
// Query device name and version
|
||||
bool successful = true;
|
||||
int selftest_enable_at_start_value = config.selftest_enable_at_start ? 1 : 0;
|
||||
if (config.disable_device_on_stop and config.selftest_enable_at_start) {
|
||||
EVLOG_warning << "disable_device_on_stop configuration option and "
|
||||
"selftest_enable_at_start are incompatible. Self test at "
|
||||
"start will be disabled.";
|
||||
selftest_enable_at_start_value = 0;
|
||||
}
|
||||
do {
|
||||
successful = true;
|
||||
successful &= send_to_imd(3000, (config.voltage_to_earth_monitoring_alarm_enable ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3005, config.r1_prealarm_kohm);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3007, config.r2_alarm_kohm);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3008, (config.undervoltage_alarm_enable ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3009, config.undervoltage_alarm_threshold_V);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3010, (config.overvoltage_alarm_enable ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3011, config.overvoltage_alarm_threshold_V);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3012, (config.alarm_memory_enable ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3013, (config.relais_r1_mode ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3014, (config.relais_r2_mode ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3018, (config.delay_startup_device ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3019, config.delay_t_on_k1_k2);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3020, config.delay_t_off_k1_k2);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3023, (config.chademo_mode ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3024, (config.selftest_enable_gridconnection ? 1 : 0));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3025, selftest_enable_at_start_value);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3027, config.relay_k1_alarm_assignment);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
successful &= send_to_imd(3028, config.relay_k2_alarm_assignment);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
// start up enable the device if the configuration option is not set
|
||||
if (!config.disable_device_on_stop) {
|
||||
successful &= send_to_imd(3026, 1);
|
||||
}
|
||||
// Give device time to process startup command
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
if (successful) {
|
||||
EVLOG_info << "IMD Device: " << read_device_name() << " (" << read_firmware_version() << ")";
|
||||
faster_cable_check_supported = check_for_faster_cablecheck();
|
||||
|
||||
if (faster_cable_check_supported) {
|
||||
EVLOG_info << "Supports faster cable check method";
|
||||
enable_faster_cable_check_mode();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
} else {
|
||||
EVLOG_info << "Does not support faster cable check method, falling "
|
||||
"back to long self test. This may create "
|
||||
"timeouts with certain cars in CableCheck. Consider "
|
||||
"upgrading to at least firmware 5.00";
|
||||
disable_faster_cable_check_mode();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
} else {
|
||||
// allow the system to recover and don't hog the MODBUS
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
}
|
||||
} while (not successful);
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::start_self_test() {
|
||||
if (last_test != TestType::ExternalTest) {
|
||||
// Wait a bit to ensure device is ready (not processing previous read
|
||||
// operations) This prevents conflicts with the regular reading loop
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
if (mod->r_serial_comm_hub->call_modbus_write_multiple_registers(
|
||||
config.imd_device_id, static_cast<int>(8005),
|
||||
(types::serial_comm_hub_requests::VectorUint16){{0x5445}}) !=
|
||||
types::serial_comm_hub_requests::StatusCodeEnum::Success) {
|
||||
set_deviceFault("Failed to start self test");
|
||||
self_test_started = false;
|
||||
return;
|
||||
}
|
||||
// Give device time to process the self-test command
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
self_test_started = true;
|
||||
self_test_timeout = 30;
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::set_deviceFault(const std::string& message) {
|
||||
if (!error_state_monitor->is_error_active("isolation_monitor/DeviceFault", "")) {
|
||||
auto error =
|
||||
error_factory->create_error("isolation_monitor/DeviceFault", "", message, Everest::error::Severity::High);
|
||||
raise_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::clear_deviceFault() {
|
||||
if (error_state_monitor->is_error_active("isolation_monitor/DeviceFault", "")) {
|
||||
clear_error("isolation_monitor/DeviceFault");
|
||||
}
|
||||
}
|
||||
|
||||
bool isolation_monitorImpl::enable_faster_cable_check_mode() {
|
||||
return send_to_imd(3021, 3);
|
||||
}
|
||||
|
||||
bool isolation_monitorImpl::disable_faster_cable_check_mode() {
|
||||
return send_to_imd(3021, 0);
|
||||
}
|
||||
|
||||
bool isolation_monitorImpl::send_to_imd(const uint16_t& command, const uint16_t& value) {
|
||||
if (mod->r_serial_comm_hub->call_modbus_write_multiple_registers(
|
||||
config.imd_device_id, static_cast<int>(command),
|
||||
(types::serial_comm_hub_requests::VectorUint16){{value}}) !=
|
||||
types::serial_comm_hub_requests::StatusCodeEnum::Success) {
|
||||
EVLOG_error << "ModBus Command failed: " << command << " Value: " << value;
|
||||
set_deviceFault("Failed to write registers");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::ready() {
|
||||
this->configure_device();
|
||||
bool self_test_running = false;
|
||||
bool need_to_disable_device = false;
|
||||
int device_disabled_timeout_s = 10;
|
||||
|
||||
while (true) {
|
||||
read_imd_values();
|
||||
|
||||
if (self_test_started) {
|
||||
self_test_timeout--;
|
||||
if (self_test_timeout <= 0) {
|
||||
// a time out happend
|
||||
self_test_started = false;
|
||||
need_to_disable_device = true;
|
||||
// publish failed result
|
||||
EVLOG_warning << "Self test timed out";
|
||||
publish_self_test_result(false);
|
||||
}
|
||||
if (self_test_running) {
|
||||
if (last_test not_eq TestType::ExternalTest) {
|
||||
// Self test is done
|
||||
self_test_running = false;
|
||||
self_test_started = false;
|
||||
need_to_disable_device = true;
|
||||
// was it successfull? If there is no error, it was...
|
||||
bool result = true;
|
||||
if (last_alarm == AlarmType::DeviceError) {
|
||||
result = false;
|
||||
}
|
||||
EVLOG_info << "Self test completed: " << result;
|
||||
publish_self_test_result(result);
|
||||
}
|
||||
} else {
|
||||
if (last_test == TestType::ExternalTest) {
|
||||
self_test_running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (not error_state_monitor->is_error_active("isolation_monitor/DeviceFault", "")) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
if (need_to_disable_device) {
|
||||
device_disabled_timeout_s--;
|
||||
if (device_disabled_timeout_s <= 0) {
|
||||
need_to_disable_device = false;
|
||||
device_disabled_timeout_s = 10;
|
||||
// disable the device if the configuration option is set and we are not publishing
|
||||
// aka we are in a measuring cycle (start called)
|
||||
if (not enable_publishing && config.disable_device_on_stop) {
|
||||
if (not send_to_imd(3026, 0)) {
|
||||
EVLOG_error << "Can't disable the device: " << read_device_name();
|
||||
} else {
|
||||
EVLOG_info << "Device disabled after self test since we didn't start measuring cycle "
|
||||
"(timeout 10s)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::handle_start() {
|
||||
enable_publishing = true;
|
||||
if (config.disable_device_on_stop) {
|
||||
if (not send_to_imd(3026, 1)) {
|
||||
EVLOG_error << "Can't enable the device: " << read_device_name();
|
||||
} else {
|
||||
EVLOG_info << "Device enabled for measurements";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::handle_stop() {
|
||||
enable_publishing = false;
|
||||
if (config.disable_device_on_stop) {
|
||||
if (not send_to_imd(3026, 0)) {
|
||||
EVLOG_error << "Can't disable the device: " << read_device_name();
|
||||
} else {
|
||||
EVLOG_info << "Device disabled after measurements";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::handle_start_self_test(double& test_voltage_V) {
|
||||
EVLOG_info << "IMD Starting self-test...";
|
||||
// make sure that the device is on
|
||||
if (config.disable_device_on_stop) {
|
||||
if (not send_to_imd(3026, 1)) {
|
||||
EVLOG_error << "Can't enable the device: " << read_device_name();
|
||||
} else {
|
||||
EVLOG_info << "Device enabled for self test";
|
||||
}
|
||||
}
|
||||
start_self_test();
|
||||
}
|
||||
|
||||
void isolation_monitorImpl::read_imd_values() {
|
||||
// Read rF
|
||||
auto rf = read_register(ImdRegisters::RESISTANCE_R_F_OHM);
|
||||
EVLOG_debug << "Resistance: " << to_string(rf);
|
||||
|
||||
last_test = rf.test;
|
||||
last_alarm = rf.alarm;
|
||||
|
||||
// Small delay between reads to avoid overwhelming the device
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
// Read Voltage U_N (always needed)
|
||||
auto voltage = read_register(ImdRegisters::VOLTAGE_U_N_V);
|
||||
EVLOG_debug << "Voltage: " << to_string(voltage);
|
||||
|
||||
isolation_monitorImpl::MeasurementValue voltage_to_earth_l1e;
|
||||
isolation_monitorImpl::MeasurementValue voltage_to_earth_l2e;
|
||||
// Read Voltage to Earth L1E and L2E only if the device is not in self test
|
||||
// mode and only it we are in a measuring cycle. We have seen that Bender
|
||||
// sometimes is overwhelmed
|
||||
if ((last_test == TestType::NoTest) and (enable_publishing or config.always_publish_measurements)) {
|
||||
// VOLTAGE_U_L1E_V (1016) and VOLTAGE_U_L2E_V (1020) are consecutive (4
|
||||
// registers apart) Read 8 registers starting from 1016 to get both
|
||||
// measurements in a single operation
|
||||
types::serial_comm_hub_requests::Result register_response =
|
||||
mod->r_serial_comm_hub->call_modbus_read_holding_registers(
|
||||
config.imd_device_id, static_cast<int>(ImdRegisters::VOLTAGE_U_L1E_V), 8);
|
||||
|
||||
if (register_response.status_code == types::serial_comm_hub_requests::StatusCodeEnum::Success and
|
||||
register_response.value.has_value() and register_response.value.value().size() == 8) {
|
||||
const auto& reg_value_int = register_response.value.value();
|
||||
// Convert std::vector<int> to std::vector<uint16_t>
|
||||
std::vector<uint16_t> reg_value(reg_value_int.begin(), reg_value_int.end());
|
||||
|
||||
// Parse voltage U_L1E from registers 0-3
|
||||
voltage_to_earth_l1e = parse_register_data(reg_value, 0);
|
||||
EVLOG_debug << "Voltage to Earth L1E: " << to_string(voltage_to_earth_l1e);
|
||||
|
||||
// Parse voltage U_L2E from registers 4-7
|
||||
voltage_to_earth_l2e = parse_register_data(reg_value, 4);
|
||||
EVLOG_debug << "Voltage to Earth L2E: " << to_string(voltage_to_earth_l2e);
|
||||
} else {
|
||||
// Fallback to individual reads
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
voltage_to_earth_l1e = read_register(ImdRegisters::VOLTAGE_U_L1E_V);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
voltage_to_earth_l2e = read_register(ImdRegisters::VOLTAGE_U_L2E_V);
|
||||
}
|
||||
}
|
||||
|
||||
if (last_alarm == AlarmType::DeviceError) {
|
||||
set_deviceFault("Device Error");
|
||||
}
|
||||
|
||||
bool valid_readings = true;
|
||||
if (enable_publishing or config.always_publish_measurements) {
|
||||
types::isolation_monitor::IsolationMeasurement m;
|
||||
if (voltage.valid not_eq ValidType::Invalid) {
|
||||
m.voltage_V = voltage.value;
|
||||
} else {
|
||||
valid_readings = false;
|
||||
}
|
||||
|
||||
if (voltage_to_earth_l1e.valid not_eq ValidType::Invalid) {
|
||||
m.voltage_to_earth_l1e_V = voltage_to_earth_l1e.value;
|
||||
} else {
|
||||
valid_readings = false;
|
||||
}
|
||||
|
||||
if (voltage_to_earth_l2e.valid not_eq ValidType::Invalid) {
|
||||
m.voltage_to_earth_l2e_V = voltage_to_earth_l2e.value;
|
||||
} else {
|
||||
valid_readings = false;
|
||||
}
|
||||
|
||||
if (rf.valid not_eq ValidType::Invalid) {
|
||||
m.resistance_F_Ohm = rf.value;
|
||||
} else {
|
||||
valid_readings = false;
|
||||
}
|
||||
|
||||
// do not publish values if in error state or the device is in self test
|
||||
// mode
|
||||
if (last_test == TestType::NoTest) {
|
||||
if (not error_state_monitor->is_error_active("isolation_monitor/DeviceFault", "")) {
|
||||
publish_isolation_measurement(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
// let at least one round of values unpublished not to use unstable ones
|
||||
if (last_alarm not_eq AlarmType::DeviceError and valid_readings and
|
||||
error_state_monitor->is_error_active("isolation_monitor/DeviceFault", "")) {
|
||||
clear_deviceFault();
|
||||
configure_device();
|
||||
}
|
||||
}
|
||||
|
||||
std::string isolation_monitorImpl::read_device_name() {
|
||||
auto result = mod->r_serial_comm_hub->call_modbus_read_holding_registers(
|
||||
config.imd_device_id, static_cast<std::underlying_type_t<ImdRegisters>>(ImdRegisters::DEVICE_NAME), 10);
|
||||
std::string device_name;
|
||||
if (result.status_code == types::serial_comm_hub_requests::StatusCodeEnum::Success) {
|
||||
for (int character : result.value.value()) {
|
||||
device_name.push_back(static_cast<char>(character >> 8));
|
||||
device_name.push_back(static_cast<char>(character & 0xFF));
|
||||
}
|
||||
} else {
|
||||
set_deviceFault("Can't read device name");
|
||||
}
|
||||
|
||||
// Strip and new lines that may be present in device name
|
||||
std::replace(device_name.begin(), device_name.end(), '\n', ' ');
|
||||
return device_name;
|
||||
}
|
||||
|
||||
std::string isolation_monitorImpl::read_firmware_version() {
|
||||
auto result = mod->r_serial_comm_hub->call_modbus_read_holding_registers(
|
||||
config.imd_device_id, static_cast<std::underlying_type_t<ImdRegisters>>(ImdRegisters::DEVICE_FIRMWARE_VERSION),
|
||||
6);
|
||||
std::string firmware_version;
|
||||
if (result.status_code == types::serial_comm_hub_requests::StatusCodeEnum::Success) {
|
||||
firmware_version = fmt::format("Ident: {} | Version {}, Date: {}-{}-{} | Modbus Driver: {}",
|
||||
result.value.value()[0], result.value.value()[1], result.value.value()[2],
|
||||
result.value.value()[3], result.value.value()[4], result.value.value()[5]);
|
||||
} else {
|
||||
set_deviceFault("Can't read the firmware version");
|
||||
}
|
||||
return firmware_version;
|
||||
}
|
||||
|
||||
bool isolation_monitorImpl::check_for_faster_cablecheck() {
|
||||
auto result = mod->r_serial_comm_hub->call_modbus_read_holding_registers(
|
||||
config.imd_device_id,
|
||||
static_cast<std::underlying_type_t<ImdRegisters>>(ImdRegisters::DEVICE_FIRMWARE_VERSION) + 1, 1);
|
||||
std::string firmware_version;
|
||||
if (result.status_code == types::serial_comm_hub_requests::StatusCodeEnum::Success) {
|
||||
if (result.value.value()[0] > 500) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
set_deviceFault("Could not check if the device supports fast cable check");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isolation_monitorImpl::MeasurementValue isolation_monitorImpl::read_register(const ImdRegisters start_register) {
|
||||
types::serial_comm_hub_requests::Result register_response{};
|
||||
|
||||
register_response = mod->r_serial_comm_hub->call_modbus_read_holding_registers(
|
||||
config.imd_device_id, static_cast<std::underlying_type<ImdRegisters>::type>(start_register), 4);
|
||||
|
||||
MeasurementValue m;
|
||||
|
||||
if (register_response.status_code != types::serial_comm_hub_requests::StatusCodeEnum::Success) {
|
||||
set_deviceFault("Can't read registers");
|
||||
}
|
||||
|
||||
if (not register_response.value.has_value() or register_response.value.value().size() not_eq 4) {
|
||||
// force an error since we did not received fully the registers
|
||||
m.alarm = AlarmType::DeviceError;
|
||||
return m;
|
||||
}
|
||||
|
||||
// Convert std::vector<int> to std::vector<uint16_t>
|
||||
const auto& reg_value_int = register_response.value.value();
|
||||
std::vector<uint16_t> reg_value(reg_value_int.begin(), reg_value_int.end());
|
||||
return parse_register_data(reg_value, 0);
|
||||
}
|
||||
|
||||
isolation_monitorImpl::MeasurementValue
|
||||
isolation_monitorImpl::parse_register_data(const std::vector<uint16_t>& reg_value, size_t offset) {
|
||||
MeasurementValue m;
|
||||
|
||||
if (reg_value.size() < offset + 4) {
|
||||
m.alarm = AlarmType::DeviceError;
|
||||
return m;
|
||||
}
|
||||
|
||||
m.alarm = to_alarm_type(reg_value.at(offset + 2) >> 8);
|
||||
m.test = to_test_type(reg_value.at(offset + 2) >> 8);
|
||||
m.unit = to_unit_type(reg_value.at(offset + 2) & 0xFF);
|
||||
m.valid = to_valid_type(reg_value.at(offset + 2) & 0xFF);
|
||||
m.description = to_channel_description(reg_value.at(offset + 3));
|
||||
|
||||
uint32_t value{0};
|
||||
value += reg_value.at(offset + 0) << 16;
|
||||
value += reg_value.at(offset + 1);
|
||||
auto val = *reinterpret_cast<float*>(&value);
|
||||
m.value = val;
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
@@ -0,0 +1,425 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef MAIN_ISOLATION_MONITOR_IMPL_HPP
|
||||
#define MAIN_ISOLATION_MONITOR_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/isolation_monitor/Implementation.hpp>
|
||||
|
||||
#include "../Bender_isoCHA425HV.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
struct Conf {
|
||||
int imd_device_id;
|
||||
int r1_prealarm_kohm;
|
||||
int r2_alarm_kohm;
|
||||
bool undervoltage_alarm_enable;
|
||||
int undervoltage_alarm_threshold_V;
|
||||
bool overvoltage_alarm_enable;
|
||||
int overvoltage_alarm_threshold_V;
|
||||
bool alarm_memory_enable;
|
||||
bool relais_r1_mode;
|
||||
bool relais_r2_mode;
|
||||
int delay_startup_device;
|
||||
int delay_t_on_k1_k2;
|
||||
int delay_t_off_k1_k2;
|
||||
bool chademo_mode;
|
||||
bool selftest_enable_gridconnection;
|
||||
bool selftest_enable_at_start;
|
||||
int relay_k1_alarm_assignment;
|
||||
int relay_k2_alarm_assignment;
|
||||
bool always_publish_measurements;
|
||||
bool voltage_to_earth_monitoring_alarm_enable;
|
||||
bool disable_device_on_stop;
|
||||
};
|
||||
|
||||
class isolation_monitorImpl : public isolation_monitorImplBase {
|
||||
public:
|
||||
isolation_monitorImpl() = delete;
|
||||
isolation_monitorImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<Bender_isoCHA425HV>& mod,
|
||||
Conf& config) :
|
||||
isolation_monitorImplBase(ev, "main"), mod(mod), config(config){};
|
||||
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
// insert your public definitions here
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
|
||||
protected:
|
||||
// command handler functions (virtual)
|
||||
virtual void handle_start() override;
|
||||
virtual void handle_stop() override;
|
||||
virtual void handle_start_self_test(double& test_voltage_V) override;
|
||||
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
// insert your protected definitions here
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
|
||||
private:
|
||||
const Everest::PtrContainer<Bender_isoCHA425HV>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
enum class ImdRegisters : uint16_t {
|
||||
RESISTANCE_R_F_OHM = 1000,
|
||||
VOLTAGE_U_N_V = 1008,
|
||||
VOLTAGE_U_L1E_V = 1016,
|
||||
VOLTAGE_U_L2E_V = 1020,
|
||||
RESISTANCE_R_FU_OHM = 1028,
|
||||
DEVICE_NAME = 9800,
|
||||
DEVICE_FIRMWARE_VERSION = 9820,
|
||||
};
|
||||
|
||||
enum class AlarmType {
|
||||
NoAlarm,
|
||||
PreWarning,
|
||||
DeviceError,
|
||||
Warning,
|
||||
Alarm,
|
||||
Reserved,
|
||||
};
|
||||
|
||||
std::string to_string(AlarmType a) {
|
||||
switch (a) {
|
||||
case AlarmType::NoAlarm:
|
||||
return "NoAlarm";
|
||||
case AlarmType::PreWarning:
|
||||
return "PreWarning";
|
||||
case AlarmType::DeviceError:
|
||||
return "DeviceError";
|
||||
case AlarmType::Warning:
|
||||
return "Warning";
|
||||
case AlarmType::Alarm:
|
||||
return "Alarm";
|
||||
case AlarmType::Reserved:
|
||||
return "Reserved";
|
||||
}
|
||||
return "Reserved";
|
||||
};
|
||||
|
||||
enum class TestType {
|
||||
NoTest,
|
||||
InternalTest,
|
||||
ExternalTest,
|
||||
};
|
||||
|
||||
std::string to_string(TestType a) {
|
||||
switch (a) {
|
||||
case TestType::NoTest:
|
||||
return "NoTest";
|
||||
case TestType::InternalTest:
|
||||
return "InternalTest";
|
||||
case TestType::ExternalTest:
|
||||
return "ExternalTest";
|
||||
}
|
||||
return "NoTest";
|
||||
};
|
||||
|
||||
enum class ValidType {
|
||||
TrueValue,
|
||||
TrueValueIsSmaller,
|
||||
TrueValueIsBigger,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
std::string to_string(ValidType a) {
|
||||
switch (a) {
|
||||
case ValidType::TrueValue:
|
||||
return "TrueValue";
|
||||
case ValidType::TrueValueIsSmaller:
|
||||
return "TrueValueIsSmaller";
|
||||
case ValidType::TrueValueIsBigger:
|
||||
return "TrueValueIsBigger";
|
||||
case ValidType::Invalid:
|
||||
return "Invalid";
|
||||
}
|
||||
return "Invalid";
|
||||
};
|
||||
|
||||
enum class UnitType {
|
||||
Invalid,
|
||||
NoUnit,
|
||||
Ohm,
|
||||
Ampere,
|
||||
Volt,
|
||||
Percent,
|
||||
Hertz,
|
||||
Baud,
|
||||
Farad,
|
||||
Henry,
|
||||
DegC,
|
||||
DegF,
|
||||
Seconds,
|
||||
Minutes,
|
||||
Hours,
|
||||
Days,
|
||||
Months,
|
||||
};
|
||||
|
||||
std::string to_string(UnitType a) {
|
||||
switch (a) {
|
||||
case UnitType::Invalid:
|
||||
return "Invalid";
|
||||
case UnitType::NoUnit:
|
||||
return "NoUnit";
|
||||
case UnitType::Ohm:
|
||||
return "Ohm";
|
||||
case UnitType::Ampere:
|
||||
return "Ampere";
|
||||
case UnitType::Volt:
|
||||
return "Volt";
|
||||
case UnitType::Percent:
|
||||
return "Percent";
|
||||
case UnitType::Hertz:
|
||||
return "Hertz";
|
||||
case UnitType::Baud:
|
||||
return "Baud";
|
||||
case UnitType::Farad:
|
||||
return "Farad";
|
||||
case UnitType::Henry:
|
||||
return "Henry";
|
||||
case UnitType::DegC:
|
||||
return "DegC";
|
||||
case UnitType::DegF:
|
||||
return "DegF";
|
||||
case UnitType::Seconds:
|
||||
return "Seconds";
|
||||
case UnitType::Minutes:
|
||||
return "Minutes";
|
||||
case UnitType::Hours:
|
||||
return "Hours";
|
||||
case UnitType::Days:
|
||||
return "Days";
|
||||
case UnitType::Months:
|
||||
return "Months";
|
||||
}
|
||||
return "Invalid";
|
||||
};
|
||||
|
||||
enum class ChannelDescription {
|
||||
Undefined,
|
||||
IsolationError,
|
||||
IsolationError_rF,
|
||||
Voltage,
|
||||
UnderVoltage,
|
||||
OverVoltage,
|
||||
Capacity,
|
||||
IsolationError_Zi,
|
||||
GridConnection,
|
||||
EarthConnection,
|
||||
DeviceErrorIsometer,
|
||||
DeviceError,
|
||||
OwnAddress,
|
||||
};
|
||||
|
||||
std::string to_string(ChannelDescription a) {
|
||||
switch (a) {
|
||||
case ChannelDescription::Undefined:
|
||||
return "Undefined";
|
||||
case ChannelDescription::IsolationError:
|
||||
return "IsolationError";
|
||||
case ChannelDescription::IsolationError_rF:
|
||||
return "IsolationError_rF";
|
||||
case ChannelDescription::Voltage:
|
||||
return "Voltage";
|
||||
case ChannelDescription::UnderVoltage:
|
||||
return "UnderVoltage";
|
||||
case ChannelDescription::OverVoltage:
|
||||
return "OverVoltage";
|
||||
case ChannelDescription::Capacity:
|
||||
return "Capacity";
|
||||
case ChannelDescription::IsolationError_Zi:
|
||||
return "IsolationError_Zi";
|
||||
case ChannelDescription::GridConnection:
|
||||
return "GridConnection";
|
||||
case ChannelDescription::EarthConnection:
|
||||
return "EarthConnection";
|
||||
case ChannelDescription::DeviceErrorIsometer:
|
||||
return "DeviceErrorIsometer";
|
||||
case ChannelDescription::DeviceError:
|
||||
return "DeviceError";
|
||||
case ChannelDescription::OwnAddress:
|
||||
return "OwnAddress";
|
||||
}
|
||||
return "Undefined";
|
||||
};
|
||||
|
||||
ChannelDescription to_channel_description(uint16_t raw) {
|
||||
switch (raw) {
|
||||
case 1:
|
||||
return ChannelDescription::IsolationError;
|
||||
case 71:
|
||||
return ChannelDescription::IsolationError_rF;
|
||||
case 76:
|
||||
return ChannelDescription::Voltage;
|
||||
case 77:
|
||||
return ChannelDescription::UnderVoltage;
|
||||
case 78:
|
||||
return ChannelDescription::OverVoltage;
|
||||
case 82:
|
||||
return ChannelDescription::Capacity;
|
||||
case 86:
|
||||
return ChannelDescription::IsolationError_Zi;
|
||||
case 101:
|
||||
return ChannelDescription::GridConnection;
|
||||
case 102:
|
||||
return ChannelDescription::EarthConnection;
|
||||
case 115:
|
||||
return ChannelDescription::DeviceErrorIsometer;
|
||||
case 129:
|
||||
return ChannelDescription::DeviceError;
|
||||
case 145:
|
||||
return ChannelDescription::OwnAddress;
|
||||
default:
|
||||
return ChannelDescription::Undefined;
|
||||
}
|
||||
};
|
||||
|
||||
UnitType to_unit_type(uint8_t raw) {
|
||||
uint8_t t = raw & 0x1F;
|
||||
switch (t) {
|
||||
case 0:
|
||||
return UnitType::Invalid;
|
||||
case 1:
|
||||
return UnitType::NoUnit;
|
||||
case 2:
|
||||
return UnitType::Ohm;
|
||||
case 3:
|
||||
return UnitType::Ampere;
|
||||
case 4:
|
||||
return UnitType::Volt;
|
||||
case 5:
|
||||
return UnitType::Percent;
|
||||
case 6:
|
||||
return UnitType::Hertz;
|
||||
case 7:
|
||||
return UnitType::Baud;
|
||||
case 8:
|
||||
return UnitType::Farad;
|
||||
case 9:
|
||||
return UnitType::Henry;
|
||||
case 10:
|
||||
return UnitType::DegC;
|
||||
case 11:
|
||||
return UnitType::DegF;
|
||||
case 12:
|
||||
return UnitType::Seconds;
|
||||
case 13:
|
||||
return UnitType::Minutes;
|
||||
case 14:
|
||||
return UnitType::Hours;
|
||||
case 15:
|
||||
return UnitType::Days;
|
||||
case 16:
|
||||
return UnitType::Months;
|
||||
}
|
||||
return UnitType::Invalid;
|
||||
};
|
||||
|
||||
ValidType to_valid_type(uint8_t raw) {
|
||||
uint8_t t = raw >> 6;
|
||||
if (t == 1) {
|
||||
return ValidType::TrueValueIsSmaller;
|
||||
} else if (t == 2) {
|
||||
return ValidType::TrueValueIsBigger;
|
||||
} else if (t == 3) {
|
||||
return ValidType::Invalid;
|
||||
} else {
|
||||
return ValidType::TrueValue;
|
||||
}
|
||||
};
|
||||
|
||||
TestType to_test_type(uint8_t raw) {
|
||||
uint8_t t = raw >> 6;
|
||||
if (t == 1) {
|
||||
return TestType::InternalTest;
|
||||
} else if (t == 2) {
|
||||
return TestType::ExternalTest;
|
||||
} else {
|
||||
return TestType::NoTest;
|
||||
}
|
||||
};
|
||||
|
||||
AlarmType to_alarm_type(uint8_t raw) {
|
||||
uint8_t t = raw & 0x07;
|
||||
|
||||
if (t == 0x00) {
|
||||
return AlarmType::NoAlarm;
|
||||
} else if (t == 0x01) {
|
||||
return AlarmType::PreWarning;
|
||||
} else if (t == 0x02 and (raw & 0xC0) == 0x00) {
|
||||
return AlarmType::DeviceError;
|
||||
} else if (t == 0x04) {
|
||||
return AlarmType::Warning;
|
||||
} else if (t == 0x05) {
|
||||
return AlarmType::Alarm;
|
||||
} else {
|
||||
return AlarmType::Reserved;
|
||||
}
|
||||
};
|
||||
|
||||
struct MeasurementValue {
|
||||
float value{0.};
|
||||
AlarmType alarm{AlarmType::NoAlarm};
|
||||
TestType test{TestType::NoTest};
|
||||
UnitType unit{UnitType::Invalid};
|
||||
ValidType valid{ValidType::Invalid};
|
||||
ChannelDescription description{ChannelDescription::Undefined};
|
||||
};
|
||||
|
||||
MeasurementValue read_register(const ImdRegisters start_register);
|
||||
MeasurementValue parse_register_data(const std::vector<uint16_t>& reg_value, size_t offset);
|
||||
|
||||
std::string to_string(MeasurementValue m) {
|
||||
return fmt::format(" {} [{}] [{}] [{}] [{}] [{}]", m.value, to_string(m.unit), to_string(m.valid),
|
||||
to_string(m.description), to_string(m.alarm), to_string(m.test));
|
||||
};
|
||||
|
||||
types::isolation_monitor::IsolationMeasurement isolation_measurement_data{};
|
||||
|
||||
std::thread output_thread;
|
||||
std::atomic_bool enable_publishing{false};
|
||||
std::atomic_bool self_test_started{false};
|
||||
std::atomic_int self_test_timeout{0};
|
||||
|
||||
TestType last_test{TestType::NoTest};
|
||||
AlarmType last_alarm{AlarmType::NoAlarm};
|
||||
|
||||
void set_deviceFault(const std::string& message);
|
||||
void clear_deviceFault();
|
||||
|
||||
void configure_device();
|
||||
void start_measurements();
|
||||
void stop_measurements();
|
||||
void start_self_test();
|
||||
bool send_to_imd(const uint16_t& command, const uint16_t& value);
|
||||
void read_imd_values();
|
||||
std::string read_device_name();
|
||||
std::string read_firmware_version();
|
||||
bool check_for_faster_cablecheck();
|
||||
bool enable_faster_cable_check_mode();
|
||||
bool disable_faster_cable_check_mode();
|
||||
bool faster_cable_check_supported{false};
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
};
|
||||
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
// insert other definitions here
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
|
||||
#endif // MAIN_ISOLATION_MONITOR_IMPL_HPP
|
||||
Reference in New Issue
Block a user