133 lines
5 KiB
C++
133 lines
5 KiB
C++
#pragma once
|
|
|
|
#include <QByteArray>
|
|
#include <QString>
|
|
#include <optional>
|
|
|
|
// Low-level, stateless helpers for the Apart Concept 1 RS-232 ASCII protocol.
|
|
//
|
|
// Port settings (fixed by the device): 19200 baud, 8 data bits, no parity,
|
|
// 1 stop bit, no flow control. Every instruction ends with <CR> (0x0D).
|
|
namespace Concept1 {
|
|
|
|
constexpr int kBaudRate = 19200;
|
|
constexpr char kCr = '\r';
|
|
constexpr char kLf = '\n';
|
|
|
|
// Volume levels: -79..0 dB, with -80 meaning "OFF".
|
|
constexpr int kLevelOff = -80;
|
|
constexpr int kLevelMin = -79;
|
|
constexpr int kLevelMax = 0;
|
|
|
|
// Equalizer (bass/treble): -14..14 in steps of 2.
|
|
constexpr int kEqMin = -14;
|
|
constexpr int kEqMax = 14;
|
|
constexpr int kEqStep = 2;
|
|
|
|
// Input gain: -20..14 in steps of 2.
|
|
constexpr int kGainMin = -20;
|
|
constexpr int kGainMax = 14;
|
|
constexpr int kGainStep = 2;
|
|
|
|
enum class Command { Set, Get, Inc, Dec };
|
|
|
|
enum class Zone { None, Zone1, Zone2 };
|
|
|
|
enum class Source { A, B, C, D };
|
|
|
|
// All attributes understood by the Concept 1.
|
|
enum class Attribute {
|
|
MultiZone,
|
|
ZoneLink,
|
|
MusicLevel, // MSCLVL
|
|
MicLevel, // MICLVL
|
|
MaxMusicLevel, // MAXMSCLVL
|
|
MaxMicLevel, // MAXMICLVL
|
|
Select, // SELECT A/B/C/D
|
|
EqBass, // EQBASS
|
|
EqTreble, // EQTREB
|
|
Standby, // STANDBY
|
|
AutoLoudness, // AUTOLD
|
|
PagingActive, // PAGACT
|
|
InputGain, // IPGAIN (per source)
|
|
Echo, // ECHO
|
|
LineFeed, // LF
|
|
BackSpace, // BS
|
|
Header, // HEADER
|
|
ValueFeedback, // VALFB
|
|
Info, // INFO (GET only)
|
|
SourceName, // SOURCENAME (per source)
|
|
Serial, // SERIAL (GET only)
|
|
HwVersion, // HWVRSN (GET only)
|
|
SwVersion, // SWVRSN (GET only)
|
|
Restore, // RESTORE (SET only)
|
|
Unknown
|
|
};
|
|
|
|
// Classification of a line received from the device.
|
|
enum class ReplyKind {
|
|
Value, // an attribute value, e.g. "MSCLVL ZONE1 -16"
|
|
Info, // a single line of a GET INFO dump (also Value-shaped)
|
|
Paging, // "PAGING ZONEx ON/OFF" or "PAGING ON/OFF"
|
|
Acknowledge, // "Command Executed!" (also accepts the doc typo "Exicuted")
|
|
PleaseCycle, // "Please Cycle Power!"
|
|
PowerDown, // "POWER DOWN!"
|
|
Standby, // bare "STANDBY ON" sent while not ready
|
|
Error, // "ERROR: ...!"
|
|
NotImplemented, // "Not Yet Implemented!"
|
|
Echo, // an echoed instruction (when ECHO is on)
|
|
Unknown
|
|
};
|
|
|
|
struct ParsedReply {
|
|
ReplyKind kind = ReplyKind::Unknown;
|
|
Attribute attribute = Attribute::Unknown;
|
|
Zone zone = Zone::None;
|
|
std::optional<Source> source;
|
|
QString rawText; // full original line (trimmed)
|
|
QString textValue; // string value (source names, serial, versions, error text)
|
|
std::optional<int> intValue; // numeric value where applicable (dB / eq / gain)
|
|
bool boolValue = false; // for ON/OFF attributes
|
|
bool hasBool = false; // whether boolValue is meaningful
|
|
};
|
|
|
|
// --- String mapping helpers ---------------------------------------------
|
|
QString attributeToken(Attribute attr); // e.g. "MSCLVL"
|
|
Attribute attributeFromToken(const QString &token); // case-insensitive
|
|
QString commandToken(Command cmd);
|
|
QString zoneToken(Zone zone); // "ZONE1"/"ZONE2"/""
|
|
QString sourceToken(Source src); // "A".."D"
|
|
std::optional<Source> sourceFromToken(const QString &token);
|
|
std::optional<Zone> zoneFromToken(const QString &token);
|
|
|
|
// --- Value validation / normalisation -----------------------------------
|
|
int clampLevel(int value); // -80..0
|
|
int normalizeEq(int value); // snap to nearest valid -14..14/2
|
|
int normalizeGain(int value); // snap to nearest valid -20..14/2
|
|
int clampStep(int step); // 1..10
|
|
QString levelToDisplay(int level); // "OFF" or "-16 dB"
|
|
QString levelToken(int level); // "OFF" or "-16" (protocol wire form)
|
|
|
|
// --- Command building ----------------------------------------------------
|
|
// Builds a full instruction terminated with <CR>.
|
|
QByteArray buildCommand(Command cmd, Attribute attr,
|
|
Zone zone = Zone::None,
|
|
std::optional<Source> source = std::nullopt,
|
|
std::optional<QString> value = std::nullopt);
|
|
|
|
// Convenience builders.
|
|
QByteArray buildGet(Attribute attr, Zone zone = Zone::None,
|
|
std::optional<Source> source = std::nullopt);
|
|
QByteArray buildSetBool(Attribute attr, bool on, Zone zone = Zone::None);
|
|
QByteArray buildSetInt(Attribute attr, int value, Zone zone = Zone::None,
|
|
std::optional<Source> source = std::nullopt);
|
|
QByteArray buildSetString(Attribute attr, const QString &value,
|
|
std::optional<Source> source = std::nullopt);
|
|
QByteArray buildStep(Command incOrDec, Attribute attr, int step,
|
|
Zone zone = Zone::None);
|
|
|
|
// --- Reply parsing -------------------------------------------------------
|
|
// Parses a single line (without <CR>/<LF>) received from the device.
|
|
ParsedReply parseReply(const QString &line);
|
|
|
|
} // namespace Concept1
|