Reinder Feenstra 0883254190 xpressnet: implemented decoder address range
short: 0-99
long: 100-9999
see #72
2023-06-29 23:53:09 +02:00

602 Zeilen
14 KiB
C++

/**
* server/src/hardware/protocol/xpressnet/messages.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_XPRESSNET_MESSAGES_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_XPRESSNET_MESSAGES_HPP
#include <cstdint>
#include <cassert>
#include <cstring>
#include <string>
#include "../../../enum/direction.hpp"
namespace XpressNet {
constexpr uint16_t shortAddressMin = 1;
constexpr uint16_t shortAddressMax = 99;
constexpr uint16_t longAddressMin = 100;
constexpr uint16_t longAddressMax = 9999;
constexpr uint8_t idFeedbackBroadcast = 0x40;
struct Message;
uint8_t calcChecksum(const Message& msg);
void updateChecksum(Message& msg);
bool isChecksumValid(const Message& msg);
std::string toString(const Message& message, bool raw = false);
struct Message
{
uint8_t header;
Message()
{
}
Message(uint8_t _header) :
header{_header}
{
}
constexpr uint8_t identification() const
{
return header & 0xF0;
}
constexpr uint8_t dataSize() const
{
return header & 0x0F;
}
constexpr uint8_t size() const
{
return 2 + dataSize();
}
};
static_assert(sizeof(Message) == 1);
struct NormalOperationResumed : Message
{
uint8_t db1 = 0x01;
uint8_t checksum = 0x60;
NormalOperationResumed()
{
header = 0x61;
}
};
static_assert(sizeof(NormalOperationResumed) == 3);
struct TrackPowerOff : Message
{
uint8_t db1 = 0x00;
uint8_t checksum = 0x61;
TrackPowerOff()
{
header = 0x61;
}
};
static_assert(sizeof(TrackPowerOff) == 3);
struct EmergencyStop : Message
{
uint8_t db1 = 0x00;
uint8_t checksum = 0x81;
EmergencyStop()
{
header = 0x81;
}
};
static_assert(sizeof(EmergencyStop) == 3);
struct FeedbackBroadcast : Message
{
struct Pair
{
enum class Type
{
AccessoryDecoderWithoutFeedback = 0,
AccessoryDecoderWithFeedback = 1,
FeedbackModule = 2,
ReservedForFutureUse = 3,
};
uint8_t address;
uint8_t data;
constexpr uint16_t groupAddress() const
{
return (static_cast<uint16_t>(address) << 1) | ((data & 0x10) ? 1 : 0);
}
void setGroupAddress(uint16_t value)
{
assert(value < 512);
address = value >> 1;
if(value & 0x0001)
data |= 0x10;
else
data &= 0xEF;
}
constexpr bool switchingCommandCompleted() const
{
return (data & 0x80);
}
constexpr Type type() const
{
return static_cast<Type>((data & 0x60) >> 5);
}
void setType(Type value)
{
assert(
value == Type::AccessoryDecoderWithoutFeedback ||
value == Type::AccessoryDecoderWithFeedback ||
value == Type::FeedbackModule ||
value == Type::ReservedForFutureUse);
data = (data & 0x9F) | (static_cast<uint8_t>(value) << 5);
}
constexpr uint8_t statusNibble() const
{
return (data & 0x0F);
}
void setStatus(uint8_t index, bool value)
{
assert(index < 4);
if(value)
data |= (1 << index);
else
data &= ~static_cast<uint8_t>(1 << index);
}
};
static_assert(sizeof(Pair) == 2);
constexpr uint8_t pairCount() const
{
return dataSize() / sizeof(Pair);
}
void setPairCount(uint8_t value)
{
assert(value <= 7);
header = (header & 0xF0) | (value * 2);
}
const Pair& pair(uint8_t index) const
{
assert(index < pairCount());
return *(reinterpret_cast<const Pair*>(&header + sizeof(header)) + index);
}
Pair& pair(uint8_t index)
{
assert(index < pairCount());
return *(reinterpret_cast<Pair*>(&header + sizeof(header)) + index);
}
};
struct ResumeOperationsRequest : Message
{
uint8_t db1 = 0x81;
uint8_t checksum = 0xA0;
ResumeOperationsRequest()
{
header = 0x21;
}
};
static_assert(sizeof(ResumeOperationsRequest) == 3);
struct StopOperationsRequest : Message
{
uint8_t db1 = 0x80;
uint8_t checksum = 0xA1;
StopOperationsRequest()
{
header = 0x21;
}
};
static_assert(sizeof(StopOperationsRequest) == 3);
struct StopAllLocomotivesRequest : Message
{
uint8_t checksum = 0x80;
StopAllLocomotivesRequest()
{
header = 0x80;
}
};
static_assert(sizeof(StopAllLocomotivesRequest) == 2);
struct EmergencyStopLocomotive : Message
{
uint8_t addressHigh;
uint8_t addressLow;
uint8_t checksum;
EmergencyStopLocomotive(uint16_t address)
{
header = 0x92;
if(address >= longAddressMin)
{
assert(address >= longAddressMin && address <= longAddressMax);
addressHigh = 0xC0 | address >> 8;
addressLow = address & 0xff;
}
else
{
assert(address >= shortAddressMin && address <= shortAddressMax);
addressHigh = 0x00;
addressLow = address & 0x7f;
}
}
};
static_assert(sizeof(EmergencyStopLocomotive) == 4);
struct LocomotiveInstruction : Message
{
uint8_t identification;
uint8_t addressHigh;
uint8_t addressLow;
LocomotiveInstruction(uint16_t address)
{
header = 0xE4;
if(address >= longAddressMin)
{
assert(address >= longAddressMin && address <= longAddressMax);
addressHigh = 0xc0 | address >> 8;
addressLow = address & 0xff;
}
else
{
assert(address >= shortAddressMin && address <= shortAddressMax);
addressHigh = 0x00;
addressLow = address & 0x7f;
}
}
};
static_assert(sizeof(LocomotiveInstruction) == 4);
struct SpeedAndDirectionInstruction : LocomotiveInstruction
{
uint8_t speedAndDirection;
uint8_t checksum;
SpeedAndDirectionInstruction(uint16_t address, bool emergencyStop, Direction direction) :
LocomotiveInstruction(address)
{
assert(direction != Direction::Unknown);
speedAndDirection = emergencyStop ? 0x01 : 0x00;
if(direction == Direction::Forward)
speedAndDirection |= 0x80;
}
};
struct SpeedAndDirectionInstruction14 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction14(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep, bool fl) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 14);
identification = 0x10;
if(!emergencyStop && speedStep > 0)
speedAndDirection |= speedStep + 1;
if(fl)
speedAndDirection |= 0x10;
checksum = calcChecksum(*this);
}
};
struct SpeedAndDirectionInstruction27 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction27(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 27);
identification = 0x11;
if(!emergencyStop && speedStep > 0)
speedAndDirection |= (((speedStep + 1) & 0x01) << 4) | ((speedStep + 1) >> 1);
checksum = calcChecksum(*this);
}
};
struct SpeedAndDirectionInstruction28 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction28(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 28);
identification = 0x12;
if(!emergencyStop && speedStep > 0)
speedAndDirection |= (((speedStep + 1) & 0x01) << 4) | ((speedStep + 1) >> 1);
checksum = calcChecksum(*this);
}
};
struct SpeedAndDirectionInstruction128 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction128(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 126);
identification = 0x13;
if(!emergencyStop && speedStep > 0)
speedAndDirection |= speedStep + 1;
checksum = calcChecksum(*this);
}
};
struct FunctionInstructionGroup : LocomotiveInstruction
{
uint8_t functions = 0x00;
uint8_t checksum;
FunctionInstructionGroup(uint16_t address, uint8_t group) :
LocomotiveInstruction(address)
{
assert(group >= 1 && group <= 5);
identification = (group == 5) ? 0x28 : (0x1F + group);
}
};
struct FunctionInstructionGroup1 : FunctionInstructionGroup
{
FunctionInstructionGroup1(uint16_t address, bool f0, bool f1, bool f2, bool f3, bool f4) :
FunctionInstructionGroup(address, 1)
{
if(f0)
functions |= 0x10;
if(f1)
functions |= 0x01;
if(f2)
functions |= 0x02;
if(f3)
functions |= 0x04;
if(f4)
functions |= 0x08;
checksum = calcChecksum(*this);
}
};
struct FunctionInstructionGroup2 : FunctionInstructionGroup
{
FunctionInstructionGroup2(uint16_t address, bool f5, bool f6, bool f7, bool f8) :
FunctionInstructionGroup(address, 2)
{
if(f5)
functions |= 0x01;
if(f6)
functions |= 0x02;
if(f7)
functions |= 0x04;
if(f8)
functions |= 0x08;
checksum = calcChecksum(*this);
}
};
struct FunctionInstructionGroup3 : FunctionInstructionGroup
{
FunctionInstructionGroup3(uint16_t address, bool f9, bool f10, bool f11, bool f12) :
FunctionInstructionGroup(address, 3)
{
if(f9)
functions |= 0x01;
if(f10)
functions |= 0x02;
if(f11)
functions |= 0x04;
if(f12)
functions |= 0x08;
checksum = calcChecksum(*this);
}
};
struct FunctionInstructionGroup4 : FunctionInstructionGroup
{
FunctionInstructionGroup4(uint16_t address, bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20) :
FunctionInstructionGroup(address, 4)
{
if(f13)
functions |= 0x01;
if(f14)
functions |= 0x02;
if(f15)
functions |= 0x04;
if(f16)
functions |= 0x08;
if(f17)
functions |= 0x10;
if(f18)
functions |= 0x20;
if(f19)
functions |= 0x40;
if(f20)
functions |= 0x80;
checksum = calcChecksum(*this);
}
};
struct FunctionInstructionGroup5 : FunctionInstructionGroup
{
FunctionInstructionGroup5(uint16_t address, bool f21, bool f22, bool f23, bool f24, bool f25, bool f26, bool f27, bool f28) :
FunctionInstructionGroup(address, 5)
{
if(f21)
functions |= 0x01;
if(f22)
functions |= 0x02;
if(f23)
functions |= 0x04;
if(f24)
functions |= 0x08;
if(f25)
functions |= 0x10;
if(f26)
functions |= 0x20;
if(f27)
functions |= 0x40;
if(f28)
functions |= 0x80;
checksum = calcChecksum(*this);
}
};
/*
struct setFunctionStateGroup : LocomotiveInstruction
{
uint8_t state = 0x00;
uint8_t checksum;
setFunctionStateGroup(uint8_t group, uint16_t address)
{
assert(group >= 1 && group <= 3);
header = 0xE4;
identification = 0x23 + group;
addressLowHigh(address, addressLow, addressHigh);
}
} __attribute__((packed));
*/
struct AccessoryDecoderOperationRequest : Message
{
uint8_t address = 0x00;
uint8_t data = 0x80;
uint8_t checksum;
AccessoryDecoderOperationRequest(uint16_t fullAddress, bool value)
: Message(0x52)
{
assert(fullAddress < 2048);
address = static_cast<uint8_t>(fullAddress >> 3);
data |= static_cast<uint8_t>(fullAddress & 0x07);
if(value)
data |= 0x40;
checksum = calcChecksum(*this);
}
};
static_assert(sizeof(AccessoryDecoderOperationRequest) == 4);
namespace RocoMultiMAUS
{
struct FunctionInstructionF13F20 : LocomotiveInstruction
{
uint8_t functions = 0x00;
uint8_t checksum;
FunctionInstructionF13F20(uint16_t address, bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20) :
LocomotiveInstruction(address)
{
identification = 0xF3;
if(f13)
functions |= 0x01;
if(f14)
functions |= 0x02;
if(f15)
functions |= 0x04;
if(f16)
functions |= 0x08;
if(f17)
functions |= 0x10;
if(f18)
functions |= 0x20;
if(f19)
functions |= 0x40;
if(f20)
functions |= 0x80;
checksum = calcChecksum(*this);
}
};
}
namespace RoSoftS88XpressNetLI
{
struct S88StartAddress : Message
{
static constexpr uint8_t startAddressMin = 0;
static constexpr uint8_t startAddressMax = 127;
static constexpr uint8_t startAddressDefault = 64;
static constexpr uint8_t startAddressGet = 0xFF;
uint8_t data1;
uint8_t startAddress;
uint8_t checksum;
S88StartAddress(uint8_t _startAddress = startAddressGet) :
Message(0xF2),
data1{0xF1},
startAddress{_startAddress}
{
assert((startAddress >= startAddressMin && startAddress <= startAddressMax) || startAddress == startAddressGet);
checksum = calcChecksum(*this);
}
};
struct S88ModuleCount : Message
{
static constexpr uint8_t moduleCountMin = 1;
static constexpr uint8_t moduleCountMax = 32;
static constexpr uint8_t moduleCountDefault = 2;
static constexpr uint8_t moduleCountGet = 0xFF;
uint8_t data1;
uint8_t moduleCount;
uint8_t checksum;
S88ModuleCount(uint8_t _moduleCount = moduleCountGet) :
Message(0xF2),
data1{0xF2},
moduleCount{_moduleCount}
{
assert((moduleCount >= moduleCountMin && moduleCount <= moduleCountMax) || moduleCount == moduleCountGet);
checksum = calcChecksum(*this);
}
};
}
}
inline bool operator ==(const XpressNet::Message& lhs, const XpressNet::Message& rhs)
{
return lhs.size() == rhs.size() && std::memcmp(&lhs, &rhs, lhs.size()) == 0;
}
#endif