mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 13:03:36 +00:00
add support for multiple powers per socket type
Both when reading from OSM and when generating OSM keys. Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
This commit is contained in:
@@ -90,20 +90,14 @@ ChargeSocketsHelper::ChargeSocketsHelper(std::string const & socketsList) : m_di
|
|||||||
desc.count = 0;
|
desc.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.size() >= 3)
|
try
|
||||||
{
|
{
|
||||||
try
|
desc.power = std::stod(std::string(fields[2]));
|
||||||
{
|
|
||||||
desc.power = std::stod(std::string(fields[2]));
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
desc.power = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
catch (...)
|
||||||
|
{
|
||||||
desc.power = 0;
|
desc.power = 0;
|
||||||
|
}
|
||||||
m_chargeSockets.push_back(desc);
|
m_chargeSockets.push_back(desc);
|
||||||
}
|
}
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
@@ -142,11 +136,8 @@ void ChargeSocketsHelper::AggregateChargeSocketKey(std::string const & k, std::s
|
|||||||
return; // ignore other suffixes
|
return; // ignore other suffixes
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize type if needed
|
// normalize type
|
||||||
// based on recommandations from https://wiki.openstreetmap.org/wiki/Key:socket:*
|
static std::unordered_map<std::string_view, std::string_view> const kTypeMap = {
|
||||||
static std::unordered_map<std::string, std::string> const kTypeMap = {
|
|
||||||
// also sometimes used in EU for 'type2_combo'
|
|
||||||
// -> those cases would require correcting the OSM tagging
|
|
||||||
{"tesla_supercharger", "nacs"},
|
{"tesla_supercharger", "nacs"},
|
||||||
{"tesla_destination", "nacs"},
|
{"tesla_destination", "nacs"},
|
||||||
{"tesla_standard", "nacs"},
|
{"tesla_standard", "nacs"},
|
||||||
@@ -163,145 +154,149 @@ void ChargeSocketsHelper::AggregateChargeSocketKey(std::string const & k, std::s
|
|||||||
if (std::find(SUPPORTED_TYPES.begin(), SUPPORTED_TYPES.end(), type) == SUPPORTED_TYPES.end())
|
if (std::find(SUPPORTED_TYPES.begin(), SUPPORTED_TYPES.end(), type) == SUPPORTED_TYPES.end())
|
||||||
type = UNKNOWN;
|
type = UNKNOWN;
|
||||||
|
|
||||||
// find or create descriptor
|
// Tokenize counts or powers
|
||||||
auto it = std::find_if(m_chargeSockets.begin(), m_chargeSockets.end(),
|
auto tokens = strings::Tokenize(v, ";");
|
||||||
[&](ChargeSocketDescriptor const & d) { return d.type == type; });
|
if (tokens.empty())
|
||||||
|
return;
|
||||||
if (it == m_chargeSockets.end())
|
|
||||||
{
|
|
||||||
m_chargeSockets.push_back({type, 0, 0});
|
|
||||||
it = std::prev(m_chargeSockets.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(v.size() > 0, ("empty value for socket key!"));
|
|
||||||
|
|
||||||
if (!isOutput)
|
if (!isOutput)
|
||||||
{
|
{
|
||||||
if (v == "yes")
|
// Parse counts
|
||||||
|
std::vector<unsigned int> counts;
|
||||||
|
for (auto & t : tokens)
|
||||||
{
|
{
|
||||||
it->count = 0;
|
strings::Trim(t);
|
||||||
|
if (t.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (t == "yes")
|
||||||
|
counts.push_back(0);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto c = std::stoi(std::string(t));
|
||||||
|
if (c > 0)
|
||||||
|
counts.push_back(c);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("Invalid count. Ignoring:", t));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG(LWARNING, ("Invalid count. Ignoring:", t));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// Update existing sockets or add new ones
|
||||||
|
for (size_t i = 0; i < counts.size(); ++i)
|
||||||
{
|
{
|
||||||
// try to parse count as a number
|
// Find the i-th socket of this type
|
||||||
int count;
|
size_t matched = 0;
|
||||||
|
auto it = std::find_if(m_chargeSockets.begin(), m_chargeSockets.end(), [&](ChargeSocketDescriptor const & d)
|
||||||
|
{
|
||||||
|
if (d.type != type)
|
||||||
|
return false;
|
||||||
|
if (matched == i)
|
||||||
|
return true;
|
||||||
|
++matched;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != m_chargeSockets.end())
|
||||||
|
it->count = counts[i];
|
||||||
|
else
|
||||||
|
m_chargeSockets.push_back({type, counts[i], 0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parse powers
|
||||||
|
std::vector<double> powers;
|
||||||
|
for (auto & t : tokens)
|
||||||
|
{
|
||||||
|
strings::Trim(t);
|
||||||
|
if (t.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string s = strings::MakeLowerCase(std::string(t));
|
||||||
|
size_t pos = s.find("va");
|
||||||
|
while (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
s.replace(pos, 2, "w");
|
||||||
|
pos = s.find("va", pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
double value = 0;
|
||||||
|
enum PowerUnit
|
||||||
|
{
|
||||||
|
WATT,
|
||||||
|
KILOWATT,
|
||||||
|
MEGAWATT
|
||||||
|
} unit = KILOWATT;
|
||||||
|
|
||||||
|
if (!s.empty() && s.back() == 'w')
|
||||||
|
{
|
||||||
|
unit = WATT;
|
||||||
|
s.pop_back();
|
||||||
|
if (!s.empty() && s.back() == 'k')
|
||||||
|
{
|
||||||
|
unit = KILOWATT;
|
||||||
|
s.pop_back();
|
||||||
|
}
|
||||||
|
else if (!s.empty() && s.back() == 'm')
|
||||||
|
{
|
||||||
|
unit = MEGAWATT;
|
||||||
|
s.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
count = std::stoi(v);
|
value = std::stod(s);
|
||||||
if (count <= 0)
|
if (value < 0)
|
||||||
{
|
{
|
||||||
LOG(LWARNING, ("Invalid socket count. Removing this socket.", ""));
|
LOG(LWARNING, ("Invalid power value. Ignoring:", t));
|
||||||
// TODO(skadge): incorrect behaviour if invalid count while modifying an existing socket that is not the last
|
continue;
|
||||||
// one!
|
}
|
||||||
m_chargeSockets.pop_back();
|
switch (unit)
|
||||||
return;
|
{
|
||||||
|
case WATT: value /= 1000.; break;
|
||||||
|
case MEGAWATT: value *= 1000.; break;
|
||||||
|
case KILOWATT: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
// ignore sockets with invalid counts (ie, can not be parsed to int)
|
LOG(LWARNING, ("Invalid power value. Ignoring:", t));
|
||||||
// note that if a valid power output is later set for this socket,
|
continue;
|
||||||
// the socket will be re-created with a default count of 'y'
|
|
||||||
LOG(LWARNING, ("Invalid count of charging socket. Removing it.", v));
|
|
||||||
// TODO(skadge): incorrect behaviour if invalid count while modifying an existing socket that is not the last
|
|
||||||
// one!
|
|
||||||
m_chargeSockets.pop_back();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
it->count = count;
|
|
||||||
|
powers.push_back(value);
|
||||||
}
|
}
|
||||||
}
|
// Update existing sockets or add new ones
|
||||||
else // isOutput == true => parse output power
|
for (size_t i = 0; i < powers.size(); ++i)
|
||||||
{
|
|
||||||
// example value string: "44;22kW;11kva;7400w"
|
|
||||||
|
|
||||||
std::string powerValues = strings::MakeLowerCase(v);
|
|
||||||
|
|
||||||
// replace all occurances of 'VA' by the more standard 'W' unit
|
|
||||||
size_t pos = powerValues.find("va");
|
|
||||||
while (pos != powerValues.npos)
|
|
||||||
{
|
{
|
||||||
powerValues.replace(pos, 2, "w");
|
size_t matched = 0;
|
||||||
pos = powerValues.find("va", pos + 1);
|
auto it = std::find_if(m_chargeSockets.begin(), m_chargeSockets.end(), [&](ChargeSocketDescriptor const & d)
|
||||||
}
|
|
||||||
|
|
||||||
// if a given socket type is present several times in the same charging
|
|
||||||
// station with different power outputs, the power outputs would be concatenated
|
|
||||||
// with ';'
|
|
||||||
auto powerTokens = strings::Tokenize(powerValues, ";/");
|
|
||||||
|
|
||||||
// TODO: for now, we only handle the *first* provided
|
|
||||||
// power output.
|
|
||||||
std::string num(powerTokens[0]);
|
|
||||||
strings::Trim(num);
|
|
||||||
|
|
||||||
if (num == "unknown")
|
|
||||||
{
|
|
||||||
it->power = 0;
|
|
||||||
m_dirty = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PowerUnit
|
|
||||||
{
|
|
||||||
WATT,
|
|
||||||
KILOWATT,
|
|
||||||
MEGAWATT
|
|
||||||
};
|
|
||||||
PowerUnit unit = KILOWATT; // if no unit, kW are assumed
|
|
||||||
|
|
||||||
if (num.size() > 2)
|
|
||||||
{
|
|
||||||
// do we have a unit?
|
|
||||||
if (num.back() == 'w')
|
|
||||||
{
|
{
|
||||||
unit = WATT;
|
if (d.type != type)
|
||||||
num.pop_back();
|
return false;
|
||||||
if (num.back() == 'k')
|
if (matched == i)
|
||||||
{
|
return true;
|
||||||
unit = KILOWATT;
|
++matched;
|
||||||
num.pop_back();
|
return false;
|
||||||
}
|
});
|
||||||
else if (num.back() == 'm')
|
|
||||||
{
|
|
||||||
unit = MEGAWATT;
|
|
||||||
num.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strings::Trim(num);
|
if (it != m_chargeSockets.end())
|
||||||
double value;
|
it->power = powers[i];
|
||||||
try
|
else
|
||||||
{
|
m_chargeSockets.push_back({type, 0, powers[i]}); // default count=0
|
||||||
value = std::stod(num);
|
|
||||||
if (value <= 0)
|
|
||||||
{
|
|
||||||
LOG(LWARNING, ("Invalid charging socket power value:", v));
|
|
||||||
// TODO(skadge): incorrect behaviour if invalid count while modifying an existing socket that is not the last
|
|
||||||
// one!
|
|
||||||
m_chargeSockets.pop_back();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream oss;
|
|
||||||
switch (unit)
|
|
||||||
{
|
|
||||||
case WATT: value = value / 1000.; break;
|
|
||||||
case MEGAWATT: value = value * 1000; break;
|
|
||||||
case KILOWATT: break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
LOG(LWARNING, ("Invalid charging socket power value:", v));
|
|
||||||
// TODO(skadge): incorrect behaviour if invalid count while modifying an existing socket that is not the last
|
|
||||||
// one!
|
|
||||||
m_chargeSockets.pop_back();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
it->power = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
@@ -345,27 +340,56 @@ OSMKeyValues ChargeSocketsHelper::GetOSMKeyValues()
|
|||||||
Sort();
|
Sort();
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> result;
|
std::vector<std::pair<std::string, std::string>> result;
|
||||||
|
std::string lastType;
|
||||||
|
std::ostringstream countStream;
|
||||||
|
std::ostringstream powerStream;
|
||||||
|
bool firstCount = true;
|
||||||
|
bool firstPower = true;
|
||||||
|
|
||||||
for (auto const & s : m_chargeSockets)
|
for (auto const & s : m_chargeSockets)
|
||||||
{
|
{
|
||||||
// Only produce if type is non-empty
|
if (s.type.empty())
|
||||||
if (!s.type.empty())
|
continue;
|
||||||
{
|
|
||||||
// socket.<type> = count
|
|
||||||
if (s.count > 0)
|
|
||||||
{
|
|
||||||
result.emplace_back("socket:" + s.type, std::to_string(s.count));
|
|
||||||
}
|
|
||||||
else if (s.count == 0)
|
|
||||||
{
|
|
||||||
// special "yes" meaning present, but unknown count
|
|
||||||
result.emplace_back("socket:" + s.type, "yes");
|
|
||||||
}
|
|
||||||
|
|
||||||
// socket.<type>.output = power
|
// If we switch type, flush previous streams
|
||||||
if (s.power > 0.0)
|
if (s.type != lastType && !lastType.empty())
|
||||||
result.emplace_back("socket:" + s.type + ":output", to_string_trimmed(s.power));
|
{
|
||||||
|
result.emplace_back("socket:" + lastType, countStream.str());
|
||||||
|
if (powerStream.str().size() > 0)
|
||||||
|
result.emplace_back("socket:" + lastType + ":output", powerStream.str());
|
||||||
|
|
||||||
|
countStream.str("");
|
||||||
|
countStream.clear();
|
||||||
|
powerStream.str("");
|
||||||
|
powerStream.clear();
|
||||||
|
firstCount = true;
|
||||||
|
firstPower = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastType = s.type;
|
||||||
|
|
||||||
|
// Append count
|
||||||
|
if (!firstCount)
|
||||||
|
countStream << ";";
|
||||||
|
countStream << ((s.count > 0) ? std::to_string(s.count) : "yes");
|
||||||
|
firstCount = false;
|
||||||
|
|
||||||
|
// Append power if > 0
|
||||||
|
if (s.power > 0)
|
||||||
|
{
|
||||||
|
if (!firstPower)
|
||||||
|
powerStream << ";";
|
||||||
|
powerStream << to_string_trimmed(s.power);
|
||||||
|
firstPower = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush last type
|
||||||
|
if (!lastType.empty())
|
||||||
|
{
|
||||||
|
result.emplace_back("socket:" + lastType, countStream.str());
|
||||||
|
if (powerStream.str().size() > 0)
|
||||||
|
result.emplace_back("socket:" + lastType + ":output", powerStream.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -75,17 +75,17 @@ public:
|
|||||||
*
|
*
|
||||||
* The list is guaranteed to be sorted first by socket type (same ordered as
|
* The list is guaranteed to be sorted first by socket type (same ordered as
|
||||||
* SUPPORTED_TYPES), then by power (descending).
|
* SUPPORTED_TYPES), then by power (descending).
|
||||||
|
*
|
||||||
|
* Note that this method is not const as it may trigger a re-ordering of the
|
||||||
|
* internal list of sockets.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
OSMKeyValues GetOSMKeyValues();
|
OSMKeyValues GetOSMKeyValues();
|
||||||
|
|
||||||
inline static std::string const UNKNOWN = "unknown";
|
static constexpr std::string_view UNKNOWN = "unknown";
|
||||||
|
|
||||||
/** List of supported sockets, ~ordered from high-power to low-power.
|
/** List of supported sockets, ~ordered from high-power to low-power.
|
||||||
* This order can be used in the UIs.
|
* This order can be used in the UIs.
|
||||||
*
|
|
||||||
* Note that this method is not const as it may trigger a re-ordering of the
|
|
||||||
* internal list of sockets.
|
|
||||||
*/
|
*/
|
||||||
static constexpr std::array<std::string_view, 12> SUPPORTED_TYPES = {
|
static constexpr std::array<std::string_view, 12> SUPPORTED_TYPES = {
|
||||||
"mcs", "type2_combo", "chademo", "nacs", "type1", "gb_dc",
|
"mcs", "type2_combo", "chademo", "nacs", "type1", "gb_dc",
|
||||||
|
|||||||
Reference in New Issue
Block a user