traintastic diy: added throttle subscribe support

Dieser Commit ist enthalten in:
Reinder Feenstra 2023-04-02 22:52:48 +02:00
Ursprung d8d7ce95c1
Commit eb6aefab09
6 geänderte Dateien mit 128 neuen und 31 gelöschten Zeilen

Datei anzeigen

@ -53,9 +53,9 @@ Some messages are sent unsolicited by the DIY device to Traintastic if changes a
| [Set input state](#tdiyp-set-input-state) | Mandatory if input feature flag is set |
| [Get output state](#tdiyp-get-output-state) | Mandatory if output feature flag is set |
| [Set output state](#tdiyp-set-output-state) | Mandatory if output feature flag is set |
| [Throttle set function](#tdiyp-throttle-set-function) | |
| [Throttle set speed/direction](#tdiyp-throttle-set-speed-direction) | |
| [Throttle unsubscribe](#tdiyp-throttle-unsubscribe) | |
| [Throttle set function](#tdiyp-throttle-set-function) | Mandatory if throttle feature flag is set |
| [Throttle set speed/direction](#tdiyp-throttle-set-speed-direction) | Mandatory if throttle feature flag is set |
| [Throttle subscribe/unsubscribe](#tdiyp-throttle-sub-unsub) | Mandatory if throttle feature flag is set |
**Badges**:
- The $badge:since:v0.2$ badge indicates in which version of Traintastic the message is added.
@ -155,7 +155,7 @@ Sent by the DIY device as response to the *[get input state](#tdiyp-get-input-st
- `0x03` if input is invalid (only as response to a *[get input state](#tdiyp-get-input-state)* message)
- `0x04`...`0xFF` are reserved, do not use
Examples:
#### Examples
```
0x13 0x00 0x12 0x02 0x03
```
@ -213,7 +213,7 @@ Sent by the DIY device as response to the *[get output state](#tdiyp-get-output-
Set locomotive decoder speed and/or direction.
Once a *Throttle set speed/direction* message is sent, the *throttle id* will automatically be subscribed for speed, direction and function changes of the locomotive decoder specified by the *decoder address*.
To stop receiving these changes a [throttle unsubscribe](#tdiyp-throttle-unsubscribe) message has to be send to Traintastic by the DIY device.
To stop receiving these changes a *[throttle unsubscribe](#tdiyp-throttle-sub-unsub)* message has to be send to Traintastic by the DIY device.
#### Message
```
@ -229,19 +229,36 @@ To stop receiving these changes a [throttle unsubscribe](#tdiyp-throttle-unsubsc
- `<AL>` lowest 8bit of 14bit decoder address
- `<SP>` speed (step), `0`...`<SM>`
- `<SM>` maximum speed (step), set to `0` for emergency stop
- `<FL>` flage:
- `<FL>` flags:
- bit 0: direction `1`=forward, `0`=reverse
- bit 1...5: reserved, must be `0`
- bit 6: set to set direction
- bit 7: set to set speed
*Throttle id* can be used to distinguish different throttles within the DIY device if it represents multiple throttles. If the DIY device is a single throttle use `0x00` `0x00` as *throttle id*.
Internally Traintastic uses a value between `0` and `1` for the speed where `0` is stop and `1` is full speed. To determine a value between `0` and `1` Traintastic calculates `<SP> / <SM>`. Set `<SP>` and `<SM>` both to zero for an emergency stop.
Using the *flags* bit 6 and 7 it is possible to set speed and direction, just the speed, just the direction or nothing. Setting nothing still subscribes the *throttle id* for speed, direction and function changes.
#### Examples
```
0x37 0x00 0x01 0x00 0x03 0x07 0x0E 0xC1 0xFD
```
Set speed to 50% (= 7 / 14) in forward direction for decoder with address 3.
```
0x37 0x00 0x01 0x00 0x03 0x00 0x00 0x80 0xB5
```
Emergency stop decoder with address 3, don't change direction.
### Throttle set function $badge:since:v0.2$ {#tdiyp-throttle-set-function}
Enable/disable locomotive decoder function.
Once a *Throttle set function* message is sent, the *throttle id* will automatically be subscribed for speed, direction and function changes of the locomotive decoder specified by the *decoder address*.
To stop receiving these changes a [throttle unsubscribe](#tdiyp-throttle-unsubscribe) message has to be send to Traintastic by the DIY device.
To stop receiving these changes a *[throttle unsubscribe](#tdiyp-throttle-sub-unsub)* message has to be send to Traintastic by the DIY device.
#### Message
```
@ -259,8 +276,7 @@ To stop receiving these changes a [throttle unsubscribe](#tdiyp-throttle-unsubsc
- bit 0...6: function number
- bit 7: function value
Examples:
#### Examples
```
0x35 0x00 0x01 0x00 0x03 0x80 0xB7
```
@ -272,7 +288,11 @@ Enable F0 for decoder with address 3.
Disable F1 for decoder with long address 5.
## Throttle unsubscribe $badge:since:v0.2$ {#tdiyp-throttle-unsubscribe}
### Throttle subscribe/unsubscribe $badge:since:v0.2$ {#tdiyp-throttle-sub-unsub}
Subscribe/unsubscribe for change events. Traintastic will send a *[throttle set speed/direction](#tdiyp-throttle-set-speed-direction)* message whenever speed or direction changes and a *[throttle set function](#tdiyp-throttle-set-function)* message for every function that changes state.
Note: Subscribe is supported since $badge:since:v0.3$, older version only support unsubscribe.
#### Message
```
@ -283,6 +303,10 @@ Disable F1 for decoder with long address 5.
- `<TL>` low byte of 16bit throttle id
- `<AH>`:
- bit 0...5: highest 6bit of 14bit decoder address
- bit 6: reserved and must be `0`
- bit 6: action: `0` = Unsubscribe, `1` = Subscribe
- bit 7: set to force a *DCC long address*
- `<AL>` lowest 8bit of 14bit decoder address
When *subscribing* Traintastic will reply with a *[throttle set speed/direction](#tdiyp-throttle-set-speed-direction)* and a *[throttle set function](#tdiyp-throttle-set-function)* message for every function that is known for the address.
When *unsubscribing* Traintastic will reply with the same message to confirm the unsubscribe.

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -68,7 +68,7 @@ bool SimulationIOHandler::send(const Message& message)
reply(message);
break;
}
case OpCode::ThrottleUnsubscribe:
case OpCode::ThrottleSubUnsub:
{
// TODO
break;

Datei anzeigen

@ -239,14 +239,44 @@ void Kernel::receive(const Message& message)
}
break;
}
case OpCode::ThrottleUnsubscribe:
case OpCode::ThrottleSubUnsub:
{
if(!m_featureFlagsSet || !hasFeatureThrottle())
break;
const auto& unsubscribe = static_cast<const ThrottleUnsubscribe&>(message);
throttleUnsubscribe(unsubscribe.throttleId(), {unsubscribe.address(), unsubscribe.isLongAddress()});
send(unsubscribe);
const auto& subUnsub = static_cast<const ThrottleSubUnsub&>(message);
switch(subUnsub.action())
{
case ThrottleSubUnsub::Unsubscribe:
throttleUnsubscribe(subUnsub.throttleId(), {subUnsub.address(), subUnsub.isLongAddress()});
send(subUnsub);
break;
case ThrottleSubUnsub::Subscribe:
throttleSubscribe(subUnsub.throttleId(), {subUnsub.address(), subUnsub.isLongAddress()});
EventLoop::call(
[this, subUnsub]()
{
if(auto decoder = getDecoder(subUnsub.address(), subUnsub.isLongAddress()))
{
uint8_t speedMax = 0;
uint8_t speed = 0;
if(!decoder->emergencyStop)
{
speedMax = decoder->speedSteps.value();
if(speedMax == Decoder::speedStepsAuto)
speedMax = std::numeric_limits<uint8_t>::max();
speed = Decoder::throttleToSpeedStep(decoder->throttle, speedMax);
}
postSend(ThrottleSetSpeedDirection(subUnsub.throttleId(), subUnsub.address(), subUnsub.isLongAddress(), speed, speedMax, decoder->direction));
for(const auto& function : *decoder->functions)
postSend(ThrottleSetFunction(subUnsub.throttleId(), subUnsub.address(), subUnsub.isLongAddress(), function->number, function->value));
}
});
break;
}
break;
}
case OpCode::ThrottleSetFunction:

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -26,6 +26,19 @@
namespace TraintasticDIY {
static constexpr std::string_view toString(ThrottleSubUnsub::Action action)
{
switch(action)
{
case ThrottleSubUnsub::Unsubscribe:
return "unsub";
case ThrottleSubUnsub::Subscribe:
return "sub";
}
return {};
}
Checksum calcChecksum(const Message& message)
{
const uint8_t* p = reinterpret_cast<const uint8_t*>(&message);
@ -84,11 +97,12 @@ std::string toString(const Message& message)
s.append(" state=").append(::toString(setOutputState.state));
break;
}
case OpCode::ThrottleUnsubscribe:
case OpCode::ThrottleSubUnsub:
{
const auto& throttleUnsubscribe = static_cast<const ThrottleUnsubscribe&>(message);
s.append(" throttle=").append(std::to_string(throttleUnsubscribe.throttleId()));
s.append(" address=").append(std::to_string(throttleUnsubscribe.address()));
const auto& throttleSubUnsub = static_cast<const ThrottleSubUnsub&>(message);
s.append(" throttle=").append(std::to_string(throttleSubUnsub.throttleId()));
s.append(" address=").append(std::to_string(throttleSubUnsub.address()));
s.append(" action=").append(toString(throttleSubUnsub.action()));
break;
}
case OpCode::ThrottleSetFunction:

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -199,17 +199,46 @@ struct ThrottleMessage : Message
};
static_assert(sizeof(ThrottleMessage) == 5);
struct ThrottleUnsubscribe : ThrottleMessage
struct ThrottleSubUnsub : ThrottleMessage
{
static constexpr uint8_t addressHighSubUnsubBit = 0x40;
enum Action
{
Unsubscribe = 0,
Subscribe = 1
};
Checksum checksum;
ThrottleUnsubscribe(uint16_t throttleId_, uint16_t address_, bool longAddress)
: ThrottleMessage(OpCode::ThrottleUnsubscribe, throttleId_, address_, longAddress)
ThrottleSubUnsub(uint16_t throttleId_, uint16_t address_, bool longAddress, Action action_)
: ThrottleMessage(OpCode::ThrottleSubUnsub, throttleId_, address_, longAddress)
, checksum{calcChecksum(*this)}
{
setAction(action_);
}
Action action() const
{
return (addressHigh & addressHighSubUnsubBit) ? Subscribe : Unsubscribe;
}
void setAction(Action value)
{
assert(value == Subscribe || value == Unsubscribe);
switch(value)
{
case Subscribe:
addressHigh |= addressHighSubUnsubBit; // set
break;
case Unsubscribe:
addressHigh &= ~addressHighSubUnsubBit; // clear
break;
}
}
};
static_assert(sizeof(ThrottleUnsubscribe) == 6);
static_assert(sizeof(ThrottleSubUnsub) == 6);
struct ThrottleSetSpeedDirection : ThrottleMessage
{

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -34,7 +34,7 @@ enum class OpCode : uint8_t
SetInputState = 0x13,
GetOutputState = 0x22,
SetOutputState = 0x23,
ThrottleUnsubscribe = 0x34,
ThrottleSubUnsub = 0x34,
ThrottleSetFunction = 0x35,
ThrottleSetSpeedDirection = 0x37,
GetFeatures = 0xE0,
@ -66,8 +66,8 @@ constexpr std::string_view toString(TraintasticDIY::OpCode value)
case OpCode::SetOutputState:
return "SetOutputState";
case OpCode::ThrottleUnsubscribe:
return "ThrottleUnsubscribe";
case OpCode::ThrottleSubUnsub:
return "ThrottleSubUnsub";
case OpCode::ThrottleSetFunction:
return "ThrottleSetFunction";