From 687007d5a043ea8586342382d4f71b2715d72662 Mon Sep 17 00:00:00 2001 From: Balazs Racz Date: Mon, 6 Mar 2023 14:59:55 +0100 Subject: [PATCH] Adds a nodejs target for memconfig utils (#697) - Adds js.emscripten traget for memconfig_utils commandline tool. This will allow building a windows binary out of it. - Refactors the main.cxx from linux to a shared main.hxx - Updates from synchronous execution to a state flow. - Adds specific main.cxx for linux and for emscripten. === * Moves main.cxx for memconfig utils to the application directory so that it can be shared between different targets. * Ignores and cleans the release binaries for bootloader client. * Adds memconfig utils target for javascript. * Adds new main files for both linux and js.emscripten. * Updates the forked version of main to be includeable. Replaces the synchronous execution of the application code with an async flow such that we can run it in emscripten as well. * Adds newline to gridconnect output. --- .../targets/js.emscripten/.gitignore | 2 + .../targets/js.emscripten/Makefile | 5 +- applications/memconfig_utils/main.hxx | 316 ++++++++++++++++++ applications/memconfig_utils/targets/Makefile | 2 +- .../targets/js.emscripten/.gitignore | 6 + .../targets/js.emscripten/Makefile | 21 ++ .../targets/js.emscripten/lib/Makefile | 1 + .../targets/js.emscripten/main.cxx | 65 ++++ .../targets/js.emscripten/package.json | 13 + .../targets/linux.x86/main.cxx | 202 +---------- 10 files changed, 434 insertions(+), 199 deletions(-) create mode 100644 applications/memconfig_utils/main.hxx create mode 100644 applications/memconfig_utils/targets/js.emscripten/.gitignore create mode 100644 applications/memconfig_utils/targets/js.emscripten/Makefile create mode 100644 applications/memconfig_utils/targets/js.emscripten/lib/Makefile create mode 100644 applications/memconfig_utils/targets/js.emscripten/main.cxx create mode 100644 applications/memconfig_utils/targets/js.emscripten/package.json diff --git a/applications/bootloader_client/targets/js.emscripten/.gitignore b/applications/bootloader_client/targets/js.emscripten/.gitignore index 97dc46a68..e5244b63b 100644 --- a/applications/bootloader_client/targets/js.emscripten/.gitignore +++ b/applications/bootloader_client/targets/js.emscripten/.gitignore @@ -1,2 +1,4 @@ bootloader_client.js node_modules +# released binary +openmrn-bootloader-client-* diff --git a/applications/bootloader_client/targets/js.emscripten/Makefile b/applications/bootloader_client/targets/js.emscripten/Makefile index 1675ca81a..363292221 100644 --- a/applications/bootloader_client/targets/js.emscripten/Makefile +++ b/applications/bootloader_client/targets/js.emscripten/Makefile @@ -11,8 +11,11 @@ release: pkg -C Brotli . -clean: clean-wasm +clean: clean-wasm clean-bin clean-wasm: rm -f $(EXECUTABLE).{wasm,wast} + +clean-bin: + rm -f openmrn-bootloader-client-{linux,macos,win.exe} diff --git a/applications/memconfig_utils/main.hxx b/applications/memconfig_utils/main.hxx new file mode 100644 index 000000000..d37e5f6e2 --- /dev/null +++ b/applications/memconfig_utils/main.hxx @@ -0,0 +1,316 @@ +/** \copyright + * Copyright (c) 2013 - 2023, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file main.hxx + * + * An application for downloading an entire memory space from a node. + * + * @author Balazs Racz + * @date 7 Sep 2017 + */ + +#include +#include +#include +#include + +#include + +#include "os/os.h" +#include "utils/constants.hxx" +#include "utils/Hub.hxx" +#include "utils/GridConnectHub.hxx" +#include "utils/GcTcpHub.hxx" +#include "utils/Crc.hxx" +#include "utils/FileUtils.hxx" +#include "utils/format_utils.hxx" +#include "executor/Executor.hxx" +#include "executor/Service.hxx" + +#include "openlcb/IfCan.hxx" +#include "openlcb/DatagramCan.hxx" +#include "openlcb/BootloaderClient.hxx" +#include "openlcb/If.hxx" +#include "openlcb/AliasAllocator.hxx" +#include "openlcb/DefaultNode.hxx" +#include "openlcb/NodeInitializeFlow.hxx" +#include "openlcb/MemoryConfig.hxx" +#include "openlcb/MemoryConfigClient.hxx" +#include "utils/socket_listener.hxx" + +NO_THREAD nt; +Executor<1> g_executor(nt); +Service g_service(&g_executor); +CanHubFlow can_hub0(&g_service); + +OVERRIDE_CONST(gc_generate_newlines, 1); + +static const openlcb::NodeID NODE_ID = 0x05010101181FULL; + +openlcb::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2); +openlcb::InitializeFlow g_init_flow{&g_service}; +openlcb::CanDatagramService g_datagram_can(&g_if_can, 10, 2); +static openlcb::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can); +openlcb::DefaultNode g_node(&g_if_can, NODE_ID); +openlcb::MemoryConfigHandler g_memcfg(&g_datagram_can, &g_node, 10); +openlcb::MemoryConfigClient g_memcfg_cli(&g_node, &g_memcfg); + +namespace openlcb +{ +Pool *const g_incoming_datagram_allocator = mainBufferPool; +} + +static int port = 12021; +static const char *host = "localhost"; +static const char *device_path = nullptr; +static const char *filename = nullptr; +static uint64_t destination_nodeid = 0; +static uint64_t destination_alias = 0; +static int memory_space_id = openlcb::MemoryConfigDefs::SPACE_CONFIG; +static uint32_t offset = 0; +static constexpr uint32_t NLEN = (uint32_t)-1; +static uint32_t len = NLEN; +static bool partial_read = false; +static bool do_read = false; +static bool do_write = false; + +void usage(const char *e) +{ + fprintf(stderr, + "Usage: %s ([-i destination_host] [-p port] | [-d serial_port]) [-s " + "memory_space_id] [-o offset] [-l len] [-c csum_algo] (-r|-w) " + "(-n nodeid | -a alias) -f filename\n", + e); + fprintf(stderr, + "Connects to an openlcb bus and performs memory configuration protocol " + "operations on openlcb node with id `nodeid` with the contents of a " + "given file or arguments.\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 serial_port. Device takes " + "precedence over TCP host:port specification."); + fprintf(stderr, "\tThe default target is localhost:12021.\n"); + fprintf(stderr, "\tnodeid should be a 12-char hex string with 0x prefix and " + "no separators, like '-n 0x05010101141F'\n"); + fprintf(stderr, "\talias should be a 3-char hex string with 0x prefix and no " + "separators, like '-a 0x3F9'\n"); + fprintf(stderr, + "\tmemory_space_id defines which memory space to use " + "data into. Default is '-s 0x%02x'.\n", + openlcb::MemoryConfigDefs::SPACE_CONFIG); + fprintf(stderr, "\t-r or -w defines whether to read or write.\n"); + fprintf(stderr, + "\tIf offset and len are skipped for a read, then the entire memory " + "space will be downloaded.\n"); +#ifdef __EMSCRIPTEN__ + fprintf(stderr, "\t-D lists available serial ports.\n"); +#endif + exit(1); +} + +void parse_args(int argc, char *argv[]) +{ + int opt; + while ((opt = getopt(argc, argv, "hp:i:d:n:a:s:f:rwo:l:D")) >= 0) + { + switch (opt) + { + case 'h': + usage(argv[0]); + break; + case 'p': + port = atoi(optarg); + break; + case 'i': + host = optarg; + break; + case 'd': + device_path = optarg; + break; + case 'f': + filename = optarg; + break; + case 'n': + destination_nodeid = strtoll(optarg, nullptr, 16); + break; + case 'a': + destination_alias = strtoul(optarg, nullptr, 16); + break; + case 's': + memory_space_id = strtol(optarg, nullptr, 16); + break; + case 'o': + offset = strtol(optarg, nullptr, 10); + break; + case 'l': + len = strtol(optarg, nullptr, 10); + break; + case 'r': + do_read = true; + break; + case 'w': + do_write = true; + break; +#ifdef __EMSCRIPTEN__ + case 'D': + JSSerialPort::list_ports(); + break; +#endif + default: + fprintf(stderr, "Unknown option %c\n", opt); + usage(argv[0]); + } + } + partial_read = do_read && ((offset != 0) || (len != NLEN)); + if ((!filename && !partial_read) || + (!destination_nodeid && !destination_alias)) + { + usage(argv[0]); + } + if ((do_read ? 1 : 0) + (do_write ? 1 : 0) != 1) + { + fprintf(stderr, "Must set exactly one of option -r and option -w.\n\n"); + usage(argv[0]); + } +} + + +class HelperFlow : public StateFlowBase { +public: + HelperFlow() : StateFlowBase(&g_service) { + start_flow(STATE(wait_for_boot)); + } + + Action wait_for_boot() + { + return sleep_and_call(&timer_, MSEC_TO_NSEC(400), STATE(send_request)); + } + + /// Application business logic. + Action send_request() + { + openlcb::NodeHandle dst; + dst.alias = destination_alias; + dst.id = destination_nodeid; + HASSERT((!!do_read) + (!!do_write) == 1); + if (do_write) + { + auto payload = read_file_to_string(filename); + printf("Read %" PRIdPTR + " bytes from file %s. Writing to memory space 0x%02x\n", + payload.size(), filename, memory_space_id); + return invoke_subflow_and_wait(&g_memcfg_cli, STATE(write_done), + openlcb::MemoryConfigClientRequest::WRITE, dst, memory_space_id, + 0, std::move(payload)); + } + + if (do_read && partial_read) + { + printf("Loading from space 0x%02x offset %u length %d\n", + (unsigned)memory_space_id, (unsigned)offset, (int)len); + return invoke_subflow_and_wait(&g_memcfg_cli, STATE(part_read_done), + openlcb::MemoryConfigClientRequest::READ_PART, dst, + memory_space_id, offset, len); + } + else if (do_read) + { + auto cb = [](openlcb::MemoryConfigClientRequest *rq) { + static size_t last_len = rq->payload.size(); + if ((last_len & ~1023) != (rq->payload.size() & ~1023)) + { + printf("Loaded %d bytes\n", (int)rq->payload.size()); + last_len = rq->payload.size(); + } + }; + printf("Loading memory space 0x%02x\n", + (unsigned)memory_space_id); + return invoke_subflow_and_wait(&g_memcfg_cli, STATE(read_done), + openlcb::MemoryConfigClientRequest::READ, dst, memory_space_id, + std::move(cb)); + } + + printf("Nothing to do."); + return call_immediately(STATE(flow_done)); + } + + /// Invoked when a write operation is complete. Prints result and + /// terminates. + Action write_done() { + auto b = get_buffer_deleter(full_allocation_result(&g_memcfg_cli)); + printf("Result: %04x\n", b->data()->resultCode); + hasError_ = b->data()->resultCode != 0; + return call_immediately(STATE(flow_done)); + } + + /// Invoked when a partial read operation is complete. Prints result and + /// terminates. + Action part_read_done() { + auto b = get_buffer_deleter(full_allocation_result(&g_memcfg_cli)); + printf("Result: %04x\n", b->data()->resultCode); + printf("Data: %s\n", string_to_hex(b->data()->payload).c_str()); + hasError_ = b->data()->resultCode != 0; + return call_immediately(STATE(flow_done)); + } + + /// Invoked when a read operation is complete. Prints result and + /// terminates. + Action read_done() { + auto b = get_buffer_deleter(full_allocation_result(&g_memcfg_cli)); + printf("Result: %04x\n", b->data()->resultCode); + write_string_to_file(filename, b->data()->payload); + fprintf(stderr, "Written %" PRIdPTR " bytes to file %s.\n", + b->data()->payload.size(), filename); + hasError_ = b->data()->resultCode != 0; + return call_immediately(STATE(flow_done)); + } + + /// Terminates the process. + Action flow_done() + { + fflush(stdout); +#ifdef __EMSCRIPTEN__ + EM_ASM(process.exit()); +#endif + _exit(hasError_ ? 1 : 0); + + return exit(); + } + + StateFlowTimer timer_{this}; + bool hasError_ = false; +} helper_flow; + + +/// Runs the executor. Never returns. +void execute() { + g_if_can.add_addressed_message_support(); + // Bootstraps the alias allocation process. + g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc()); + + g_executor.thread_body(); +} diff --git a/applications/memconfig_utils/targets/Makefile b/applications/memconfig_utils/targets/Makefile index e09efa82d..4e1071528 100644 --- a/applications/memconfig_utils/targets/Makefile +++ b/applications/memconfig_utils/targets/Makefile @@ -1,5 +1,5 @@ SUBDIRS = linux.x86 \ -# js.emscripten + js.emscripten include $(OPENMRNPATH)/etc/recurse.mk diff --git a/applications/memconfig_utils/targets/js.emscripten/.gitignore b/applications/memconfig_utils/targets/js.emscripten/.gitignore new file mode 100644 index 000000000..eba1e38bc --- /dev/null +++ b/applications/memconfig_utils/targets/js.emscripten/.gitignore @@ -0,0 +1,6 @@ +memconfig_utils.js +node_modules +openmrn-memconfig-utils-linux +openmrn-memconfig-utils-macos +openmrn-memconfig-utils-win.exe + diff --git a/applications/memconfig_utils/targets/js.emscripten/Makefile b/applications/memconfig_utils/targets/js.emscripten/Makefile new file mode 100644 index 000000000..7e9f4547d --- /dev/null +++ b/applications/memconfig_utils/targets/js.emscripten/Makefile @@ -0,0 +1,21 @@ +-include ../../config.mk +include $(OPENMRNPATH)/etc/prog.mk +LDFLAGS += --bind -s WASM=0 + + +# How to prepare for releasing this: +# as administrator do +# npm install -g pkg +# then you can call make release +release: + pkg -C Brotli . + + +clean: clean-wasm clean-bin + + +clean-wasm: + rm -f $(EXECUTABLE).{wasm,wast} + +clean-bin: + rm -f openmrn-memconfig-utils-{linux,macos,win.exe} diff --git a/applications/memconfig_utils/targets/js.emscripten/lib/Makefile b/applications/memconfig_utils/targets/js.emscripten/lib/Makefile new file mode 100644 index 000000000..a414ed98e --- /dev/null +++ b/applications/memconfig_utils/targets/js.emscripten/lib/Makefile @@ -0,0 +1 @@ +include $(OPENMRNPATH)/etc/app_target_lib.mk diff --git a/applications/memconfig_utils/targets/js.emscripten/main.cxx b/applications/memconfig_utils/targets/js.emscripten/main.cxx new file mode 100644 index 000000000..36c4fc48c --- /dev/null +++ b/applications/memconfig_utils/targets/js.emscripten/main.cxx @@ -0,0 +1,65 @@ +/** \copyright + * Copyright (c) 2013, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \file main.cxx + * + * An application for updating the firmware of a remote node on the bus. + * + * @author Balazs Racz + * @date 3 Aug 2013 + */ + +#include +#include +#include + +#include "utils/JSSerialPort.hxx" +#include "utils/JSTcpClient.hxx" + +#include "main.hxx" + +/** Entry point to application. + * @param argc number of command line arguments + * @param argv array of command line arguments + * @return 0, should never return + */ +int appl_main(int argc, char *argv[]) +{ + parse_args(argc, argv); + std::unique_ptr dev; + std::unique_ptr client; + if (device_path) + { + dev.reset(new JSSerialPort(&can_hub0, device_path)); + } + else + { + client.reset(new JSTcpClient(&can_hub0, host, port)); + } + + execute(); + return 0; +} diff --git a/applications/memconfig_utils/targets/js.emscripten/package.json b/applications/memconfig_utils/targets/js.emscripten/package.json new file mode 100644 index 000000000..c1fc258ea --- /dev/null +++ b/applications/memconfig_utils/targets/js.emscripten/package.json @@ -0,0 +1,13 @@ +{ + "name": "openmrn-memconfig-utils", + "version": "1.0.0", + "dependencies": { + "serialport": "^9.2.7" + }, + "bin": "memconfig_utils.js", + "pkg": { + "assets": [ + "./node_modules/@serialport/bindings/build/Release/bindings.node" + ] + } +} diff --git a/applications/memconfig_utils/targets/linux.x86/main.cxx b/applications/memconfig_utils/targets/linux.x86/main.cxx index 746a4deb1..511d02f9c 100644 --- a/applications/memconfig_utils/targets/linux.x86/main.cxx +++ b/applications/memconfig_utils/targets/linux.x86/main.cxx @@ -1,5 +1,5 @@ /** \copyright - * Copyright (c) 2013 - 2017, Balazs Racz + * Copyright (c) 2013 - 2023, Balazs Racz * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,159 +29,10 @@ * An application for downloading an entire memory space from a node. * * @author Balazs Racz - * @date 7 Sep 2017 + * @date 1 Mar 2023 */ -#include -#include -#include -#include - -#include - -#include "os/os.h" -#include "utils/constants.hxx" -#include "utils/Hub.hxx" -#include "utils/GridConnectHub.hxx" -#include "utils/GcTcpHub.hxx" -#include "utils/Crc.hxx" -#include "utils/FileUtils.hxx" -#include "utils/format_utils.hxx" -#include "executor/Executor.hxx" -#include "executor/Service.hxx" - -#include "openlcb/IfCan.hxx" -#include "openlcb/DatagramCan.hxx" -#include "openlcb/BootloaderClient.hxx" -#include "openlcb/If.hxx" -#include "openlcb/AliasAllocator.hxx" -#include "openlcb/DefaultNode.hxx" -#include "openlcb/NodeInitializeFlow.hxx" -#include "openlcb/MemoryConfig.hxx" -#include "openlcb/MemoryConfigClient.hxx" -#include "utils/socket_listener.hxx" - -NO_THREAD nt; -Executor<1> g_executor(nt); -Service g_service(&g_executor); -CanHubFlow can_hub0(&g_service); - -static const openlcb::NodeID NODE_ID = 0x05010101181FULL; - -openlcb::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2); -openlcb::InitializeFlow g_init_flow{&g_service}; -openlcb::CanDatagramService g_datagram_can(&g_if_can, 10, 2); -static openlcb::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can); -openlcb::DefaultNode g_node(&g_if_can, NODE_ID); -openlcb::MemoryConfigHandler g_memcfg(&g_datagram_can, &g_node, 10); -openlcb::MemoryConfigClient g_memcfg_cli(&g_node, &g_memcfg); - -namespace openlcb -{ -Pool *const g_incoming_datagram_allocator = mainBufferPool; -} - -static int port = 12021; -static const char *host = "localhost"; -static const char *device_path = nullptr; -static const char *filename = nullptr; -static uint64_t destination_nodeid = 0; -static uint64_t destination_alias = 0; -static int memory_space_id = openlcb::MemoryConfigDefs::SPACE_CONFIG; -static uint32_t offset = 0; -static constexpr uint32_t NLEN = (uint32_t)-1; -static uint32_t len = NLEN; -static bool partial_read = false; -static bool do_read = false; -static bool do_write = false; - -void usage(const char *e) -{ - fprintf(stderr, - "Usage: %s ([-i destination_host] [-p port] | [-d device_path]) [-s " - "memory_space_id] [-o offset] [-l len] [-c csum_algo] (-r|-w) " - "(-n nodeid | -a alias) -f filename\n", - e); - fprintf(stderr, "Connects to an openlcb bus and performs the " - "bootloader protocol on openlcb node with id nodeid with " - "the contents of a given file.\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."); - fprintf(stderr, "The default target is localhost:12021.\n"); - fprintf(stderr, "nodeid should be a 12-char hex string with 0x prefix and " - "no separators, like '-b 0x05010101141F'\n"); - fprintf(stderr, "alias should be a 3-char hex string with 0x prefix and no " - "separators, like '-a 0x3F9'\n"); - fprintf(stderr, "memory_space_id defines which memory space to use " - "data into. Default is '-s 0xF0'.\n"); - fprintf(stderr, "-r or -w defines whether to read or write.\n"); - exit(1); -} - -void parse_args(int argc, char *argv[]) -{ - int opt; - while ((opt = getopt(argc, argv, "hp:i:d:n:a:s:f:rwo:l:")) >= 0) - { - switch (opt) - { - case 'h': - usage(argv[0]); - break; - case 'p': - port = atoi(optarg); - break; - case 'i': - host = optarg; - break; - case 'd': - device_path = optarg; - break; - case 'f': - filename = optarg; - break; - case 'n': - destination_nodeid = strtoll(optarg, nullptr, 16); - break; - case 'a': - destination_alias = strtoul(optarg, nullptr, 16); - break; - case 's': - memory_space_id = strtol(optarg, nullptr, 16); - break; - case 'o': - offset = strtol(optarg, nullptr, 10); - break; - case 'l': - len = strtol(optarg, nullptr, 10); - break; - case 'r': - do_read = true; - break; - case 'w': - do_write = true; - break; - default: - fprintf(stderr, "Unknown option %c\n", opt); - usage(argv[0]); - } - } - partial_read = do_read && ((offset != 0) || (len != NLEN)); - if ((!filename && !partial_read) || - (!destination_nodeid && !destination_alias)) - { - usage(argv[0]); - } - if ((do_read ? 1 : 0) + (do_write ? 1 : 0) != 1) - { - fprintf(stderr, "Must set exactly one of option -r and option -w.\n\n"); - usage(argv[0]); - } -} +#include "main.hxx" /** Entry point to application. * @param argc number of command line arguments @@ -191,6 +42,7 @@ void parse_args(int argc, char *argv[]) int appl_main(int argc, char *argv[]) { parse_args(argc, argv); + int conn_fd = 0; if (device_path) { @@ -203,50 +55,6 @@ int appl_main(int argc, char *argv[]) HASSERT(conn_fd >= 0); create_gc_port_for_can_hub(&can_hub0, conn_fd); - g_if_can.add_addressed_message_support(); - // Bootstraps the alias allocation process. - g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc()); - - g_executor.start_thread("g_executor", 0, 1024); - usleep(400000); - - openlcb::NodeHandle dst; - dst.alias = destination_alias; - dst.id = destination_nodeid; - HASSERT((!!do_read) + (!!do_write) == 1); - if (do_write) - { - auto payload = read_file_to_string(filename); - printf("Read %" PRIdPTR - " bytes from file %s. Writing to memory space 0x%02x\n", - payload.size(), filename, memory_space_id); - auto b = invoke_flow(&g_memcfg_cli, - openlcb::MemoryConfigClientRequest::WRITE, dst, memory_space_id, 0, - std::move(payload)); - printf("Result: %04x\n", b->data()->resultCode); - } - - if (do_read && partial_read) - { - auto b = invoke_flow(&g_memcfg_cli, openlcb::MemoryConfigClientRequest::READ_PART, dst, memory_space_id, offset, len); - printf("Result: %04x\n", b->data()->resultCode); - printf("Data: %s\n", string_to_hex(b->data()->payload).c_str()); - } - else if (do_read) - { - auto cb = [](openlcb::MemoryConfigClientRequest *rq) { - static size_t last_len = rq->payload.size(); - if ((last_len & ~1023) != (rq->payload.size() & ~1023)) { - printf("Loaded %d bytes\n", (int)rq->payload.size()); - last_len = rq->payload.size(); - } - }; - auto b = invoke_flow(&g_memcfg_cli, openlcb::MemoryConfigClientRequest::READ, dst, memory_space_id, std::move(cb)); - printf("Result: %04x\n", b->data()->resultCode); - write_string_to_file(filename, b->data()->payload); - fprintf(stderr, "Written %" PRIdPTR " bytes to file %s.\n", - b->data()->payload.size(), filename); - } - + execute(); return 0; }