Added Z21 protocol stuff

Dieser Commit ist enthalten in:
reinder 2019-12-09 23:19:05 +01:00
Ursprung e3e139678e
Commit 6a7aac1634
3 geänderte Dateien mit 335 neuen und 51 gelöschten Zeilen

Datei anzeigen

@ -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

Datei anzeigen

@ -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;

Datei anzeigen

@ -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;
};
}