removed Env MQTT topic
This commit is contained in:
parent
8eda0b4a36
commit
cd8491dcc0
|
|
@ -107,6 +107,13 @@ scripts for this are worth keeping; note that wrapping them in `timeout` SIGTERM
|
|||
|
||||
## Possible follow-ups (not done)
|
||||
|
||||
- **A retained `Env` payload may still sit on the broker.** `GGS/FWT/<tower>/Env` was
|
||||
published retained until it was removed, so a broker that saw the old build still holds
|
||||
the last ambient reading and hands it to every new subscriber — a frozen temperature and
|
||||
humidity with an old timestamp, indistinguishable from a sensor that stopped reporting.
|
||||
Nothing in the code does this any more, so nothing here would otherwise explain it.
|
||||
Clear it broker-side by publishing a zero-length retained message to that topic.
|
||||
|
||||
- **NWU vs ENU is decided by a constant, not by the device.** `kMtiReference`
|
||||
([MtiProtocol.h](../include/fgc/MtiProtocol.h)) says the MTi reports against NWU, and the
|
||||
host re-references it to NED. The two candidates give *identical* roll and pitch and
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Per-file reference for the refactored tree, plus the shared data structures.
|
|||
| [include/fgc/sensors/Sht41Protocol.h](../include/fgc/sensors/Sht41Protocol.h), [src/sensors/Sht41Protocol.cpp](../src/sensors/Sht41Protocol.cpp) | Sensirion SHT4x wire protocol, pure: command bytes, `sht41Crc8` (poly 0x31/init 0xFF), `decodeSht41Measurement` (6-byte response → `Sht41Reading`, CRC-checked, raw→°C/%RH with RH clamped 0..100) |
|
||||
| [include/fgc/CameraProfile.h](../include/fgc/CameraProfile.h), [src/core/CameraProfile.cpp](../src/core/CameraProfile.cpp) | `loadCameraProfile` / `loadCameraProfiles`: one VimbaX settings XML per profile. Hashes the XML, regenerates the `.txt` record when it changes, and reports which settings moved. A missing XML throws — the record is never a fallback |
|
||||
| [include/fgc/CameraSettingsXml.h](../include/fgc/CameraSettingsXml.h), [src/core/CameraSettingsXml.cpp](../src/core/CameraSettingsXml.cpp) | Parses the Viewer export's `<RemoteDevice>` features (ordered, selector-aware), renders the human-readable `.txt` record, reads one back, and diffs two settings lists. Used for display and change detection only — never to configure a camera |
|
||||
| [include/fgc/ChannelTopics.h](../include/fgc/ChannelTopics.h), [src/core/ChannelTopics.cpp](../src/core/ChannelTopics.cpp) | The MQTT wire format in one place: topic builders for the `GGS/FWT/<tower>/…` tree and the `CamEvent`/`EnvEvent`/status payload builders. Pure, so the documented format is unit-tested rather than assembled inline at each publish site |
|
||||
| [include/fgc/ChannelTopics.h](../include/fgc/ChannelTopics.h), [src/core/ChannelTopics.cpp](../src/core/ChannelTopics.cpp) | The MQTT wire format in one place: topic builders for the `GGS/FWT/<tower>/…` tree and the `CamEvent`/status payload builders. Pure, so the documented format is unit-tested rather than assembled inline at each publish site |
|
||||
| [include/fgc/ChannelHistory.h](../include/fgc/ChannelHistory.h), [src/core/ChannelHistory.cpp](../src/core/ChannelHistory.cpp) | Ring buffer of recently published messages (time, topic, payload, outcome) plus the delivery counters — sent, failed, connects, losses, last status. Feeds the expanded Connectivity view so a publish can be confirmed without a broker-side subscriber |
|
||||
| [include/fgc/Hash.h](../include/fgc/Hash.h), [src/core/Hash.cpp](../src/core/Hash.cpp) | FNV-1a 64 over a string or a file, with an algorithm-prefixed label. Change detection for the profile records, deliberately not a crypto hash |
|
||||
| [include/fgc/CaptureScheduler.h](../include/fgc/CaptureScheduler.h), [src/core/CaptureScheduler.cpp](../src/core/CaptureScheduler.cpp) | Capture state machine over the interfaces; injectable clock; `setGeometry` adopts a recalibration |
|
||||
|
|
@ -38,7 +38,7 @@ Per-file reference for the refactored tree, plus the shared data structures.
|
|||
| File | Interface | Shared structs |
|
||||
|------|-----------|----------------|
|
||||
| [include/fgc/IMotorController.h](../include/fgc/IMotorController.h) | `IMotorController` | `MotorTelemetry` |
|
||||
| [include/fgc/IControlChannel.h](../include/fgc/IControlChannel.h) | `IControlChannel` | `ControlCommand`, `CamEvent`, `EnvEvent` |
|
||||
| [include/fgc/IControlChannel.h](../include/fgc/IControlChannel.h) | `IControlChannel` | `ControlCommand`, `CamEvent` |
|
||||
| [include/fgc/ICameraSource.h](../include/fgc/ICameraSource.h) | `ICameraSource` | `Frame` |
|
||||
| [include/fgc/IImuSource.h](../include/fgc/IImuSource.h) | `IImuSource` (incl. `config()`, `configProblems()`, `refreshConfig()`, `noRotation()`, `headingReset()`, `setFilterProfile()`) | `ImuSample` (from `MtiProtocol.h`) |
|
||||
| [include/fgc/IEnvSensor.h](../include/fgc/IEnvSensor.h) | `IEnvSensor` (`connected()`, `sample()`, `name()`) | `EnvSample` |
|
||||
|
|
@ -98,10 +98,6 @@ homing or `gimbal reset`, so a lit badge means "position untrustworthy since the
|
|||
`tower`, `camera` (RGB/ACR/NIR), `heading_decideg` (yaw×10), `pitch_decideg` (pitch×10) and
|
||||
`timestamp_ms` (which matches the image filename). Serialized to the CamEvent JSON payload (see [mqtt-api.md](mqtt-api.md)).
|
||||
|
||||
### `EnvEvent` ([IControlChannel.h](../include/fgc/IControlChannel.h))
|
||||
`tower`, `temp_c`, `humidity_pct`, `timestamp_ms`. Published once per **fresh** sample (deduplicated
|
||||
on `timestamp_ms`, not per control-loop tick) to the `Env` topic — see [mqtt-api.md](mqtt-api.md).
|
||||
|
||||
### `EnvSample` ([IEnvSensor.h](../include/fgc/IEnvSensor.h))
|
||||
One ambient reading: `valid`, `temp_c` (°C), `humidity_pct` (0..100), `timestamp_ms` (epoch ms).
|
||||
`IEnvSensor::sample()` returns `nullopt` rather than a stale sample once nothing valid has arrived
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ When a ControlCode message arrives, the program echoes the current code back on
|
|||
|-------|------|---------|--------------|
|
||||
| `GGS/FWT/<tower>/StatusCode` | At startup (`"0"`); whenever a ControlCode message is received (echoes the code) | integer as string | 1 / retained |
|
||||
| `GGS/FWT/<tower>/CamEvent` | After each image is saved | JSON object (below) | 1 / retained |
|
||||
| `GGS/FWT/<tower>/Env` | Once per fresh ambient sensor sample (see `[Env] period_ms`) | JSON object (below) | 1 / retained |
|
||||
|
||||
### CamEvent payload
|
||||
|
||||
|
|
@ -101,22 +100,6 @@ Both `hdg` and `pit` are sampled **when the frame arrives**, not when its JPEG X
|
|||
finishes, so a slow encode or a backed-up queue cannot attribute a later pose to earlier
|
||||
pixels.
|
||||
|
||||
### Env payload
|
||||
|
||||
Built by `MqttControlChannel::publishEnv` from an `EnvEvent`, sourced from whichever `IEnvSensor` backend is
|
||||
active (SHT41 on the LattePanda's own I2C bus by default; see [configuration.md](configuration.md)):
|
||||
|
||||
```json
|
||||
{ "fwt":"ExampleTower", "temp_c":22.4, "humid_pct":46.1, "time":1719312345678 }
|
||||
```
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|-------|------|---------|
|
||||
| `fwt` | string | Tower name (`config.ini` `tower_name`) |
|
||||
| `temp_c` | float | Ambient temperature, °C |
|
||||
| `humid_pct` | float | Ambient relative humidity, % |
|
||||
| `time` | int | Sample timestamp, Unix epoch **milliseconds** |
|
||||
|
||||
## Topic summary
|
||||
|
||||
```
|
||||
|
|
@ -125,7 +108,6 @@ subscribe: GGS/FWT/<tower>/target_HDG (int heading)
|
|||
|
||||
publish: GGS/FWT/<tower>/StatusCode (echoed control code)
|
||||
GGS/FWT/<tower>/CamEvent (JSON: fwt, cam, hdg×10, pit×10, time-ms)
|
||||
GGS/FWT/<tower>/Env (JSON: fwt, temp_c, humid_pct, time-ms)
|
||||
```
|
||||
|
||||
## Local testing
|
||||
|
|
|
|||
|
|
@ -18,12 +18,10 @@ inline constexpr const char* kTopicTargetHdg = "target_HDG";
|
|||
inline constexpr const char* kTopicControlCode = "ControlCode";
|
||||
inline constexpr const char* kTopicStatusCode = "StatusCode";
|
||||
inline constexpr const char* kTopicCamEvent = "CamEvent";
|
||||
inline constexpr const char* kTopicEnv = "Env";
|
||||
|
||||
std::string towerTopic(const std::string& tower, const std::string& leaf);
|
||||
|
||||
// Payload builders. One definition each, used by both channels.
|
||||
std::string camEventJson(const CamEvent& e);
|
||||
std::string envJson(const EnvEvent& e);
|
||||
|
||||
} // namespace fgc
|
||||
|
|
|
|||
|
|
@ -23,14 +23,6 @@ struct CamEvent {
|
|||
long long timestamp_ms = 0; // Unix epoch ms; matches the image filename
|
||||
};
|
||||
|
||||
// Ambient temperature/humidity reading published once per fresh sensor sample.
|
||||
struct EnvEvent {
|
||||
std::string tower;
|
||||
float temp_c = 0.0f;
|
||||
float humidity_pct = 0.0f;
|
||||
long long timestamp_ms = 0;
|
||||
};
|
||||
|
||||
// What became of an outbound message. Dropped and Failed are separated because
|
||||
// they mean different things operationally: Dropped is "the link was down and we
|
||||
// discarded it silently", Failed is "the broker or library rejected it".
|
||||
|
|
@ -71,7 +63,6 @@ public:
|
|||
|
||||
virtual void publishStatus(int code) = 0;
|
||||
virtual void publishCamEvent(const CamEvent& event) = 0;
|
||||
virtual void publishEnv(const EnvEvent& event) = 0;
|
||||
|
||||
// Latest control input; clears the *_available flags so each update is
|
||||
// acted on once.
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ public:
|
|||
|
||||
void publishStatus(int code) override;
|
||||
void publishCamEvent(const CamEvent& event) override;
|
||||
void publishEnv(const EnvEvent& event) override;
|
||||
ControlCommand poll() override;
|
||||
|
||||
std::vector<ChannelMessage> recentMessages() const override { return history_.recent(); }
|
||||
|
|
@ -59,7 +58,6 @@ private:
|
|||
std::string topic_control_code_;
|
||||
std::string topic_status_;
|
||||
std::string topic_cam_event_;
|
||||
std::string topic_env_;
|
||||
|
||||
mqtt::async_client client_;
|
||||
mqtt::connect_options conn_opts_;
|
||||
|
|
|
|||
|
|
@ -38,13 +38,6 @@ public:
|
|||
<< " (null channel)";
|
||||
drop(kTopicCamEvent, camEventJson(e));
|
||||
}
|
||||
void publishEnv(const EnvEvent& e) override {
|
||||
LOG_TRACE_CAT(LogCat::Mqtt) << "PUB Env temp_c=" << e.temp_c
|
||||
<< " humid_pct=" << e.humidity_pct << " t=" << e.timestamp_ms
|
||||
<< " (null channel)";
|
||||
drop(kTopicEnv, envJson(e));
|
||||
}
|
||||
|
||||
ControlCommand poll() override { return {}; }
|
||||
|
||||
std::vector<ChannelMessage> recentMessages() const override { return history_.recent(); }
|
||||
|
|
|
|||
|
|
@ -230,7 +230,6 @@ struct Application::Impl {
|
|||
bool test_drift_gate_ = false;
|
||||
mutable ImuConfigView imu_config_view_; // formatted MTi config (read once)
|
||||
mutable bool imu_config_done_ = false;
|
||||
mutable long long last_env_published_ts_ = 0; // gate MQTT publish to one per fresh sample
|
||||
// Latches the "no IMU sample, publishing encoder pitch" warning so an outage
|
||||
// costs one line, not one per frame. Written from the camera delivery
|
||||
// thread(s) via the orientation supplier, hence atomic.
|
||||
|
|
@ -746,16 +745,6 @@ struct Application::Impl {
|
|||
setField(g.fields[0], fmt1(m->temp_c));
|
||||
setField(g.fields[1], fmt1(m->humidity_pct));
|
||||
}
|
||||
// Publish once per fresh sample, not every ~10ms control-loop tick.
|
||||
if (channel && m->timestamp_ms != last_env_published_ts_) {
|
||||
last_env_published_ts_ = m->timestamp_ms;
|
||||
EnvEvent ev;
|
||||
ev.tower = cfg.general.tower_name;
|
||||
ev.temp_c = m->temp_c;
|
||||
ev.humidity_pct = m->humidity_pct;
|
||||
ev.timestamp_ms = m->timestamp_ms;
|
||||
channel->publishEnv(ev);
|
||||
}
|
||||
} else {
|
||||
s.sensors.env.status = "no fix";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,4 @@ std::string camEventJson(const CamEvent& e) {
|
|||
", \"time\":" + std::to_string(e.timestamp_ms) + " }";
|
||||
}
|
||||
|
||||
std::string envJson(const EnvEvent& e) {
|
||||
return "{ \"fwt\":\"" + e.tower + "\", \"temp_c\":" + std::to_string(e.temp_c) +
|
||||
", \"humid_pct\":" + std::to_string(e.humidity_pct) +
|
||||
", \"time\":" + std::to_string(e.timestamp_ms) + " }";
|
||||
}
|
||||
|
||||
} // namespace fgc
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ MqttControlChannel::MqttControlChannel(const std::string& broker_uri, const std:
|
|||
topic_control_code_(towerTopic(tower, kTopicControlCode)),
|
||||
topic_status_(towerTopic(tower, kTopicStatusCode)),
|
||||
topic_cam_event_(towerTopic(tower, kTopicCamEvent)),
|
||||
topic_env_(towerTopic(tower, kTopicEnv)),
|
||||
client_(broker_uri, tower) {
|
||||
conn_opts_.set_keep_alive_interval(20);
|
||||
conn_opts_.set_clean_session(true);
|
||||
|
|
@ -136,8 +135,4 @@ void MqttControlChannel::publishCamEvent(const CamEvent& e) {
|
|||
publish(topic_cam_event_, camEventJson(e));
|
||||
}
|
||||
|
||||
void MqttControlChannel::publishEnv(const EnvEvent& e) {
|
||||
publish(topic_env_, envJson(e));
|
||||
}
|
||||
|
||||
} // namespace fgc
|
||||
|
|
|
|||
|
|
@ -544,7 +544,7 @@ Element connDetailPanel(const ConnView& v, ScrollState& st) {
|
|||
struct TopicRow { const char* dir; const char* leaf; };
|
||||
static const TopicRow kTopics[] = {
|
||||
{"sub", kTopicTargetHdg}, {"sub", kTopicControlCode}, {"pub", kTopicStatusCode},
|
||||
{"pub", kTopicCamEvent}, {"pub", kTopicEnv},
|
||||
{"pub", kTopicCamEvent},
|
||||
};
|
||||
for (const auto& t : kTopics) {
|
||||
const ChannelMessage* last = nullptr;
|
||||
|
|
|
|||
|
|
@ -82,11 +82,4 @@ TEST_CASE("topic and payload builders match the documented wire format") {
|
|||
CHECK(j.find("\"time\":1719312345678") != std::string::npos);
|
||||
// The quality gate is gone; nothing should still be publishing a verdict.
|
||||
CHECK(j.find("degraded") == std::string::npos);
|
||||
|
||||
EnvEvent e;
|
||||
e.tower = "Tower"; e.temp_c = 21.5f; e.humidity_pct = 48.0f; e.timestamp_ms = 42;
|
||||
const std::string ej = envJson(e);
|
||||
CHECK(ej.find("\"fwt\":\"Tower\"") != std::string::npos);
|
||||
CHECK(ej.find("\"temp_c\":21.5") != std::string::npos);
|
||||
CHECK(ej.find("\"time\":42") != std::string::npos);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ struct FakeChannel : IControlChannel {
|
|||
bool connected() const override { return true; }
|
||||
void publishStatus(int c) override { last_status = c; }
|
||||
void publishCamEvent(const CamEvent&) override {}
|
||||
void publishEnv(const EnvEvent&) override {}
|
||||
ControlCommand poll() override {
|
||||
ControlCommand c = next;
|
||||
next = {};
|
||||
|
|
|
|||
Loading…
Reference in New Issue