#pragma once #include #include #include // 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 (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; QString rawText; // full original line (trimmed) QString textValue; // string value (source names, serial, versions, error text) std::optional 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 sourceFromToken(const QString &token); std::optional 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 . QByteArray buildCommand(Command cmd, Attribute attr, Zone zone = Zone::None, std::optional source = std::nullopt, std::optional value = std::nullopt); // Convenience builders. QByteArray buildGet(Attribute attr, Zone zone = Zone::None, std::optional 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 = std::nullopt); QByteArray buildSetString(Attribute attr, const QString &value, std::optional source = std::nullopt); QByteArray buildStep(Command incOrDec, Attribute attr, int step, Zone zone = Zone::None); // --- Reply parsing ------------------------------------------------------- // Parses a single line (without /) received from the device. ParsedReply parseReply(const QString &line); } // namespace Concept1