loconet: added support for F9-F20 (works with DR5000)

Dieser Commit ist enthalten in:
Reinder Feenstra 2020-07-16 23:41:15 +02:00
Ursprung 3edd6525d0
Commit 4bb36509a5
3 geänderte Dateien mit 387 neuen und 29 gelöschten Zeilen

Datei anzeigen

@ -22,7 +22,7 @@
#include "loconet.hpp"
#include <thread>
#include <chrono>
#include <chrono>
#include "../../core/eventloop.hpp"
#include "../../core/traintastic.hpp"
#include "../commandstation/commandstation.hpp"
@ -106,6 +106,16 @@ std::string to_string(const LocoNet::Message& message, bool raw = false)
s.append(" f8=").append(locoSnd.f8() ? "on" : "off");
break;
}
case LocoNet::OPC_LOCO_F9F12:
{
const LocoNet::LocoF9F12& locoF9F12 = static_cast<const LocoNet::LocoF9F12&>(message);
s.append(" slot=").append(std::to_string(locoF9F12.slot));
s.append(" f9=").append(locoF9F12.f9() ? "on" : "off");
s.append(" f10=").append(locoF9F12.f10() ? "on" : "off");
s.append(" f11=").append(locoF9F12.f11() ? "on" : "off");
s.append(" f12=").append(locoF9F12.f12() ? "on" : "off");
break;
}
case LocoNet::OPC_INPUT_REP:
{
const LocoNet::InputRep& inputRep = static_cast<const LocoNet::InputRep&>(message);
@ -170,11 +180,6 @@ std::string to_string(const LocoNet::Message& message, bool raw = false)
return s;
}
constexpr bool isLongAddress(uint16_t address)
{
return address > 127;
}
void updateDecoderSpeed(const std::shared_ptr<Hardware::Decoder>& decoder, uint8_t speed)
{
decoder->emergencyStop.setValueInternal(speed == LocoNet::SPEED_ESTOP);
@ -246,9 +251,9 @@ bool LocoNet::send(const Message& message)
return m_send(message);
}
void LocoNet::send(uint16_t address, SlotMessage& message)
void LocoNet::send(uint16_t address, Message& message, uint8_t& slot)
{
if((message.slot = m_slots.getSlot(address)) != SLOT_UNKNOWN)
if((slot = m_slots.getSlot(address)) != SLOT_UNKNOWN)
{
updateChecksum(message);
send(message);
@ -317,7 +322,7 @@ void LocoNet::receive(const Message& message)
updateDecoderSpeed(decoder, locoSpd.speed);
});
break;
case OPC_LOCO_DIRF:
EventLoop::call(
[this, locoDirF=*static_cast<const LocoDirF*>(&message)]()
@ -348,6 +353,20 @@ void LocoNet::receive(const Message& message)
});
break;
case OPC_LOCO_F9F12:
EventLoop::call(
[this, locoF9F12=*static_cast<const LocoF9F12*>(&message)]()
{
if(auto decoder = getDecoder(locoF9F12.slot))
{
decoder->setFunctionValue(9, locoF9F12.f9());
decoder->setFunctionValue(10, locoF9F12.f10());
decoder->setFunctionValue(11, locoF9F12.f11());
decoder->setFunctionValue(12, locoF9F12.f12());
}
});
break;
case OPC_INPUT_REP:
EventLoop::call(
[this, inputRep=*static_cast<const InputRep*>(&message)]()
@ -370,7 +389,7 @@ void LocoNet::receive(const Message& message)
else
m_queryLocoSlots = SLOT_UNKNOWN; // done
}
if(slotReadData.isBusy() || slotReadData.isActive())
{
m_slots.set(slotReadData.address(), slotReadData.slot);
@ -410,7 +429,8 @@ void LocoNet::decoderChanged(const Hardware::Decoder& decoder, Hardware::Decoder
LocoSpd message{static_cast<uint8_t>(decoder.emergencyStop ? 1 : (decoder.speedStep > 0 ? 1 + decoder.speedStep : 0))};
send(decoder.address, message);
}
else if(has(changes, DecoderChangeFlags::FunctionValue | DecoderChangeFlags::Direction))
if(has(changes, DecoderChangeFlags::FunctionValue | DecoderChangeFlags::Direction))
{
if(functionNumber <= 4 || has(changes, DecoderChangeFlags::Direction))
{
@ -432,6 +452,28 @@ void LocoNet::decoderChanged(const Hardware::Decoder& decoder, Hardware::Decoder
decoder.getFunctionValue(8)};
send(decoder.address, message);
}
else if(functionNumber <= 12)
{
LocoF9F12 message{
decoder.getFunctionValue(9),
decoder.getFunctionValue(10),
decoder.getFunctionValue(11),
decoder.getFunctionValue(12)};
send(decoder.address, message);
}
else if(functionNumber <= 20)
{
LocoF13F20 message{
decoder.getFunctionValue(13),
decoder.getFunctionValue(14),
decoder.getFunctionValue(15),
decoder.getFunctionValue(16),
decoder.getFunctionValue(17),
decoder.getFunctionValue(18),
decoder.getFunctionValue(19),
decoder.getFunctionValue(20)};
send(decoder.address, message);
}
else
logWarning("Function F" + std::to_string(functionNumber) + " not supported");
}

Datei anzeigen

@ -26,11 +26,6 @@
* LocoNet is a registered trademark of DigiTrax, Inc.
*/
/**
* OPC_MULTI_SENSE and OPC_MULTI_SENSE_LONG message format
* based on reverse engineering, see loconet.md
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_HPP
@ -55,6 +50,12 @@ class LocoNet : public SubObject
{
//friend class LocoNetInput;
protected:
static constexpr bool isLongAddress(uint16_t address)
{
return address > 127;
}
public:
struct Message;
@ -84,10 +85,24 @@ class LocoNet : public SubObject
static constexpr uint8_t SL_F2 = 0x02;
static constexpr uint8_t SL_F1 = 0x01;
static constexpr uint8_t SL_F8 = 0x08;
static constexpr uint8_t SL_F7 = 0x04;
static constexpr uint8_t SL_F6 = 0x02;
static constexpr uint8_t SL_F5 = 0x01;
static constexpr uint8_t SL_F6 = 0x02;
static constexpr uint8_t SL_F7 = 0x04;
static constexpr uint8_t SL_F8 = 0x08;
static constexpr uint8_t SL_F9 = 0x01;
static constexpr uint8_t SL_F10 = 0x02;
static constexpr uint8_t SL_F11 = 0x04;
static constexpr uint8_t SL_F12 = 0x08;
static constexpr uint8_t SL_F13 = 0x01;
static constexpr uint8_t SL_F14 = 0x02;
static constexpr uint8_t SL_F15 = 0x04;
static constexpr uint8_t SL_F16 = 0x08;
static constexpr uint8_t SL_F17 = 0x10;
static constexpr uint8_t SL_F18 = 0x20;
static constexpr uint8_t SL_F19 = 0x40;
static constexpr uint8_t SL_F20 = 0x80;
static constexpr uint8_t MULTI_SENSE_TYPE_MASK = 0xE0;
static constexpr uint8_t MULTI_SENSE_TYPE_TRANSPONDER_GONE = 0x00;
@ -106,6 +121,7 @@ class LocoNet : public SubObject
OPC_LOCO_SPD = 0xA0,
OPC_LOCO_DIRF = 0xA1,
OPC_LOCO_SND = 0xA2,
OPC_LOCO_F9F12 = 0xA3, // based on reverse engineering, see loconet.md
OPC_SW_REQ = 0xB0,
OPC_SW_REP = 0xB1,
OPC_INPUT_REP = 0xB2,
@ -121,10 +137,11 @@ class LocoNet : public SubObject
OPC_LOCO_ADR = 0xBF,
// 6 byte message opcodes:
OPC_MULTI_SENSE = 0xD0,
OPC_MULTI_SENSE = 0xD0, // based on reverse engineering, see loconet.md
OPC_D4 = 0xD4,// based on reverse engineering, probably used for multiple sub commands, see loconet.md
// variable byte message opcodes:
OPC_MULTI_SENSE_LONG = 0XE0,
OPC_MULTI_SENSE_LONG = 0XE0, // based on reverse engineering, see loconet.md
OPC_PEER_XFER = 0xE5,
OPC_SL_RD_DATA = 0xE7,
OPC_IMM_PACKET = 0xED,
@ -161,8 +178,7 @@ class LocoNet : public SubObject
return reinterpret_cast<const uint8_t*>(this)[1];
default:
//assert(false);
return 0;
return 0; // invalid opcode
}
}
};
@ -367,7 +383,7 @@ class LocoNet : public SubObject
uint8_t checksum;
LocoSnd(bool f5, bool f6, bool f7, bool f8) :
SlotMessage{OPC_LOCO_DIRF, SLOT_UNKNOWN},
SlotMessage{OPC_LOCO_SND, SLOT_UNKNOWN},
snd{0}
{
if(f5)
@ -436,6 +452,80 @@ class LocoNet : public SubObject
};
static_assert(sizeof(LocoSnd) == 4);
struct LocoF9F12 : SlotMessage
{
uint8_t function;
uint8_t checksum;
LocoF9F12(bool f9, bool f10, bool f11, bool f12) :
SlotMessage{OPC_LOCO_F9F12, SLOT_UNKNOWN},
function{0}
{
if(f9)
function |= SL_F9;
if(f10)
function |= SL_F10;
if(f11)
function |= SL_F11;
if(f12)
function |= SL_F12;
checksum = calcChecksum(*this);
}
inline bool f9() const
{
return function & SL_F9;
}
inline void setF9(bool value)
{
if(value)
function |= SL_F9;
else
function &= ~SL_F9;
}
inline bool f10() const
{
return function & SL_F10;
}
inline void setF10(bool value)
{
if(value)
function |= SL_F10;
else
function &= ~SL_F10;
}
inline bool f11() const
{
return function & SL_F7;
}
inline void setF11(bool value)
{
if(value)
function |= SL_F11;
else
function &= ~SL_F11;
}
inline bool f12() const
{
return function & SL_F8;
}
inline void setF12(bool value)
{
if(value)
function |= SL_F12;
else
function &= ~SL_F12;
}
};
static_assert(sizeof(LocoF9F12) == 4);
/*
@ -493,7 +583,7 @@ class LocoNet : public SubObject
uint8_t data2;
uint8_t checksum;
RequestSlotData(uint8_t _slot) :
RequestSlotData(uint8_t _slot) :
Message(OPC_RQ_SL_DATA),
slot{_slot},
data2{0}
@ -528,6 +618,43 @@ class LocoNet : public SubObject
};
static_assert(sizeof(MultiSense) == 6);
struct LocoF13F20 : Message
{
uint8_t data1;
uint8_t slot;
uint8_t data3;
uint8_t function;
uint8_t checksum;
LocoF13F20(bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20) :
Message(OPC_D4),
data1{0x20},
slot{SLOT_UNKNOWN},
data3{0x08},
function{0}
{
if(f13)
function |= SL_F13;
if(f14)
function |= SL_F14;
if(f15)
function |= SL_F15;
if(f16)
function |= SL_F16;
if(f17)
function |= SL_F17;
if(f18)
function |= SL_F18;
if(f19)
function |= SL_F19;
if(f20)
function |= SL_F20;
checksum = calcChecksum(*this);
}
};
static_assert(sizeof(LocoF13F20) == 6);
struct MultiSenseTransponder : MultiSense
{
bool isPresent() const
@ -792,7 +919,153 @@ class LocoNet : public SubObject
}
};
static_assert(sizeof(SlotReadData) == 14);
/*
struct ImmediatePacket : Message
{
uint8_t len;
uint8_t header;
uint8_t reps;
uint8_t dhi;
uint8_t im[5];
uint8_t checksum;
ImmediatePacket() :
Message(OPC_IMM_PACKET),
len{11},
header{0x7F},
reps{0},
dhi{0},
im{0, 0, 0, 0, 0}
{
}
void setIMCount(uint8_t value)
{
assert(value <= 5);
reps = (reps & 0x8F) | ((value & 0x07) << 4);
}
void updateDHI()
{
dhi = 0x20 |
(im[0] & 0x40) >> 7 |
(im[1] & 0x40) >> 6 |
(im[2] & 0x40) >> 5 |
(im[3] & 0x40) >> 4 |
(im[4] & 0x40) >> 3;
}
};
static_assert(sizeof(ImmediatePacket) == 11);
struct ImmediatePacketLoco : ImmediatePacket
{
ImmediatePacketLoco(uint16_t address, uint8_t repeatCount) :
ImmediatePacket()
{
assert(repeatCount <= 7);
reps = repeatCount & 0x07;
if(isLongAddress(address))
{
im[0] = 0xC0 | ((address >> 8) & 0x3F);
im[1] = address & 0xFF;
}
else
im[0] = address & 0x7F;
}
};
static_assert(sizeof(ImmediatePacketLoco) == sizeof(ImmediatePacket));
struct ImmediatePacketF9F12 : ImmediatePacketLoco
{
ImmediatePacketF9F12(uint16_t address, bool f9, bool f10, bool f11, bool f12, uint8_t repeatCount = 2) :
ImmediatePacketLoco(address, repeatCount)
{
const uint8_t offset = (im[0] & 0x80) ? 2 : 1;
im[offset] = 0xB0; // Function group two instruction: F9-F12
if(f9)
im[offset] |= 0x01;
if(f10)
im[offset] |= 0x02;
if(f11)
im[offset] |= 0x04;
if(f12)
im[offset] |= 0x08;
setIMCount(offset + 1);
updateDHI();
checksum = calcChecksum(*this);
}
};
static_assert(sizeof(ImmediatePacketF9F12) == sizeof(ImmediatePacketLoco));
struct ImmediatePacketF13F20 : ImmediatePacketLoco
{
ImmediatePacketF13F20(uint16_t address, bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20, uint8_t repeatCount = 2) :
ImmediatePacketLoco(address, repeatCount)
{
uint8_t offset = (im[0] & 0x80) ? 2 : 1;
im[offset++] = 0xDE; // Feature Expansion Instruction: F13-F20 function control
if(f13)
im[offset] |= 0x01;
if(f14)
im[offset] |= 0x02;
if(f15)
im[offset] |= 0x04;
if(f16)
im[offset] |= 0x08;
if(f17)
im[offset] |= 0x10;
if(f18)
im[offset] |= 0x20;
if(f19)
im[offset] |= 0x40;
if(f20)
im[offset] |= 0x80;
setIMCount(offset + 1);
updateDHI();
checksum = calcChecksum(*this);
}
};
static_assert(sizeof(ImmediatePacketF13F20) == sizeof(ImmediatePacketLoco));
struct ImmediatePacketF21F28 : ImmediatePacketLoco
{
ImmediatePacketF21F28(uint16_t address, bool f21, bool f22, bool f23, bool f24, bool f25, bool f26, bool f27, bool f28, uint8_t repeatCount = 2) :
ImmediatePacketLoco(address, repeatCount)
{
uint8_t offset = (im[0] & 0x80) ? 2 : 1;
im[offset++] = 0xDF; // Feature Expansion Instruction: F20-F28 function control
if(f21)
im[offset] |= 0x01;
if(f22)
im[offset] |= 0x02;
if(f23)
im[offset] |= 0x04;
if(f24)
im[offset] |= 0x08;
if(f25)
im[offset] |= 0x10;
if(f26)
im[offset] |= 0x20;
if(f27)
im[offset] |= 0x40;
if(f28)
im[offset] |= 0x80;
setIMCount(offset + 1);
updateDHI();
checksum = calcChecksum(*this);
}
};
static_assert(sizeof(ImmediatePacketF21F28) == sizeof(ImmediatePacketLoco));
*/
protected:
class Slots
{
@ -835,6 +1108,13 @@ class LocoNet : public SubObject
std::shared_ptr<Hardware::Decoder> getDecoder(uint8_t slot, bool request = true);
void send(uint16_t address, Message& message, uint8_t& slot);
template<typename T>
inline void send(uint16_t address, T& message)
{
send(address, message, message.slot);
}
public://protected:
bool isInputAddressAvailable(uint16_t address);
bool addInput(const std::shared_ptr<LocoNetInput>& input);
@ -849,7 +1129,6 @@ class LocoNet : public SubObject
LocoNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send);
bool send(const Message& message);
void send(uint16_t address, SlotMessage& message);
void receive(const Message& message);
void decoderChanged(const Hardware::Decoder& decoder, Hardware::DecoderChangeFlags changes, uint32_t functionNumber);
@ -860,6 +1139,3 @@ class LocoNet : public SubObject
}
#endif

Datei anzeigen

@ -1,5 +1,45 @@
# LocoNet
## DR5000 - loconet 0 using Multimaus
2020-07-16 22:20:38.953136 [debug] cs_1.loconet: rx: A3 [A3 1F 01 42] -> F9
2020-07-16 22:20:46.782380 [debug] cs_1.loconet: rx: A3 [A3 1F 00 43]
2020-07-16 22:21:13.039958 [debug] cs_1.loconet: rx: A3 [A3 1F 02 41] -> F10
2020-07-16 22:21:15.329510 [debug] cs_1.loconet: rx: A3 [A3 1F 00 43]
2020-07-16 22:21:20.004596 [debug] cs_1.loconet: rx: A3 [A3 1F 04 47] -> F11
2020-07-16 22:21:20.644998 [debug] cs_1.loconet: rx: A3 [A3 1F 00 43]
2020-07-16 22:21:21.413525 [debug] cs_1.loconet: rx: A3 [A3 1F 08 4B] -> F12
2020-07-16 22:21:21.893828 [debug] cs_1.loconet: rx: A3 [A3 1F 00 43]
F13 ON:
2020-07-16 22:49:50.937588 [debug] cs_1.loconet: rx: D4 [D4 20 1F 08 01 1D]
2020-07-16 22:49:50.937756 [debug] cs_1.loconet: rx: D4 [D4 20 1F 05 00 11] // why??
F13 OFF:
2020-07-16 22:49:53.723441 [debug] cs_1.loconet: rx: D4 [D4 20 1F 08 00 1C]
2020-07-16 22:49:53.723604 [debug] cs_1.loconet: rx: D4 [D4 20 1F 05 00 11]
F14 ON:
2020-07-16 22:50:54.355985 [debug] cs_1.loconet: rx: D4 [D4 20 1F 08 02 1E]
2020-07-16 22:50:54.356150 [debug] cs_1.loconet: rx: D4 [D4 20 1F 05 00 11]
F14 OFF:
2020-07-16 22:50:56.821657 [debug] cs_1.loconet: rx: D4 [D4 20 1F 08 00 1C]
2020-07-16 22:50:56.821816 [debug] cs_1.loconet: rx: D4 [D4 20 1F 05 00 11]
F15 ON:
2020-07-16 22:51:18.564022 [debug] cs_1.loconet: rx: D4 [D4 20 1F 08 04 18]
2020-07-16 22:51:18.564195 [debug] cs_1.loconet: rx: D4 [D4 20 1F 05 00 11]
F16 OFF:
2020-07-16 22:51:20.933660 [debug] cs_1.loconet: rx: D4 [D4 20 1F 08 00 1C]
2020-07-16 22:51:20.949613 [debug] cs_1.loconet: rx: D4 [D4 20 1F 05 00 11]
## DR5088RC - RailCom
OPC_MULTI_SENSE standard (dir=off):