Skip to content

Commit

Permalink
Virtual Train application: adds ability to spawn many train nodes. (#698
Browse files Browse the repository at this point in the history
)

* Virtual Train application: adds ability to spawn many train nodes.

* Fixes bug in variable scopes.
  • Loading branch information
balazsracz authored Mar 6, 2023
1 parent 687007d commit 465b36a
Showing 1 changed file with 128 additions and 23 deletions.
151 changes: 128 additions & 23 deletions applications/train/targets/linux.x86/main.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@

#define LOGLEVEL INFO

#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>

#include <memory>

Expand All @@ -47,6 +47,7 @@
#include "openlcb/TractionTestTrain.hxx"
#include "openlcb/TractionTrain.hxx"
#include "os/os.h"
#include "os/sleep.h"
#include "utils/constants.hxx"

static const openlcb::NodeID NODE_ID = 0x0501010100F5ULL;
Expand All @@ -55,7 +56,8 @@ openlcb::SimpleCanStack stack(NODE_ID);

openlcb::TrainService traction_service(stack.iface());

const char *const openlcb::SNIP_DYNAMIC_FILENAME = openlcb::MockSNIPUserFile::snip_user_file_path;
const char *const openlcb::SNIP_DYNAMIC_FILENAME =
openlcb::MockSNIPUserFile::snip_user_file_path;
const char *const openlcb::CONFIG_FILENAME = openlcb::SNIP_DYNAMIC_FILENAME;

int port = 12021;
Expand All @@ -64,6 +66,8 @@ const char *device_path = nullptr;
const char *name = "Deadrail Train";
int address = 1732;
OVERRIDE_CONST(num_memory_spaces, 4);
OVERRIDE_CONST(local_nodes_count, 250);
OVERRIDE_CONST(local_alias_cache_size, 251);

namespace openlcb
{
Expand All @@ -74,17 +78,17 @@ const SimpleNodeStaticValues SNIP_STATIC_DATA = {
void usage(const char *e)
{
fprintf(stderr,
"Usage: %s ([-i destination_host] [-p port] | [-d device_path]) "
"[-a address] [-n name]\n\n",
e);
"Usage: %s ([-i destination_host] [-p port] | [-d device_path]) "
"[-a address] [-n name]\n\n",
e);
fprintf(
stderr, "Connects to an openlcb bus and exports a virtual train.\n");
fprintf(stderr,
"Connects to an openlcb bus and exports a virtual train.\n");
fprintf(stderr,
"The bus connection will be through an OpenLCB HUB on "
"destination_host:port with OpenLCB over TCP "
"(in GridConnect format) protocol, or through the CAN-USB device "
"(also in GridConnect protocol) found at device_path. Device takes "
"precedence over TCP host:port specification.");
"The bus connection will be through an OpenLCB HUB on "
"destination_host:port with OpenLCB over TCP "
"(in GridConnect format) protocol, or through the CAN-USB device "
"(also in GridConnect protocol) found at device_path. Device takes "
"precedence over TCP host:port specification.");
fprintf(stderr, "The default target is localhost:12021.\n");
fprintf(stderr, "Address is the virtual loco address. Default 1726.\n");
exit(1);
Expand Down Expand Up @@ -122,8 +126,92 @@ void parse_args(int argc, char *argv[])
}
}



namespace openlcb
{

/// This struct encompasses all objects needed for a virtual train node.
struct TrainInstance : private IncomingMessageStateFlow
{
/// Constructor
///
/// @param addr Train (DCC) address. Will be used for generating the node
/// ID.
/// @param name Train Name. Will be used for SNIP.
/// @param desc Train User Description. Will be used for SNIP.
TrainInstance(unsigned addr, const string &name, const string &desc)
: IncomingMessageStateFlow(stack.iface())
, address_(addr)
, nodeName_(name)
, nodeDescription_(desc)
, responseFlow_(stack.info_flow())
{
stack.iface()->dispatcher()->register_handler(
this, Defs::MTI_IDENT_INFO_REQUEST, Defs::MTI_EXACT);
}

/// DCC address
unsigned address_;
/// Text name for the train. Will appear on the throttle.
string nodeName_;
/// Text description of the node. Will be used in the SNIP reply.
string nodeDescription_;
/// Common helper flow for responding to SNIP requests.
SimpleInfoFlow *responseFlow_;
/// This implementtion object is supposed to drive the hardware.
LoggingTrain train_impl {address_};
/// Virtual Train Node object.
TrainNodeWithId node_ {&traction_service, &train_impl,
TractionDefs::NODE_ID_DCC | 0xFF000000u | address_};
/// Handles PIP requests for this node.
ProtocolIdentificationHandler pip {&node_,
Defs::EVENT_EXCHANGE | Defs::SIMPLE_NODE_INFORMATION |
Defs::TRACTION_CONTROL};
/// Ensures that this train responds to the IS_TRAIN event requests.
FixedEventProducer<TractionDefs::IS_TRAIN_EVENT> isTrainEventHandler_ {
&node_};

/// What should be our response to SNIP requests.
const SimpleInfoDescriptor SNIP_CUSTOM_RESPONSE[9] = {
{SimpleInfoDescriptor::LITERAL_BYTE, 4, 0, nullptr},
{SimpleInfoDescriptor::C_STRING, 0, 0,
SNIP_STATIC_DATA.manufacturer_name},
{SimpleInfoDescriptor::C_STRING, 0, 0, SNIP_STATIC_DATA.model_name},
{SimpleInfoDescriptor::C_STRING, 0, 0,
SNIP_STATIC_DATA.hardware_version},
{SimpleInfoDescriptor::C_STRING, 0, 0,
SNIP_STATIC_DATA.software_version},
{SimpleInfoDescriptor::LITERAL_BYTE, 2, 0, nullptr},
{SimpleInfoDescriptor::C_STRING, 0, 0, nodeName_.c_str()},
{SimpleInfoDescriptor::C_STRING, 0, 0, nodeDescription_.c_str()},
{SimpleInfoDescriptor::END_OF_DATA, 0, 0, 0}};

/// Custom handler for SNIP reply.
Action entry() OVERRIDE
{
if (!nmsg()->dstNode)
return release_and_exit();
if (nmsg()->dstNode != &node_)
return release_and_exit();
auto *b = responseFlow_->alloc();
b->data()->reset(
nmsg(), SNIP_CUSTOM_RESPONSE, Defs::MTI_IDENT_INFO_REPLY);
responseFlow_->send(b);
return release_and_exit();
}
}; // struct TrainInstance

} // namespace openlcb

int appl_main(int argc, char *argv[])
{
if (false)
{
// This will make it appear that the device is slow to respond to
// anything.
stack.iface()->set_tx_hook([]() { microsleep(50000); });
}
parse_args(argc, argv);
LOG(INFO, "Train name: %s", name);
openlcb::MockSNIPUserFile snip_user_file(name, "Deadrail--description");
Expand All @@ -137,16 +225,33 @@ int appl_main(int argc, char *argv[])
stack.connect_tcp_gridconnect_hub(host, port);
}

openlcb::LoggingTrain train_impl(address);
openlcb::TrainNodeWithId train_node(&traction_service, &train_impl, openlcb::TractionDefs::NODE_ID_DCC | 0xFF000000u | address);
openlcb::FixedEventProducer<openlcb::TractionDefs::IS_TRAIN_EVENT>
is_train_event_handler(&train_node);
openlcb::ProtocolIdentificationHandler pip(
&train_node,
openlcb::Defs::EVENT_EXCHANGE | openlcb::Defs::SIMPLE_NODE_INFORMATION |
openlcb::Defs::TRACTION_CONTROL);
openlcb::SNIPHandler snip_handler{stack.iface(), nullptr, stack.info_flow()};

stack.loop_executor();
if (false)
{
// This is the simple way to create one train
openlcb::LoggingTrain train_impl(address);
openlcb::TrainNodeWithId train_node(&traction_service, &train_impl,
openlcb::TractionDefs::NODE_ID_DCC | 0xFF000000u | address);
openlcb::FixedEventProducer<openlcb::TractionDefs::IS_TRAIN_EVENT>
is_train_event_handler(&train_node);
openlcb::ProtocolIdentificationHandler pip(&train_node,
openlcb::Defs::EVENT_EXCHANGE |
openlcb::Defs::SIMPLE_NODE_INFORMATION |
openlcb::Defs::TRACTION_CONTROL);
openlcb::SNIPHandler snip_handler {
stack.iface(), &train_node, stack.info_flow()};

// Have to loop before the objects above go out of scope.
stack.loop_executor();
}
else
{
// This is how we can create multiple trains.
openlcb::TrainInstance some_train(
address, name, "Deadrail--description");

// Have to loop before the objects above go out of scope.
stack.loop_executor();
}

return 0;
}

0 comments on commit 465b36a

Please sign in to comment.