1283 Zeilen
23 KiB
C++
1283 Zeilen
23 KiB
C++
/**
|
|
* server/src/hardware/protocol/loconet/messages.hpp
|
|
*
|
|
* This file is part of the traintastic source code.
|
|
*
|
|
* Copyright (C) 2019-2020 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.
|
|
*/
|
|
|
|
/**
|
|
* Portions Copyright (C) Digitrax Inc.
|
|
*
|
|
* LocoNet is a registered trademark of DigiTrax, Inc.
|
|
*/
|
|
|
|
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_MESSAGES_HPP
|
|
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_MESSAGES_HPP
|
|
|
|
#include <string>
|
|
#include <traintastic/enum/direction.hpp>
|
|
#include "opcode.hpp"
|
|
|
|
namespace LocoNet {
|
|
|
|
struct Message;
|
|
|
|
uint8_t calcChecksum(const Message& msmessageg);
|
|
void updateChecksum(Message& message);
|
|
bool isChecksumValid(const Message& message);
|
|
bool isValid(const Message& message);
|
|
std::string to_string(const Message& message, bool raw = false);
|
|
|
|
constexpr uint8_t SLOT_LOCO_MIN = 1;
|
|
constexpr uint8_t SLOT_LOCO_MAX = 119;
|
|
constexpr uint8_t SLOT_FAST_CLOCK = 123;
|
|
constexpr uint8_t SLOT_PROGRAMMING_TRACK = 124;
|
|
constexpr uint8_t SLOT_UNKNOWN = 255; //!< placeholder to indicate invalid slot
|
|
|
|
constexpr uint8_t SPEED_STOP = 0;
|
|
constexpr uint8_t SPEED_ESTOP = 1;
|
|
constexpr uint8_t SPEED_MAX = 127;
|
|
|
|
constexpr uint8_t SL_CONUP = 0x40;
|
|
constexpr uint8_t SL_BUSY = 0x20;
|
|
constexpr uint8_t SL_ACTIVE = 0x10;
|
|
constexpr uint8_t SL_CONDN = 0x08;
|
|
|
|
constexpr uint8_t SL_DIR = 0x20;
|
|
constexpr uint8_t SL_F0 = 0x10;
|
|
constexpr uint8_t SL_F4 = 0x08;
|
|
constexpr uint8_t SL_F3 = 0x04;
|
|
constexpr uint8_t SL_F2 = 0x02;
|
|
constexpr uint8_t SL_F1 = 0x01;
|
|
|
|
constexpr uint8_t SL_F5 = 0x01;
|
|
constexpr uint8_t SL_F6 = 0x02;
|
|
constexpr uint8_t SL_F7 = 0x04;
|
|
constexpr uint8_t SL_F8 = 0x08;
|
|
|
|
constexpr uint8_t SL_F9 = 0x01;
|
|
constexpr uint8_t SL_F10 = 0x02;
|
|
constexpr uint8_t SL_F11 = 0x04;
|
|
constexpr uint8_t SL_F12 = 0x08;
|
|
|
|
constexpr uint8_t SL_F13 = 0x01;
|
|
constexpr uint8_t SL_F14 = 0x02;
|
|
constexpr uint8_t SL_F15 = 0x04;
|
|
constexpr uint8_t SL_F16 = 0x08;
|
|
constexpr uint8_t SL_F17 = 0x10;
|
|
constexpr uint8_t SL_F18 = 0x20;
|
|
constexpr uint8_t SL_F19 = 0x40;
|
|
|
|
constexpr uint8_t SL_F20 = 0x20;
|
|
constexpr uint8_t SL_F28 = 0x40;
|
|
|
|
constexpr uint8_t SL_F21 = 0x01;
|
|
constexpr uint8_t SL_F22 = 0x02;
|
|
constexpr uint8_t SL_F23 = 0x04;
|
|
constexpr uint8_t SL_F24 = 0x08;
|
|
constexpr uint8_t SL_F25 = 0x10;
|
|
constexpr uint8_t SL_F26 = 0x20;
|
|
constexpr uint8_t SL_F27 = 0x40;
|
|
|
|
constexpr uint8_t MULTI_SENSE_TYPE_MASK = 0xE0;
|
|
constexpr uint8_t MULTI_SENSE_TYPE_TRANSPONDER_GONE = 0x00;
|
|
constexpr uint8_t MULTI_SENSE_TYPE_TRANSPONDER_PRESENT = 0x20;
|
|
constexpr uint8_t MULTI_SENSE_TRANSPONDER_ADDRESS_SHORT = 0xFD;
|
|
|
|
struct Message
|
|
{
|
|
OpCode opCode;
|
|
|
|
Message()
|
|
{
|
|
}
|
|
|
|
Message(OpCode _opCode) :
|
|
opCode{_opCode}
|
|
{
|
|
}
|
|
|
|
uint8_t size() const
|
|
{
|
|
switch(opCode & 0xE0)
|
|
{
|
|
case 0x80: // 1 0 0 F D C B A
|
|
return 2;
|
|
|
|
case 0xA0: // 1 0 1 F D C B A
|
|
return 4;
|
|
|
|
case 0xC0: // 1 1 0 F D C B A
|
|
return 6;
|
|
|
|
case 0xE0: // 1 1 1 F D C B A => length in next byte
|
|
return reinterpret_cast<const uint8_t*>(this)[1];
|
|
|
|
default:
|
|
return 0; // not an op opcode, bit 7 not 1
|
|
}
|
|
}
|
|
};
|
|
|
|
struct SlotMessage : Message
|
|
{
|
|
uint8_t slot;
|
|
|
|
SlotMessage(OpCode _opCode, uint8_t _slot) :
|
|
Message{_opCode},
|
|
slot{_slot}
|
|
{
|
|
}
|
|
};
|
|
|
|
struct Idle : Message
|
|
{
|
|
uint8_t checksum;
|
|
|
|
Idle() :
|
|
Message{OPC_IDLE},
|
|
checksum{0x7A}
|
|
{
|
|
}
|
|
};
|
|
static_assert(sizeof(Idle) == 2);
|
|
|
|
struct GlobalPowerOn : Message
|
|
{
|
|
uint8_t checksum;
|
|
|
|
GlobalPowerOn() :
|
|
Message{OPC_GPON},
|
|
checksum{0x7C}
|
|
{
|
|
}
|
|
};
|
|
static_assert(sizeof(GlobalPowerOn) == 2);
|
|
|
|
struct GlobalPowerOff : Message
|
|
{
|
|
uint8_t checksum;
|
|
|
|
GlobalPowerOff() :
|
|
Message{OPC_GPOFF},
|
|
checksum{0x7D}
|
|
{
|
|
}
|
|
};
|
|
static_assert(sizeof(GlobalPowerOff) == 2);
|
|
|
|
struct Busy : Message
|
|
{
|
|
uint8_t checksum;
|
|
|
|
Busy() :
|
|
Message{OPC_BUSY},
|
|
checksum{0x7E}
|
|
{
|
|
}
|
|
};
|
|
static_assert(sizeof(Busy) == 2);
|
|
|
|
|
|
|
|
struct LocoAdr : Message
|
|
{
|
|
uint8_t addressHigh;
|
|
uint8_t addressLow;
|
|
uint8_t checksum;
|
|
|
|
LocoAdr(uint16_t address) :
|
|
Message{OPC_LOCO_ADR},
|
|
addressHigh{static_cast<uint8_t>(address >> 7)},
|
|
addressLow{static_cast<uint8_t>(address & 0x7F)}
|
|
{
|
|
checksum = calcChecksum(*this);
|
|
}
|
|
};
|
|
static_assert(sizeof(LocoAdr) == 4);
|
|
|
|
struct LocoSpd : SlotMessage
|
|
{
|
|
uint8_t speed;
|
|
uint8_t checksum;
|
|
|
|
LocoSpd(uint8_t _speed) :
|
|
SlotMessage{OPC_LOCO_SPD, SLOT_UNKNOWN},
|
|
speed{_speed}
|
|
{
|
|
checksum = calcChecksum(*this);
|
|
}
|
|
};
|
|
static_assert(sizeof(LocoSpd) == 4);
|
|
|
|
struct LocoDirF : SlotMessage
|
|
{
|
|
uint8_t dirf;
|
|
uint8_t checksum;
|
|
|
|
LocoDirF(Direction direction, bool f0, bool f1, bool f2, bool f3, bool f4) :
|
|
SlotMessage{OPC_LOCO_DIRF, SLOT_UNKNOWN},
|
|
dirf{0}
|
|
{
|
|
if(direction == Direction::Forward)
|
|
dirf |= SL_DIR;
|
|
if(f0)
|
|
dirf |= SL_F0;
|
|
if(f1)
|
|
dirf |= SL_F1;
|
|
if(f2)
|
|
dirf |= SL_F2;
|
|
if(f3)
|
|
dirf |= SL_F3;
|
|
if(f4)
|
|
dirf |= SL_F4;
|
|
}
|
|
|
|
inline Direction direction() const
|
|
{
|
|
return (dirf & SL_DIR) ? Direction::Forward : Direction::Reverse;
|
|
}
|
|
|
|
inline void setDirection(Direction value)
|
|
{
|
|
if(value == Direction::Forward)
|
|
dirf |= SL_DIR;
|
|
else
|
|
dirf &= ~SL_DIR;
|
|
}
|
|
|
|
inline bool f0() const
|
|
{
|
|
return dirf & SL_F0;
|
|
}
|
|
|
|
inline void setF0(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F0;
|
|
else
|
|
dirf &= ~SL_F0;
|
|
}
|
|
|
|
inline bool f1() const
|
|
{
|
|
return dirf & SL_F1;
|
|
}
|
|
|
|
inline void setF1(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F1;
|
|
else
|
|
dirf &= ~SL_F1;
|
|
}
|
|
|
|
inline bool f2() const
|
|
{
|
|
return dirf & SL_F2;
|
|
}
|
|
|
|
inline void setF2(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F2;
|
|
else
|
|
dirf &= ~SL_F2;
|
|
}
|
|
|
|
inline bool f3() const
|
|
{
|
|
return dirf & SL_F3;
|
|
}
|
|
|
|
inline void setF3(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F3;
|
|
else
|
|
dirf &= ~SL_F3;
|
|
}
|
|
|
|
inline bool f4() const
|
|
{
|
|
return dirf & SL_F4;
|
|
}
|
|
|
|
inline void setF4(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F4;
|
|
else
|
|
dirf &= ~SL_F4;
|
|
}
|
|
};
|
|
static_assert(sizeof(LocoDirF) == 4);
|
|
|
|
struct LocoSnd : SlotMessage
|
|
{
|
|
uint8_t snd;
|
|
uint8_t checksum;
|
|
|
|
LocoSnd(bool f5, bool f6, bool f7, bool f8) :
|
|
SlotMessage{OPC_LOCO_SND, SLOT_UNKNOWN},
|
|
snd{0}
|
|
{
|
|
if(f5)
|
|
snd |= SL_F5;
|
|
if(f6)
|
|
snd |= SL_F6;
|
|
if(f7)
|
|
snd |= SL_F7;
|
|
if(f8)
|
|
snd |= SL_F8;
|
|
|
|
checksum = calcChecksum(*this);
|
|
}
|
|
|
|
inline bool f5() const
|
|
{
|
|
return snd & SL_F5;
|
|
}
|
|
|
|
inline void setF5(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F5;
|
|
else
|
|
snd &= ~SL_F5;
|
|
}
|
|
|
|
inline bool f6() const
|
|
{
|
|
return snd & SL_F6;
|
|
}
|
|
|
|
inline void setF6(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F6;
|
|
else
|
|
snd &= ~SL_F6;
|
|
}
|
|
|
|
inline bool f7() const
|
|
{
|
|
return snd & SL_F7;
|
|
}
|
|
|
|
inline void setF7(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F7;
|
|
else
|
|
snd &= ~SL_F7;
|
|
}
|
|
|
|
inline bool f8() const
|
|
{
|
|
return snd & SL_F8;
|
|
}
|
|
|
|
inline void setF8(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F8;
|
|
else
|
|
snd &= ~SL_F8;
|
|
}
|
|
};
|
|
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);
|
|
|
|
/*
|
|
|
|
2020-02-04 21:27:59.123954 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 11 70 2c
|
|
2020-02-04 21:27:59.413558 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 19 40 14
|
|
2020-02-04 21:28:00.046282 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 11 50 0c
|
|
2020-02-04 21:28:00.433662 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 11 60 3c
|
|
2020-02-04 21:28:02.502012 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 09 70 34
|
|
2020-02-04 21:28:02.913595 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 11 40 1c
|
|
2020-02-04 21:28:03.629638 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 09 50 14
|
|
2020-02-04 21:28:03.937476 [debug] cs1: unknown message: dataLen=0x0008, header=0x00a0, data=b2 09 60 24
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct InputRep : Message
|
|
{
|
|
uint8_t in1;
|
|
uint8_t in2;
|
|
uint8_t checksum;
|
|
|
|
InputRep() :
|
|
Message{OPC_INPUT_REP}
|
|
{
|
|
}
|
|
|
|
inline uint16_t address() const
|
|
{
|
|
return (in1 & 0x7F) | (static_cast<uint16_t>(in2 & 0x0F) << 7);
|
|
}
|
|
|
|
inline bool isSwitchInput() const
|
|
{
|
|
return in2 & 0x20;
|
|
}
|
|
|
|
inline bool isAuxInput() const
|
|
{
|
|
return !isSwitchInput();
|
|
}
|
|
|
|
inline bool value() const
|
|
{
|
|
return in2 & 0x10;
|
|
}
|
|
};
|
|
static_assert(sizeof(InputRep) == 4);
|
|
|
|
|
|
|
|
struct RequestSlotData : Message
|
|
{
|
|
uint8_t slot;
|
|
uint8_t data2;
|
|
uint8_t checksum;
|
|
|
|
RequestSlotData(uint8_t _slot) :
|
|
Message(OPC_RQ_SL_DATA),
|
|
slot{_slot},
|
|
data2{0}
|
|
{
|
|
checksum = calcChecksum(*this);
|
|
}
|
|
};
|
|
static_assert(sizeof(RequestSlotData) == 4);
|
|
|
|
|
|
// d0 00 42 20 13 5e
|
|
|
|
struct MultiSense : Message
|
|
{
|
|
uint8_t data1;
|
|
uint8_t data2;
|
|
uint8_t data3;
|
|
uint8_t data4;
|
|
uint8_t checksum;
|
|
|
|
MultiSense() :
|
|
Message(OPC_MULTI_SENSE)
|
|
{
|
|
}
|
|
|
|
bool isTransponder() const
|
|
{
|
|
return
|
|
((data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_GONE) ||
|
|
((data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_PRESENT);
|
|
}
|
|
};
|
|
static_assert(sizeof(MultiSense) == 6);
|
|
|
|
struct LocoF13F19 : Message
|
|
{
|
|
uint8_t data1;
|
|
uint8_t slot;
|
|
uint8_t data3;
|
|
uint8_t function;
|
|
uint8_t checksum;
|
|
|
|
LocoF13F19(bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19) :
|
|
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;
|
|
}
|
|
|
|
inline bool f13() const
|
|
{
|
|
return function & SL_F13;
|
|
}
|
|
|
|
inline void setF13(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F13;
|
|
else
|
|
function &= ~SL_F13;
|
|
}
|
|
|
|
inline bool f14() const
|
|
{
|
|
return function & SL_F14;
|
|
}
|
|
|
|
inline void setF14(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F14;
|
|
else
|
|
function &= ~SL_F14;
|
|
}
|
|
|
|
inline bool f15() const
|
|
{
|
|
return function & SL_F15;
|
|
}
|
|
|
|
inline void setF15(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F15;
|
|
else
|
|
function &= ~SL_F15;
|
|
}
|
|
|
|
inline bool f16() const
|
|
{
|
|
return function & SL_F16;
|
|
}
|
|
|
|
inline void setF16(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F16;
|
|
else
|
|
function &= ~SL_F16;
|
|
}
|
|
|
|
inline bool f17() const
|
|
{
|
|
return function & SL_F17;
|
|
}
|
|
|
|
inline void setF17(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F17;
|
|
else
|
|
function &= ~SL_F17;
|
|
}
|
|
|
|
inline bool f18() const
|
|
{
|
|
return function & SL_F18;
|
|
}
|
|
|
|
inline void setF18(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F18;
|
|
else
|
|
function &= ~SL_F18;
|
|
}
|
|
|
|
inline bool f19() const
|
|
{
|
|
return function & SL_F19;
|
|
}
|
|
|
|
inline void setF19(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F19;
|
|
else
|
|
function &= ~SL_F19;
|
|
}
|
|
};
|
|
static_assert(sizeof(LocoF13F19) == 6);
|
|
|
|
struct LocoF20F28 : Message
|
|
{
|
|
uint8_t data1;
|
|
uint8_t slot;
|
|
uint8_t data3;
|
|
uint8_t function;
|
|
uint8_t checksum;
|
|
|
|
LocoF20F28(bool f20, bool f28) :
|
|
Message(OPC_D4),
|
|
data1{0x20},
|
|
slot{SLOT_UNKNOWN},
|
|
data3{0x05},
|
|
function{0}
|
|
{
|
|
if(f20)
|
|
function |= SL_F20;
|
|
if(f28)
|
|
function |= SL_F28;
|
|
}
|
|
|
|
inline bool f20() const
|
|
{
|
|
return function & SL_F20;
|
|
}
|
|
|
|
inline void setF20(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F20;
|
|
else
|
|
function &= ~SL_F20;
|
|
}
|
|
inline bool f28() const
|
|
{
|
|
return function & SL_F28;
|
|
}
|
|
|
|
inline void setF28(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F28;
|
|
else
|
|
function &= ~SL_F28;
|
|
}
|
|
};
|
|
static_assert(sizeof(LocoF20F28) == 6);
|
|
|
|
struct LocoF21F27 : Message
|
|
{
|
|
uint8_t data1;
|
|
uint8_t slot;
|
|
uint8_t data3;
|
|
uint8_t function;
|
|
uint8_t checksum;
|
|
|
|
LocoF21F27(bool f21, bool f22, bool f23, bool f24, bool f25, bool f26, bool f27) :
|
|
Message(OPC_D4),
|
|
data1{0x20},
|
|
slot{SLOT_UNKNOWN},
|
|
data3{0x09},
|
|
function{0}
|
|
{
|
|
if(f21)
|
|
function |= SL_F21;
|
|
if(f22)
|
|
function |= SL_F22;
|
|
if(f23)
|
|
function |= SL_F23;
|
|
if(f24)
|
|
function |= SL_F24;
|
|
if(f25)
|
|
function |= SL_F25;
|
|
if(f26)
|
|
function |= SL_F26;
|
|
if(f27)
|
|
function |= SL_F27;
|
|
}
|
|
|
|
inline bool f21() const
|
|
{
|
|
return function & SL_F21;
|
|
}
|
|
|
|
inline void setF21(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F21;
|
|
else
|
|
function &= ~SL_F21;
|
|
}
|
|
|
|
inline bool f22() const
|
|
{
|
|
return function & SL_F22;
|
|
}
|
|
|
|
inline void setF22(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F22;
|
|
else
|
|
function &= ~SL_F22;
|
|
}
|
|
|
|
inline bool f23() const
|
|
{
|
|
return function & SL_F23;
|
|
}
|
|
|
|
inline void setF23(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F23;
|
|
else
|
|
function &= ~SL_F23;
|
|
}
|
|
|
|
inline bool f24() const
|
|
{
|
|
return function & SL_F24;
|
|
}
|
|
|
|
inline void setF24(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F24;
|
|
else
|
|
function &= ~SL_F24;
|
|
}
|
|
|
|
inline bool f25() const
|
|
{
|
|
return function & SL_F25;
|
|
}
|
|
|
|
inline void setF25(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F25;
|
|
else
|
|
function &= ~SL_F25;
|
|
}
|
|
|
|
inline bool f26() const
|
|
{
|
|
return function & SL_F26;
|
|
}
|
|
|
|
inline void setF26(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F26;
|
|
else
|
|
function &= ~SL_F26;
|
|
}
|
|
|
|
inline bool f27() const
|
|
{
|
|
return function & SL_F27;
|
|
}
|
|
|
|
inline void setF27(bool value)
|
|
{
|
|
if(value)
|
|
function |= SL_F27;
|
|
else
|
|
function &= ~SL_F27;
|
|
}
|
|
};
|
|
static_assert(sizeof(LocoF21F27) == 6);
|
|
|
|
struct MultiSenseTransponder : MultiSense
|
|
{
|
|
bool isPresent() const
|
|
{
|
|
return (data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_PRESENT;
|
|
}
|
|
|
|
uint16_t sensorAddress() const
|
|
{
|
|
return (static_cast<uint16_t>(data1 & 0x1F) << 7) | (data2 & 0x7F);
|
|
}
|
|
|
|
uint16_t transponderAddress() const
|
|
{
|
|
if(isTransponderAddressLong())
|
|
return (static_cast<uint16_t>(data3 & 0x7F) << 7) | (data4 & 0x7F);
|
|
else
|
|
return (data4 & 0x7F);
|
|
}
|
|
|
|
bool isTransponderAddressLong() const
|
|
{
|
|
return data3 != MULTI_SENSE_TRANSPONDER_ADDRESS_SHORT;
|
|
}
|
|
};
|
|
static_assert(sizeof(MultiSenseTransponder) == 6);
|
|
|
|
struct MultiSenseLong : Message
|
|
{
|
|
uint8_t len;
|
|
uint8_t data1;
|
|
uint8_t data2;
|
|
uint8_t data3;
|
|
uint8_t data4;
|
|
uint8_t data5;
|
|
uint8_t data6;
|
|
uint8_t checksum;
|
|
|
|
MultiSenseLong() :
|
|
Message(OPC_MULTI_SENSE_LONG),
|
|
len{9}
|
|
{
|
|
}
|
|
|
|
bool isTransponder() const
|
|
{
|
|
return
|
|
((data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_GONE) ||
|
|
((data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_PRESENT);
|
|
}
|
|
};
|
|
static_assert(sizeof(MultiSenseLong) == 9);
|
|
|
|
struct MultiSenseLongTransponder : MultiSenseLong
|
|
{
|
|
bool isPresent() const
|
|
{
|
|
return (data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_PRESENT;
|
|
}
|
|
|
|
uint16_t sensorAddress() const
|
|
{
|
|
return (static_cast<uint16_t>(data1 & 0x1F) << 7) | (data2 & 0x7F);
|
|
}
|
|
|
|
uint16_t transponderAddress() const
|
|
{
|
|
if(isTransponderAddressLong())
|
|
return (static_cast<uint16_t>(data3 & 0x7F) << 7) | (data4 & 0x7F);
|
|
else
|
|
return (data4 & 0x7F);
|
|
}
|
|
|
|
bool isTransponderAddressLong() const
|
|
{
|
|
return data3 != MULTI_SENSE_TRANSPONDER_ADDRESS_SHORT;
|
|
}
|
|
|
|
Direction transponderDirection() const
|
|
{
|
|
return (data5 & 0x40) ? Direction::Forward : Direction::Reverse;
|
|
}
|
|
};
|
|
static_assert(sizeof(MultiSenseLongTransponder) == 9);
|
|
|
|
// OPC_SL_RD_DATA [E7 0E 1F 13 6F 01 30 07 08 19 00 00 00 52]
|
|
struct SlotReadData : Message
|
|
{
|
|
uint8_t len;
|
|
uint8_t slot;
|
|
uint8_t stat;
|
|
uint8_t adr;
|
|
uint8_t spd;
|
|
uint8_t dirf;
|
|
uint8_t trk;
|
|
uint8_t ss2;
|
|
uint8_t adr2;
|
|
uint8_t snd;
|
|
uint8_t id1;
|
|
uint8_t id2;
|
|
uint8_t checksum;
|
|
|
|
SlotReadData() :
|
|
Message(OPC_SL_RD_DATA),
|
|
len{14}
|
|
{
|
|
}
|
|
|
|
bool isBusy() const
|
|
{
|
|
return stat & SL_BUSY;
|
|
}
|
|
|
|
bool isActive() const
|
|
{
|
|
return stat & SL_ACTIVE;
|
|
}
|
|
|
|
uint16_t address() const
|
|
{
|
|
return (static_cast<uint16_t>(adr2) << 7) | adr;
|
|
}
|
|
|
|
bool isEmergencyStop() const
|
|
{
|
|
return spd == 0x01;
|
|
}
|
|
|
|
uint8_t speed() const
|
|
{
|
|
return spd > 1 ? spd - 1 : 0;
|
|
}
|
|
|
|
inline Direction direction() const
|
|
{
|
|
return (dirf & SL_DIR) ? Direction::Forward : Direction::Reverse;
|
|
}
|
|
|
|
inline void setDirection(Direction value)
|
|
{
|
|
if(value == Direction::Forward)
|
|
dirf |= SL_DIR;
|
|
else
|
|
dirf &= ~SL_DIR;
|
|
}
|
|
|
|
inline bool f0() const
|
|
{
|
|
return dirf & SL_F0;
|
|
}
|
|
|
|
inline void setF0(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F0;
|
|
else
|
|
dirf &= ~SL_F0;
|
|
}
|
|
|
|
inline bool f1() const
|
|
{
|
|
return dirf & SL_F1;
|
|
}
|
|
|
|
inline void setF1(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F1;
|
|
else
|
|
dirf &= ~SL_F1;
|
|
}
|
|
|
|
inline bool f2() const
|
|
{
|
|
return dirf & SL_F2;
|
|
}
|
|
|
|
inline void setF2(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F2;
|
|
else
|
|
dirf &= ~SL_F2;
|
|
}
|
|
|
|
inline bool f3() const
|
|
{
|
|
return dirf & SL_F3;
|
|
}
|
|
|
|
inline void setF3(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F3;
|
|
else
|
|
dirf &= ~SL_F3;
|
|
}
|
|
|
|
inline bool f4() const
|
|
{
|
|
return dirf & SL_F4;
|
|
}
|
|
|
|
inline void setF4(bool value)
|
|
{
|
|
if(value)
|
|
dirf |= SL_F4;
|
|
else
|
|
dirf &= ~SL_F4;
|
|
}
|
|
|
|
inline bool f5() const
|
|
{
|
|
return snd & SL_F5;
|
|
}
|
|
|
|
inline void setF5(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F5;
|
|
else
|
|
snd &= ~SL_F5;
|
|
}
|
|
|
|
inline bool f6() const
|
|
{
|
|
return snd & SL_F6;
|
|
}
|
|
|
|
inline void setF6(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F6;
|
|
else
|
|
snd &= ~SL_F6;
|
|
}
|
|
|
|
inline bool f7() const
|
|
{
|
|
return snd & SL_F7;
|
|
}
|
|
|
|
inline void setF7(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F7;
|
|
else
|
|
snd &= ~SL_F7;
|
|
}
|
|
|
|
inline bool f8() const
|
|
{
|
|
return snd & SL_F8;
|
|
}
|
|
|
|
inline void setF8(bool value)
|
|
{
|
|
if(value)
|
|
snd |= SL_F8;
|
|
else
|
|
snd &= ~SL_F8;
|
|
}
|
|
};
|
|
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));
|
|
*/
|
|
|
|
}
|
|
|
|
#endif
|