initial commit

This commit is contained in:
Tony
2026-03-26 12:04:08 +08:00
commit 7e8ac7f566
31 changed files with 4304 additions and 0 deletions

396
src/base.cpp Normal file
View File

@@ -0,0 +1,396 @@
#include "base.hpp"
#include "dali_define.hpp"
#include <algorithm>
DaliStatus DaliStatus::fromByte(uint8_t status) {
DaliStatus s;
s.controlGearPresent = (status & 0x01) != 0;
s.lampFailure = (status & 0x02) != 0;
s.lampPowerOn = (status & 0x04) != 0;
s.limitError = (status & 0x08) != 0;
s.fadingCompleted = (status & 0x10) != 0;
s.resetState = (status & 0x20) != 0;
s.missingShortAddress = (status & 0x40) != 0;
s.psFault = (status & 0x80) != 0;
return s;
}
DaliBase::DaliBase(DaliComm& comm) : comm_(comm) {}
int64_t DaliBase::mcuTicks() const {
const auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now());
return now.time_since_epoch().count();
}
uint8_t DaliBase::encodeCmdAddr(int dec_addr) { return DaliComm::toCmdAddr(dec_addr); }
uint8_t DaliBase::encodeArcAddr(int dec_addr) { return DaliComm::toArcAddr(dec_addr); }
bool DaliBase::toScene(int a, int s) {
const int scene = DALI_CMD_GO_TO_SCENE(s);
return send(a, static_cast<uint8_t>(scene));
}
bool DaliBase::reset(int a, int /*t*/) { return send(a, DALI_CMD_RESET); }
int DaliBase::brightnessToLog(int brightness) const {
const double val = std::log(static_cast<double>(brightness) + 1.0) / std::log(256.0) * 255.0;
return static_cast<int>(val);
}
int DaliBase::logToBrightness(int logValue) const {
const double val = std::pow(10.0, logValue * std::log(256.0) / std::log(10.0)) - 1.0;
return static_cast<int>(val);
}
bool DaliBase::setBright(int a, int b) {
int bright = std::clamp(b, 0, 254);
const auto addr = encodeArcAddr(a);
return comm_.sendCmd(addr, static_cast<uint8_t>(bright));
}
bool DaliBase::setBrightPercentage(int a, double b) {
int bright = static_cast<int>(b * 254.0 / 100.0);
bright = std::clamp(bright, 0, 254);
return setBright(a, bright);
}
bool DaliBase::stopFade(int a) { return sendCmd(encodeCmdAddr(a), DALI_CMD_STOP_FADE); }
bool DaliBase::off(int a) { return sendCmd(encodeCmdAddr(a), DALI_CMD_OFF); }
bool DaliBase::on(int a) { return sendCmd(encodeCmdAddr(a), DALI_CMD_RECALL_MAX); }
bool DaliBase::recallMaxLevel(int a) { return sendCmd(encodeCmdAddr(a), DALI_CMD_RECALL_MAX); }
bool DaliBase::recallMinLevel(int a) { return sendCmd(encodeCmdAddr(a), DALI_CMD_RECALL_MIN); }
int DaliBase::groupToAddr(int gp) const { return 64 + gp; }
bool DaliBase::send(int a, uint8_t cmd) { return sendCmd(encodeCmdAddr(a), cmd); }
bool DaliBase::sendCmd(uint8_t addr, uint8_t cmd) { return comm_.sendCmd(addr, cmd); }
bool DaliBase::sendExtCmd(int cmd, int value) { return comm_.sendExtCmd(static_cast<uint8_t>(cmd), static_cast<uint8_t>(value)); }
bool DaliBase::setDTR(int value) { return comm_.sendCmd(DALI_CMD_SPECIAL_SET_DTR0, static_cast<uint8_t>(value)); }
bool DaliBase::setDTR1(int value) { return comm_.sendCmd(DALI_CMD_SPECIAL_SET_DTR_1, static_cast<uint8_t>(value)); }
bool DaliBase::setDTR2(int value) { return comm_.sendCmd(DALI_CMD_SPECIAL_SET_DTR_2, static_cast<uint8_t>(value)); }
std::optional<int> DaliBase::getDTR(int a) { return query(a, DALI_CMD_QUERY_CONTENT_DTR); }
std::optional<int> DaliBase::getDTR1(int a) { return query(a, DALI_CMD_QUERY_CONTENT_DTR1); }
std::optional<int> DaliBase::getDTR2(int a) { return query(a, DALI_CMD_QUERY_CONTENT_DTR2); }
bool DaliBase::copyCurrentBrightToDTR(int a) {
return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_ACTUAL_LEVEL_IN_THE_DTR);
}
bool DaliBase::queryColourValue(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_QUERY_COLOR_VALUE); }
bool DaliBase::storeDTRAsAddr(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_DTR_AS_SHORT_ADDRESS); }
bool DaliBase::storeDTRAsSceneBright(int a, int s) {
const int value = DALI_CMD_SET_SCENE(s);
return sendExtCmd(encodeCmdAddr(a), value);
}
bool DaliBase::storeScene(int a, int s) {
return copyCurrentBrightToDTR(a) && storeDTRAsSceneBright(a, s);
}
bool DaliBase::removeScene(int a, int s) {
const int value = DALI_CMD_REMOVE_SCENE(s);
return sendExtCmd(encodeCmdAddr(a), value);
}
bool DaliBase::addToGroup(int a, int g) {
const int value = DALI_CMD_ADD_TO_GROUP(g);
return sendExtCmd(encodeCmdAddr(a), value);
}
bool DaliBase::removeFromGroup(int a, int g) {
const int value = DALI_CMD_REMOVE_FROM_GROUP(g);
return sendExtCmd(encodeCmdAddr(a), value);
}
bool DaliBase::storeDTRAsFadeTime(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_THE_DTR_AS_FADE_TIME); }
bool DaliBase::storeDTRAsFadeRate(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_THE_DTR_AS_FADE_RATE); }
bool DaliBase::storeDTRAsPoweredBright(int a) {
return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_THE_DTR_AS_PWR_ON_LEVEL);
}
bool DaliBase::storeDTRAsSystemFailureLevel(int a) {
return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_THE_DTR_AS_SYS_FAIL_LEVEL);
}
bool DaliBase::storeDTRAsMinLevel(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_THE_DTR_AS_MIN_LEVEL); }
bool DaliBase::storeDTRAsMaxLevel(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_STORE_THE_DTR_AS_MAX_LEVEL); }
bool DaliBase::storeColourTempLimits(int a) {
return sendExtCmd(encodeCmdAddr(a), DALI_CMD_DT8_STORE_COLOR_TEMPERATURE_LIMIT);
}
std::optional<bool> DaliBase::getOnlineStatus(int a) {
const auto status = query(a, DALI_CMD_QUERY_BALLAST);
if (!status.has_value()) return std::nullopt;
return status.value() == 255;
}
std::optional<int> DaliBase::getBright(int a) {
const auto res = query(a, DALI_CMD_QUERY_ACTUAL_LEVEL);
if (!res.has_value()) return std::nullopt;
if (res.value() == 255) return 254;
return res;
}
std::optional<int> DaliBase::getDeviceType(int a) { return queryCmd(encodeCmdAddr(a), DALI_CMD_QUERY_DEVICE_TYPE); }
std::optional<int> DaliBase::getPhysicalMinLevel(int a) {
return queryCmd(encodeCmdAddr(a), DALI_CMD_QUERY_PHYSICAL_MINIMUM_LEVEL);
}
std::optional<int> DaliBase::getDeviceVersion(int a) {
return queryCmd(encodeCmdAddr(a), DALI_CMD_QUERY_VERSION_NUMBER);
}
bool DaliBase::dtSelect(int value) { return comm_.sendCmd(DALI_CMD_SPECIAL_DT_SELECT, static_cast<uint8_t>(value)); }
bool DaliBase::activate(int a) { return comm_.sendCmd(encodeCmdAddr(a), DALI_CMD_DT8_ACTIVATE); }
bool DaliBase::setDTRAsColourX(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_DT8_STORE_DTR_AS_COLORX); }
bool DaliBase::setDTRAsColourY(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_DT8_STORE_DTR_AS_COLORY); }
bool DaliBase::setDTRAsColourRGB(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_DT8_ACTIVATE); }
bool DaliBase::setDTRAsColourTemp(int a) { return sendExtCmd(encodeCmdAddr(a), DALI_CMD_DT8_SET_COLOR_TEMPERATURE); }
bool DaliBase::copyReportColourToTemp(int a) {
if (!dtSelect(8)) return false;
return sendExtCmd(encodeCmdAddr(a), DALI_CMD_DT8_COPY_REPORT_TO_TEMPORARY);
}
bool DaliBase::setGradualChangeSpeed(int a, int value) { return setDTR(value) && storeDTRAsFadeTime(a); }
bool DaliBase::setGradualChangeRate(int a, int value) { return setDTR(value) && storeDTRAsFadeRate(a); }
std::optional<std::pair<int, int>> DaliBase::getGradualChange(int a) {
const auto ret = query(a, DALI_CMD_QUERY_FADE_TIME_FADE_RATE);
if (!ret.has_value()) return std::nullopt;
int speed = ret.value();
int rate = 0;
while (speed > 15) {
speed -= 16;
rate++;
}
return std::make_pair(rate, speed);
}
std::optional<int> DaliBase::getGradualChangeRate(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->second;
}
std::optional<int> DaliBase::getGradualChangeSpeed(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->first;
}
bool DaliBase::setPowerOnLevel(int a, int value) { return setDTR(value) && storeDTRAsPoweredBright(a); }
std::optional<int> DaliBase::getPowerOnLevel(int a) { return query(a, DALI_CMD_QUERY_MAX_LEVEL); }
bool DaliBase::setSystemFailureLevel(int a, int value) { return setDTR(value) && storeDTRAsSystemFailureLevel(a); }
std::optional<int> DaliBase::getSystemFailureLevel(int a) { return query(a, DALI_CMD_QUERY_MIN_LEVEL); }
bool DaliBase::setMinLevel(int a, int value) { return setDTR(value) && storeDTRAsMinLevel(a); }
std::optional<int> DaliBase::getMinLevel(int a) { return query(a, DALI_CMD_QUERY_POWER_ON_LEVEL); }
bool DaliBase::setMaxLevel(int a, int value) { return setDTR(value) && storeDTRAsMaxLevel(a); }
std::optional<int> DaliBase::getMaxLevel(int a) { return query(a, DALI_CMD_QUERY_SYSTEM_FAILURE_LEVEL); }
bool DaliBase::setFadeTime(int a, int value) {
int v = value;
if (v > 15) v = 15;
return setDTR(v) && storeDTRAsFadeTime(a);
}
std::optional<int> DaliBase::getFadeTime(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->first;
}
bool DaliBase::setFadeRate(int a, int value) { return setDTR(value) && storeDTRAsFadeRate(a); }
std::optional<int> DaliBase::getFadeRate(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->second;
}
std::optional<int> DaliBase::getNextDeviceType(int a) { return query(a, DALI_CMD_QUERY_NEXT_DEVICE_TYPE); }
std::optional<int> DaliBase::getGroupH(int a) { return query(a, DALI_CMD_QUERY_GROUP_8_15); }
std::optional<int> DaliBase::getGroupL(int a) { return query(a, DALI_CMD_QUERY_GROUPS_0_7); }
std::optional<int> DaliBase::getGroup(int a) {
const auto h = getGroupH(a);
const auto l = getGroupL(a);
if (!h.has_value() || !l.has_value()) return std::nullopt;
return h.value() * 256 + l.value();
}
bool DaliBase::setGroup(int a, int value) {
auto currentGroupOpt = getGroup(a);
int currentGroup = currentGroupOpt.value_or(-1);
for (int i = 0; i < 16; i++) {
if (currentGroup != -1 && (currentGroup & (1 << i)) == (value & (1 << i))) {
continue;
}
if ((value & (1 << i)) != 0) {
if (!addToGroup(a, i)) return false;
} else {
if (!removeFromGroup(a, i)) return false;
}
}
return true;
}
std::optional<int> DaliBase::getScene(int a, int b) {
return query(a, static_cast<uint8_t>(DALI_CMD_QUERY_SCENE_LEVEL(b)));
}
bool DaliBase::setScene(int a, int b) { return setDTR(b) && storeDTRAsSceneBright(a, b); }
std::map<int, int> DaliBase::getScenes(int a) {
std::map<int, int> ret;
for (int i = 0; i < 16; i++) {
const auto r = getScene(a, i);
if (r.has_value()) ret[i] = r.value();
}
return ret;
}
std::optional<int> DaliBase::getStatus(int a) { return query(a, DALI_CMD_QUERY_STATUS); }
std::optional<bool> DaliBase::getControlGearPresent(int a) {
const auto ret = query(a, DALI_CMD_QUERY_BALLAST);
if (!ret.has_value()) return std::nullopt;
return ret.value() == 255;
}
std::optional<bool> DaliBase::getLampFailureStatus(int a) {
const auto ret = query(a, DALI_CMD_QUERY_LAMP_FAILURE);
if (!ret.has_value()) return std::nullopt;
return ret.value() == 255;
}
std::optional<bool> DaliBase::getLampPowerOnStatus(int a) {
const auto ret = query(a, DALI_CMD_QUERY_LAMP_POWER_ON);
if (!ret.has_value()) return std::nullopt;
return ret.value() == 255;
}
std::optional<bool> DaliBase::getLimitError(int a) {
const auto ret = query(a, DALI_CMD_QUERY_LIMIT_ERROR);
if (!ret.has_value()) return std::nullopt;
return ret.value() == 255;
}
std::optional<bool> DaliBase::getResetStatus(int a) {
const auto ret = query(a, DALI_CMD_QUERY_RESET_STATE);
if (!ret.has_value()) return std::nullopt;
return ret.value() == 255;
}
std::optional<bool> DaliBase::getMissingShortAddress(int a) {
const auto ret = query(a, DALI_CMD_QUERY_MISSING_SHORT_ADDRESS);
if (!ret.has_value()) return std::nullopt;
return ret.value() == 255;
}
bool DaliBase::terminate() { return comm_.sendCmd(DALI_CMD_SPECIAL_TERMINATE, DALI_CMD_OFF); }
bool DaliBase::randomise() { return comm_.sendExtCmd(DALI_CMD_SPECIAL_RANDOMIZE, DALI_CMD_OFF); }
bool DaliBase::initialiseAll() { return comm_.sendExtCmd(DALI_CMD_SPECIAL_INITIALIZE, DALI_CMD_OFF); }
bool DaliBase::initialise() { return comm_.sendExtCmd(DALI_CMD_SPECIAL_INITIALIZE, DALI_CMD_STOP_FADE); }
bool DaliBase::withdraw() { return comm_.sendCmd(DALI_CMD_SPECIAL_WITHDRAW, DALI_CMD_OFF); }
bool DaliBase::cancel() { return comm_.sendCmd(DALI_CMD_SPECIAL_CANCEL, DALI_CMD_OFF); }
bool DaliBase::physicalSelection() {
return comm_.sendCmd(DALI_CMD_SPECIAL_PHYSICAL_SELECTION, DALI_CMD_OFF);
}
bool DaliBase::queryAddressH(int addr) {
return comm_.sendCmd(DALI_CMD_SPECIAL_SEARCHADDRH, static_cast<uint8_t>(addr));
}
bool DaliBase::queryAddressM(int addr) {
return comm_.sendCmd(DALI_CMD_SPECIAL_SEARCHADDRM, static_cast<uint8_t>(addr));
}
bool DaliBase::queryAddressL(int addr) {
return comm_.sendCmd(DALI_CMD_SPECIAL_SEARCHADDRL, static_cast<uint8_t>(addr));
}
bool DaliBase::programShortAddr(int a) {
return comm_.sendCmd(DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS, encodeCmdAddr(a));
}
std::optional<int> DaliBase::queryShortAddr() {
const auto ret1 = queryCmd(DALI_CMD_SPECIAL_QUERY_SHORT_ADDRESS, DALI_CMD_OFF);
if (!ret1.has_value()) return std::nullopt;
int ret = ret1.value() - 1;
return ret / 2;
}
std::optional<bool> DaliBase::verifyShortAddr(int a) {
const auto res = queryCmd(DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS, encodeCmdAddr(a));
if (!res.has_value()) return std::nullopt;
return true;
}
std::optional<bool> DaliBase::compareAddress() {
const auto ret = queryCmd(DALI_CMD_SPECIAL_COMPARE, DALI_CMD_OFF);
if (!ret.has_value()) return std::nullopt;
return ret.value() >= 0;
}
std::optional<bool> DaliBase::compare(int h, int m, int l) {
if (!queryAddressL(l)) return std::nullopt;
if (!queryAddressM(m)) return std::nullopt;
if (!queryAddressH(h)) return std::nullopt;
const auto matched = compareAddress();
if (!matched.has_value()) return std::nullopt;
return matched.value();
}
std::optional<int> DaliBase::getRandomAddrH(int addr) { return query(addr, DALI_CMD_QUERY_RANDOM_ADDRESS_H); }
std::optional<int> DaliBase::getRandomAddrM(int addr) { return query(addr, DALI_CMD_QUERY_RANDOM_ADDRESS_M); }
std::optional<int> DaliBase::getRandomAddrL(int addr) { return query(addr, DALI_CMD_QUERY_RANDOM_ADDRESS_L); }
std::optional<int> DaliBase::query(int a, uint8_t cmd) { return comm_.queryCmd(encodeCmdAddr(a), cmd); }
std::optional<int> DaliBase::queryCmd(uint8_t addr, uint8_t cmd) { return comm_.queryCmd(addr, cmd); }