traintastic diy: added throttle subscribe support
Dieser Commit ist enthalten in:
Ursprung
d8d7ce95c1
Commit
eb6aefab09
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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";
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren