Added Z21 protocol stuff
Dieser Commit ist enthalten in:
Ursprung
e3e139678e
Commit
6a7aac1634
215
server/src/hardware/commandstation/protocol/z21.hpp
Normale Datei
215
server/src/hardware/commandstation/protocol/z21.hpp
Normale Datei
@ -0,0 +1,215 @@
|
||||
/**
|
||||
* server/src/hardware/commandstation/protocol/z21.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019 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 SERVER_HARDWARE_COMMANDSTATION_PROTOCOL_Z21_HPP
|
||||
#define SERVER_HARDWARE_COMMANDSTATION_PROTOCOL_Z21_HPP
|
||||
|
||||
#define Z21_LAN_GET_SERIAL_NUMBER 0x10
|
||||
#define Z21_LAN_GET_HWINFO 0x1A
|
||||
#define Z21_LAN_LOGOFF 0x30
|
||||
#define Z21_LAN_X 0x40
|
||||
#define Z21_LAN_X_SET_STOP 0x80
|
||||
#define Z21_LAN_X_TURNOUT_INFO 0x43
|
||||
#define Z21_LAN_X_BC 0x61
|
||||
#define Z21_LAN_X_BC_TRACK_POWER_OFF 0x00
|
||||
#define Z21_LAN_X_BC_TRACK_POWER_ON 0x01
|
||||
#define Z21_LAN_X_BC_PROGRAMMING_MODE 0x02
|
||||
#define Z21_LAN_X_BC_TRACK_SHORT_CIRCUIT 0x08
|
||||
#define Z21_LAN_X_CV_NACK_SC 0x12
|
||||
#define Z21_LAN_X_CV_NACK 0x13
|
||||
#define Z21_LAN_X_UNKNOWN_COMMAND 0x82
|
||||
#define Z21_LAN_X_BC_STOPPED 0x81
|
||||
#define Z21_LAN_X_LOCO_INFO 0xEF
|
||||
#define Z21_LAN_SYSTEMSTATE_DATACHANGED 0x84
|
||||
#define Z21_LAN_SYSTEMSTATE_GETDATA 0x85
|
||||
|
||||
#define Z21_HWT_Z21_OLD 0x00000200 //!< „black Z21” (hardware variant from 2012)
|
||||
#define Z21_HWT_Z21_NEW 0x00000201 //!< „black Z21”(hardware variant from 2013)
|
||||
#define Z21_HWT_SMARTRAIL 0x00000202 //!< SmartRail (from 2012)
|
||||
#define Z21_HWT_z21_SMALL 0x00000203 //!< „white z21” starter set variant (from 2013)
|
||||
#define Z21_HWT_z21_START 0x00000204 //!< „z21 start” starter set variant (from 2016)
|
||||
|
||||
#define Z21_CENTRALSTATE_EMERGENCYSTOP 0x01 //!< The emergency stop is switched on
|
||||
#define Z21_CENTRALSTATE_TRACKVOLTAGEOFF 0x02 //!< The track voltage is switched off
|
||||
#define Z21_CENTRALSTATE_SHORTCIRCUIT 0x04 //!< Short circuit
|
||||
#define Z21_CENTRALSTATE_PROGRAMMINGMODEACTIVE 0x20 //!< The programming mode is active
|
||||
|
||||
#define Z21_CENTRALSTATEEX_HIGHTEMPERATURE 0x01 //!< Temperature too high
|
||||
#define Z21_CENTRALSTATEEX_POWERLOST 0x02 //!< Input voltage too low
|
||||
#define Z21_CENTRALSTATEEX_SHORTCIRCUITEXTERNAL 0x04 //!< Short circuit at the external booster output
|
||||
#define Z21_CENTRALSTATEEX_SHORTCIRCUITINTERNAL 0x08 //!< Short circuit at the main track or programming track
|
||||
|
||||
struct z21_lan_header
|
||||
{
|
||||
uint16_t dataLen; // LE
|
||||
uint16_t header; // LE
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_header) == 0x04);
|
||||
|
||||
struct z21_lan_get_serial_number : z21_lan_header
|
||||
{
|
||||
z21_lan_get_serial_number()
|
||||
{
|
||||
dataLen = sizeof(z21_lan_get_serial_number);
|
||||
header = Z21_LAN_GET_SERIAL_NUMBER;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_get_serial_number) == 0x04);
|
||||
|
||||
struct z21_lan_get_serial_number_reply : z21_lan_get_serial_number
|
||||
{
|
||||
uint32_t serialNumber; // LE
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_get_serial_number_reply) == 0x08);
|
||||
|
||||
struct z21_lan_get_hwinfo : z21_lan_header
|
||||
{
|
||||
z21_lan_get_hwinfo()
|
||||
{
|
||||
dataLen = sizeof(z21_lan_get_hwinfo);
|
||||
header = Z21_LAN_GET_HWINFO;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_get_hwinfo) == 0x04);
|
||||
|
||||
struct z21_lan_get_hwinfo_reply : z21_lan_get_hwinfo
|
||||
{
|
||||
uint32_t hardwareType; // LE
|
||||
uint32_t firmwareVersion; // LE
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_get_hwinfo_reply) == 0x0C);
|
||||
|
||||
struct z21_lan_logoff : z21_lan_header
|
||||
{
|
||||
z21_lan_logoff()
|
||||
{
|
||||
dataLen = sizeof(z21_lan_logoff);
|
||||
header = Z21_LAN_LOGOFF;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_logoff) == 0x04);
|
||||
|
||||
struct z21_lan_x : z21_lan_header
|
||||
{
|
||||
uint8_t xheader;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_x) == 0x05);
|
||||
|
||||
struct z21_lan_x_set_stop : z21_lan_x
|
||||
{
|
||||
uint8_t checksum = 0x80;
|
||||
|
||||
z21_lan_x_set_stop()
|
||||
{
|
||||
dataLen = sizeof(z21_lan_x_set_stop);
|
||||
header = Z21_LAN_X;
|
||||
xheader = Z21_LAN_X_SET_STOP;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_x_set_stop) == 0x06);
|
||||
|
||||
struct z21_lan_x_set_track_power_off : z21_lan_x
|
||||
{
|
||||
uint8_t db0 = 0x80;
|
||||
uint8_t checksum = 0xa1;
|
||||
|
||||
z21_lan_x_set_track_power_off()
|
||||
{
|
||||
dataLen = sizeof(z21_lan_x_set_track_power_off);
|
||||
header = Z21_LAN_X;
|
||||
xheader = 0x21;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
struct z21_lan_x_set_track_power_on : z21_lan_x
|
||||
{
|
||||
uint8_t db0 = 0x81;
|
||||
uint8_t checksum = 0xa0;
|
||||
|
||||
z21_lan_x_set_track_power_on()
|
||||
{
|
||||
dataLen = sizeof(z21_lan_x_set_track_power_off);
|
||||
header = Z21_LAN_X;
|
||||
xheader = 0x21;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
struct z21_lan_x_loco_info : z21_lan_x
|
||||
{
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
uint8_t db2;
|
||||
uint8_t speedAndDirection;
|
||||
uint8_t db4;
|
||||
uint8_t f5f12;
|
||||
uint8_t f13f20;
|
||||
uint8_t f21f28;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct z21_lan_x_set_loco_drive : z21_lan_header
|
||||
{
|
||||
uint8_t xheader = 0xe4;
|
||||
uint8_t db0;
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
uint8_t speedAndDirection = 0;
|
||||
uint8_t checksum;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_x_set_loco_drive) == 0x0a);
|
||||
|
||||
struct z21_lan_x_set_loco_function : z21_lan_header
|
||||
{
|
||||
uint8_t xheader = 0xe4;
|
||||
uint8_t db0 = 0xf8;
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
uint8_t db3;
|
||||
uint8_t checksum;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_x_set_loco_function) == 0x0a);
|
||||
|
||||
struct z21_lan_systemstate_getdata : z21_lan_header
|
||||
{
|
||||
z21_lan_systemstate_getdata()
|
||||
{
|
||||
dataLen = sizeof(z21_lan_systemstate_getdata);
|
||||
header = Z21_LAN_SYSTEMSTATE_GETDATA;
|
||||
}
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_systemstate_getdata) == 0x04);
|
||||
|
||||
struct z21_lan_systemstate_datachanged : z21_lan_header
|
||||
{
|
||||
int16_t mainCurrent; //!< Current on the main track in mA
|
||||
int16_t progCurrent; //!< Current on programming track in mA;
|
||||
int16_t filteredMainCurrent; //!< Smoothed current on the main track in mA
|
||||
int16_t temperature; //!< Command station internal temperature in °C
|
||||
uint16_t SupplyVoltage; //!< Supply voltage in mV
|
||||
uint16_t vccVoltage; //!< Internal voltage, identical to track voltage in mV
|
||||
uint8_t centralState; //!< bitmask, see Z21_CENTRALSTATE
|
||||
uint8_t centralStateEx; //!< bitmask, see Z21_CENTRALSTATEEX
|
||||
uint8_t _reserved1;
|
||||
uint8_t _reserved2;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_systemstate_datachanged) == 0x14);
|
||||
|
||||
#endif
|
||||
@ -26,6 +26,7 @@
|
||||
#include "../../core/eventloop.hpp"
|
||||
#include "../decoder/decoderchangeflags.hpp"
|
||||
#include "protocol/xpressnet.hpp"
|
||||
#include "protocol/z21.hpp"
|
||||
#include "../../utils/to_hex.hpp"
|
||||
|
||||
|
||||
@ -38,8 +39,7 @@
|
||||
|
||||
namespace Hardware::CommandStation {
|
||||
|
||||
#define Z21_HEADER_X 0x40
|
||||
#define Z21_LAN_X_LOCO_INFO 0xEF
|
||||
|
||||
|
||||
#define SET_ADDRESS \
|
||||
if(decoder.longAddress) \
|
||||
@ -53,50 +53,7 @@ namespace Hardware::CommandStation {
|
||||
cmd.addressLow = decoder.address; \
|
||||
}
|
||||
|
||||
struct z21_lan_header
|
||||
{
|
||||
uint16_t dataLen; // LE
|
||||
uint16_t header; // LE
|
||||
} __attribute__((packed));
|
||||
|
||||
struct z21_lan_x : z21_lan_header
|
||||
{
|
||||
uint8_t xheader;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct z21_lan_x_loco_info : z21_lan_x
|
||||
{
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
uint8_t db2;
|
||||
uint8_t speedAndDirection;
|
||||
uint8_t db4;
|
||||
uint8_t f5f12;
|
||||
uint8_t f13f20;
|
||||
uint8_t f21f28;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct z21_lan_x_set_loco_drive : z21_lan_header
|
||||
{
|
||||
uint8_t xheader = 0xe4;
|
||||
uint8_t db0;
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
uint8_t speedAndDirection = 0;
|
||||
uint8_t checksum;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_x_set_loco_drive) == 0x0a);
|
||||
|
||||
struct z21_lan_x_set_loco_function : z21_lan_header
|
||||
{
|
||||
uint8_t xheader = 0xe4;
|
||||
uint8_t db0 = 0xf8;
|
||||
uint8_t addressHigh;
|
||||
uint8_t addressLow;
|
||||
uint8_t db3;
|
||||
uint8_t checksum;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(z21_lan_x_set_loco_function) == 0x0a);
|
||||
|
||||
|
||||
|
||||
@ -105,12 +62,34 @@ Z21::Z21(const std::weak_ptr<World>& world, const std::string& _id) :
|
||||
CommandStation(world, _id),
|
||||
m_socket{Traintastic::instance->ioContext()},
|
||||
hostname{this, "hostname", "", PropertyFlags::AccessWCC},
|
||||
port{this, "port", 21105, PropertyFlags::AccessWCC}
|
||||
port{this, "port", 21105, PropertyFlags::AccessWCC},
|
||||
serialNumber{this, "serial_number", 0, PropertyFlags::AccessRRR},
|
||||
hardwareType{this, "hardware_type", "", PropertyFlags::AccessRRR},
|
||||
firmwareVersion{this, "firmware_version", "", PropertyFlags::AccessRRR},
|
||||
emergencyStop{this, "emergency_stop", false, PropertyFlags::TODO,
|
||||
[this](bool value)
|
||||
{
|
||||
if(value)
|
||||
send(z21_lan_x_set_stop());
|
||||
}},
|
||||
trackVoltageOff{this, "track_voltage_off", false, PropertyFlags::TODO,
|
||||
[this](bool value)
|
||||
{
|
||||
if(value)
|
||||
send(z21_lan_x_set_track_power_off());
|
||||
else
|
||||
send(z21_lan_x_set_track_power_on());
|
||||
}}
|
||||
{
|
||||
name = "Z21";
|
||||
|
||||
m_interfaceItems.add(hostname);
|
||||
m_interfaceItems.add(port);
|
||||
m_interfaceItems.add(serialNumber);
|
||||
m_interfaceItems.add(hardwareType);
|
||||
m_interfaceItems.add(firmwareVersion);
|
||||
m_interfaceItems.add(emergencyStop);
|
||||
m_interfaceItems.add(trackVoltageOff);
|
||||
}
|
||||
|
||||
bool Z21::isDecoderSupported(Decoder& decoder) const
|
||||
@ -129,7 +108,7 @@ void Z21::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uin
|
||||
{
|
||||
z21_lan_x_set_loco_drive cmd;
|
||||
cmd.dataLen = sizeof(cmd);
|
||||
cmd.header = Z21_HEADER_X;
|
||||
cmd.header = Z21_LAN_X;
|
||||
SET_ADDRESS;
|
||||
|
||||
assert(decoder.speedStep <= decoder.speedSteps);
|
||||
@ -180,7 +159,7 @@ void Z21::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uin
|
||||
{
|
||||
z21_lan_x_set_loco_function cmd;
|
||||
cmd.dataLen = sizeof(cmd);
|
||||
cmd.header = Z21_HEADER_X;
|
||||
cmd.header = Z21_LAN_X;
|
||||
SET_ADDRESS;
|
||||
cmd.db3 = (f->value ? 0x40 : 0x00) | static_cast<uint8_t>(functionNumber);
|
||||
cmd.checksum = XpressNet::calcChecksum(&cmd.xheader);
|
||||
@ -216,6 +195,9 @@ bool Z21::setOnline(bool& value)
|
||||
return false;
|
||||
}
|
||||
// try to communicate with Z21
|
||||
send(z21_lan_get_serial_number());
|
||||
send(z21_lan_get_hwinfo());
|
||||
send(z21_lan_systemstate_getdata());
|
||||
}
|
||||
else if(m_socket.is_open() && !value)
|
||||
{
|
||||
@ -236,7 +218,53 @@ void Z21::receive()
|
||||
const z21_lan_header* cmd = reinterpret_cast<const z21_lan_header*>(m_receiveBuffer.data());
|
||||
switch(cmd->header)
|
||||
{
|
||||
case Z21_HEADER_X:
|
||||
case Z21_LAN_GET_SERIAL_NUMBER:
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, value=static_cast<const z21_lan_get_serial_number_reply*>(cmd)->serialNumber]()
|
||||
{
|
||||
serialNumber = value;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Z21_LAN_GET_HWINFO:
|
||||
{
|
||||
const z21_lan_get_hwinfo_reply* reply = static_cast<const z21_lan_get_hwinfo_reply*>(cmd);
|
||||
|
||||
std::string hwType;
|
||||
switch(reply->hardwareType)
|
||||
{
|
||||
case Z21_HWT_Z21_OLD:
|
||||
hwType = "Black Z21 (hardware variant from 2012)";
|
||||
break;
|
||||
case Z21_HWT_Z21_NEW:
|
||||
hwType = "Black Z21 (hardware variant from 2013)";
|
||||
break;
|
||||
case Z21_HWT_SMARTRAIL:
|
||||
hwType = "SmartRail (from 2012)";
|
||||
break;
|
||||
case Z21_HWT_z21_SMALL:
|
||||
hwType = "White Z21 (starter set variant from 2013)";
|
||||
break;
|
||||
case Z21_HWT_z21_START :
|
||||
hwType = "Z21 start (starter set variant from 2016)";
|
||||
break;
|
||||
default:
|
||||
hwType = "0x" + to_hex(reply->hardwareType);
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string fwVersion = std::to_string((reply->firmwareVersion >> 8) & 0xFF) + "." + std::to_string(reply->firmwareVersion & 0xFF);
|
||||
|
||||
EventLoop::call(
|
||||
[this, hwType, fwVersion]()
|
||||
{
|
||||
hardwareType = hwType;
|
||||
firmwareVersion = fwVersion;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Z21_LAN_X:
|
||||
{
|
||||
// TODO check XOR
|
||||
const uint8_t xheader = static_cast<const z21_lan_x*>(cmd)->xheader;
|
||||
@ -263,16 +291,51 @@ void Z21::receive()
|
||||
if(decoder)
|
||||
{
|
||||
decoder->direction = direction;
|
||||
if((speedStepMode == 0 && decoder->speedSteps == 14) ||
|
||||
(speedStepMode == 2 && decoder->speedSteps == 28) ||
|
||||
(speedStepMode == 4 && decoder->speedSteps == 126))
|
||||
decoder->speedStep = speedStep;
|
||||
|
||||
for(auto& function : *decoder->functions)
|
||||
{
|
||||
const uint8_t number = function->number;
|
||||
if(number <= 28)
|
||||
function->value = functions & (1 << number);
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Z21_LAN_X_BC:
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
case Z21_LAN_X_BC_STOPPED:
|
||||
EventLoop::call(
|
||||
[this]()
|
||||
{
|
||||
emergencyStop = true;
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
EventLoop::call([this, xheader](){ Traintastic::instance->console->debug(id, "unknown xheader 0x" + to_hex(xheader)); });
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Z21_LAN_SYSTEMSTATE_DATACHANGED:
|
||||
{
|
||||
const z21_lan_systemstate_datachanged state = *reinterpret_cast<const z21_lan_systemstate_datachanged*>(m_receiveBuffer.data());
|
||||
EventLoop::call(
|
||||
[this, state]()
|
||||
{
|
||||
emergencyStop = state.centralState & Z21_CENTRALSTATE_EMERGENCYSTOP;
|
||||
trackVoltageOff = state.centralState & Z21_CENTRALSTATE_TRACKVOLTAGEOFF;
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
EventLoop::call([this, header=cmd->header](){ Traintastic::instance->console->debug(id, "unknown header 0x" + to_hex(header)); });
|
||||
break;
|
||||
|
||||
@ -28,10 +28,10 @@
|
||||
#include "../../core/objectproperty.hpp"
|
||||
#include "protocol/xpressnet.hpp"
|
||||
|
||||
namespace Hardware::CommandStation {
|
||||
|
||||
struct z21_lan_header;
|
||||
|
||||
namespace Hardware::CommandStation {
|
||||
|
||||
class Z21 : public CommandStation
|
||||
{
|
||||
protected:
|
||||
@ -46,6 +46,7 @@ class Z21 : public CommandStation
|
||||
|
||||
void receive();
|
||||
void send(const z21_lan_header* msg);
|
||||
inline void send(const z21_lan_header& msg) { send(&msg); }
|
||||
|
||||
public:
|
||||
CLASS_ID("hardware.command_station.z21")
|
||||
@ -55,6 +56,11 @@ class Z21 : public CommandStation
|
||||
|
||||
Property<std::string> hostname;
|
||||
Property<uint16_t> port;
|
||||
Property<uint32_t> serialNumber;
|
||||
Property<std::string> hardwareType;
|
||||
Property<std::string> firmwareVersion;
|
||||
Property<bool> emergencyStop;
|
||||
Property<bool> trackVoltageOff;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren