commit 0ce1c2ba3cedd1e3494b36b8d385c963597b2d94 Author: dianjixz <18637716021@163.com> Date: Tue Jan 27 18:28:38 2026 +0800 [init] diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8b4feec --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "SDK"] + path = SDK + url = https://github.com/m5stack/M5Stack_Linux_Libs.git diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7cc8e0d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 M5Stack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a30bf06 --- /dev/null +++ b/README.md @@ -0,0 +1,369 @@ +# EC Proxy - Embedded Controller Communication System + +

M5Stack

+ +

+ EC Proxy is a high-performance embedded controller communication system that provides hardware abstraction and control for embedded devices. It bridges Modbus RTU communication with modern ZeroMQ-based RPC/PUB-SUB architecture, enabling intuitive hardware control through simple command-line tools or programmatic interfaces. +

+ +## Table of Contents + +* [Features](#features) +* [Architecture](#architecture) +* [Components](#components) +* [System Requirements](#system-requirements) +* [Compilation](#compilation) +* [Installation](#installation) +* [Usage](#usage) +* [Hardware Support](#hardware-support) +* [Documentation](#documentation) +* [Contributing](#contributing) +* [License](#license) + +## Features + +* **Modbus RTU Bridge**: Seamless communication with embedded controllers via serial interface +* **ZeroMQ RPC Service**: Modern, high-performance remote procedure call interface +* **Event Broadcasting**: Real-time PUB/SUB event system for hardware state changes +* **Comprehensive Hardware Control**: Manage power, USB ports, PCIe slots, RGB LEDs, LCD displays, fans, and sensors +* **Network Interface Management**: Automatic IP address synchronization for multiple network interfaces +* **Configuration Persistence**: Flash storage for critical configuration data +* **Multi-threaded Architecture**: Efficient concurrent handling of multiple hardware subsystems +* **Simple CLI Tool**: User-friendly command-line interface for all hardware operations +* **Cross-platform**: Built for embedded Linux devices (ARM64 architecture) +* **Open Source**: Licensed under MIT License + +## Architecture + +The EC Proxy system consists of two main components that work together to provide hardware abstraction: + +``` +┌─────────────────┐ Modbus RTU ┌──────────────────┐ ZMQ RPC/PUB ┌─────────────┐ +│ Hardware EC │ ◄─────────────────► │ EC Proxy │ ◄──────────────────► │ Client │ +│ (STM32/MCU) │ /dev/ttyS3 │ Server │ IPC/TCP Sockets │ (CLI/App) │ +└─────────────────┘ 115200-921600bps └──────────────────┘ └─────────────┘ +``` + +### Communication Flow + +1. **Hardware Layer**: Embedded controller (EC) exposes Modbus RTU interface over serial port +2. **Proxy Layer**: EC Proxy server translates Modbus commands to ZMQ RPC calls +3. **Client Layer**: CLI tools or applications interact via simple RPC interface +4. **Event Layer**: Hardware events (button presses, status changes) broadcast via PUB/SUB + +## Components + +### 1. EC Proxy Server (`ec_proxy`) + +The proxy server daemon that manages all hardware communication: + +- Modbus RTU serial communication +- ZeroMQ RPC service (`ipc:///tmp/rpc.ec_prox`) +- Event publisher (`ipc:///tmp/llm/ec_prox.event.socket`) +- Network interface monitoring +- Automatic fan control +- Button event handling + +### 2. EC CLI Tool (`cli`) + +Command-line interface for hardware control: + +```bash +# Control hardware peripherals +ec_cli device --fan -d 80 # Set fan to 80% speed +ec_cli device --rgb -d 5 # Set RGB LED mode +ec_cli device --board # Get power consumption info +ec_cli device --poweroff -d 1 # Trigger system poweroff + +# Execute custom RPC calls +ec_cli exec -l # List all available functions +ec_cli exec -f -d # Call custom function + +# Monitor hardware events +ec_cli echo --button # Subscribe to button events +``` + +See [detailed documentation](./projects/ec_proxy/README.md) for complete command reference. + +## System Requirements + +### Hardware +- ARM64-based embedded Linux device (AX630C, AX650N, or compatible) +- Serial port for Modbus communication (e.g., `/dev/ttyS3`) +- Embedded controller with Modbus RTU support + +### Software +- **Operating System**: Ubuntu 20.04+ (ARM64) or compatible Linux distribution +- **Cross-compilation Toolchain**: aarch64-none-linux-gnu-gcc 10.3+ +- **Build Tools**: SCons, Python 3.8+ +- **Libraries**: ZeroMQ, libmodbus, fmt, nlohmann-json + +## Compilation + +### 1. Install Cross-compilation Toolchain + +```bash +# Download and install ARM64 cross-compilation toolchain +wget https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/linaro/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz +sudo tar Jxvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt + +# Add to PATH (add to ~/.bashrc for persistence) +export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH +``` + +### 2. Install Build Dependencies + +```bash +# Install required packages +sudo apt update +sudo apt install -y python3 python3-pip libffi-dev git + +# Install Python build tools +pip3 install parse scons requests kconfiglib +``` + +### 3. Clone and Build + +```bash +# Clone repository +git clone https://github.com/m5stack/Ai_Pyramid_ec_proxy.git +cd Ai_Pyramid_ec_proxy + +# Initialize submodules (SDK components) +git submodule update --init --recursive + +# Navigate to EC Proxy project +cd projects/ec_proxy + +# Clean previous builds (optional) +scons distclean + +# Compile (use -j flag for parallel compilation) +scons -j$(nproc) +``` + +### 4. Build Output + +After successful compilation, binaries will be located in: + +``` +projects/ec_proxy/build/ +├── main_ec_proxy/ +│ └── ec_proxy # Proxy server executable +└── main_ec_cli/ + └── ec_cli # CLI tool executable +``` + +## Installation + +### Deploy to Target Device + +```bash +# Copy binaries to target device +scp build/main_ec_proxy/ec_proxy root@:/usr/local/bin/ +scp build/main_ec_cli/ec_cli root@:/usr/local/bin/ + +# On target device, make executable +chmod +x /usr/local/bin/ec_proxy +chmod +x /usr/local/bin/cli +``` + +### Create Systemd Service (Optional) + +Create `/etc/systemd/system/ec-proxy.service`: + +```ini +[Unit] +Description=EC Proxy Service +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/ec_proxy +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +``` + +Enable and start the service: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable ec-proxy +sudo systemctl start ec-proxy +``` + +## Usage + +### Start EC Proxy Server + +```bash +# Run directly +./ec_proxy + +# Or use systemd service +sudo systemctl start ec-proxy +``` + +### Using the CLI Tool + +```bash +# Get hardware status +ec_cli device --board # Power consumption info +ec_cli device --version # EC firmware version +ec_cli device --fanspeed # Current fan speed + +# Control peripherals +ec_cli device --fan -d 60 # Set fan PWM to 60% +ec_cli device --rgb -d 1 # Set RGB mode to 1 +ec_cli device --lcd_brightness -d 200 # Set LCD brightness + +# Network configuration +ec_cli device --ip_eth0 -d "192.168.1.100" +ec_cli device --flash_value # Save to flash memory + +# Monitor events +ec_cli echo --button # Watch for button presses +``` + +## Hardware Support + +EC Proxy supports control of the following hardware peripherals: + +### Power Management +- Board power control +- External power switch +- USB PD power info +- Power consumption monitoring +- Scheduled power on/off + +### Peripherals +- 3× USB downstream ports (with high-power mode) +- 2× PCIe slots +- GL3510 USB hub +- Grove I2C/UART interfaces + +### Display & LEDs +- RGB LED array (up to 64 LEDs) +- LCD display with brightness control +- Multiple display modes + +### Sensors & Control +- Fan PWM control with RPM monitoring +- Temperature-based auto fan control +- CPU voltage (VDD) adjustment +- Button input detection + +### Network +- Ethernet interface management (eth0, eth1) +- WLAN interface support +- Automatic IP address synchronization + +See [complete hardware documentation](./projects/ec_proxy/README.md#supported-hardware-peripherals) for detailed register mappings. + +## Documentation + +- **[EC Proxy Full Documentation](./projects/ec_proxy/README.md)** - Complete guide with all commands and features +- **[SDK Documentation](./SDK/README.md)** - Build system and component information +- **[API Reference](./projects/ec_proxy/README.md#supported-hardware-peripherals)** - Modbus register mapping + +## Configuration + +### Environment Variables + +```bash +# RPC socket path (default: ipc:///tmp/rpc.ec_prox) +export AX650C_EC_PROXY_RPC_SOCKET="ipc:///tmp/rpc.ec_prox" + +# PUB socket path (default: ipc:///tmp/llm/ec_prox.event.socket) +export AX650C_EC_PROXY_PUB_SOCKET="ipc:///tmp/llm/ec_prox.event.socket" + +# Initial RGB LED mode (default: 1) +export AX650_EC_RGB_MODE=1 + +# Initial LCD display mode (default: 2) +export AX650_EC_LCD_MODE=2 + +# Auto power-off timer in seconds (default: 30000) +export AX650_EC_POWER_OFF_TIME=30000 +``` + +### Modbus Configuration + +- **Serial Port**: `/dev/ttyS3` (configurable in code) +- **Baud Rate**: 921600 bps (auto-negotiable) +- **Data Format**: 8N1 (8 data bits, no parity, 1 stop bit) +- **Slave Address**: 1 + +## Troubleshooting + +### Common Issues + +**1. Compilation Errors** +```bash +# Ensure toolchain is in PATH +which aarch64-none-linux-gnu-gcc + +# Update submodules +git submodule update --init --recursive + +# Clean and rebuild +scons distclean && scons -j$(nproc) +``` + +**2. Connection Issues** +```bash +# Check if proxy is running +systemctl status ec-proxy + +# Verify serial port +ls -l /dev/ttyS3 + +# Check socket files +ls -l /tmp/rpc.ec_prox +``` + +**3. Permission Issues** +```bash +# Add user to dialout group for serial access +sudo usermod -aG dialout $USER + +# Set proper permissions +sudo chmod 666 /dev/ttyS3 +``` + +## Contributing + +Contributions are welcome! Please: + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +### Reporting Issues + +Please report bugs and feature requests via [GitHub Issues](https://github.com/m5stack/Ai_Pyramid_ec_proxy/issues). + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Acknowledgments + +- M5Stack team for hardware platform support +- AXERA for AI acceleration platform +- Open source community for excellent libraries (ZeroMQ, libmodbus, fmt, nlohmann-json) + +## Support + +- **Documentation**: [M5Stack Docs](https://docs.m5stack.com) +- **Forum**: [M5Stack Community](https://community.m5stack.com) +- **Issues**: [GitHub Issues](https://github.com/m5stack/Ai_Pyramid_ec_proxy/issues) + +--- + +**Note**: This project provides hardware abstraction for embedded controllers. It can be integrated with AI frameworks for advanced applications like voice-controlled hardware or automated system management. diff --git a/SDK b/SDK new file mode 160000 index 0000000..afbf2d2 --- /dev/null +++ b/SDK @@ -0,0 +1 @@ +Subproject commit afbf2d2382fc3a150c19469c1bab6fe2dac3e541 diff --git a/projects/ec_proxy/SConstruct b/projects/ec_proxy/SConstruct new file mode 100644 index 0000000..bc3199c --- /dev/null +++ b/projects/ec_proxy/SConstruct @@ -0,0 +1,11 @@ +from pathlib import Path +import os +import shutil + +os.environ['SDK_PATH'] = os.path.normpath(str(Path(os.getcwd())/'..'/'..'/'SDK')) +os.environ['EXT_COMPONENTS_PATH'] = os.path.normpath(str(Path(os.getcwd())/'..'/'..'/'ext_components')) + + +with open(str(Path(os.getcwd())/'..'/'..'/'SDK'/'tools'/'scons'/'project.py')) as f: + exec(f.read()) + diff --git a/projects/ec_proxy/config_defaults.mk b/projects/ec_proxy/config_defaults.mk new file mode 100644 index 0000000..3f6c11a --- /dev/null +++ b/projects/ec_proxy/config_defaults.mk @@ -0,0 +1,13 @@ +# unix +# CONFIG_TOOLCHAIN_PATH="/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin" +# win +# CONFIG_TOOLCHAIN_PATH="..\\gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf\\bin" + +# CONFIG_TOOLCHAIN_PREFIX="arm-linux-gnueabihf-" +# CONFIG_TOOLCHAIN_PATH="/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin" +CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-" +CONFIG_CPU_ARCH="armv8" +CONFIG_UTILITIES_ENABLED=y +CONFIG_UTILITIES_BASE64_ENABLED=y +CONFIG_MODBUS_ENABLED=y +CONFIG_UTILITIES_FMT_ENABLED=y diff --git a/projects/ec_proxy/include/cmdline.hpp b/projects/ec_proxy/include/cmdline.hpp new file mode 100644 index 0000000..ea8b59e --- /dev/null +++ b/projects/ec_proxy/include/cmdline.hpp @@ -0,0 +1,732 @@ +/* + Copyright (c) 2009, Hideyuki Tanaka + 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. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY ''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 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. +*/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cmdline { + +namespace detail { + +template +class lexical_cast_t { + public: + static Target cast(const Source &arg) { + Target ret; + std::stringstream ss; + if (!(ss << arg && ss >> ret && ss.eof())) throw std::bad_cast(); + + return ret; + } +}; + +template +class lexical_cast_t { + public: + static Target cast(const Source &arg) { return arg; } +}; + +template +class lexical_cast_t { + public: + static std::string cast(const Source &arg) { + std::ostringstream ss; + ss << arg; + return ss.str(); + } +}; + +template +class lexical_cast_t { + public: + static Target cast(const std::string &arg) { + Target ret; + std::istringstream ss(arg); + if (!(ss >> ret && ss.eof())) throw std::bad_cast(); + return ret; + } +}; + +template +struct is_same { + static const bool value = false; +}; + +template +struct is_same { + static const bool value = true; +}; + +template +Target lexical_cast(const Source &arg) { + return lexical_cast_t::value>::cast(arg); +} + +static inline std::string demangle(const std::string &name) { + int status = 0; + char *p = abi::__cxa_demangle(name.c_str(), 0, 0, &status); + std::string ret(p); + free(p); + return ret; +} + +template +std::string readable_typename() { + return demangle(typeid(T).name()); +} + +template +std::string default_value(T def) { + return detail::lexical_cast(def); +} + +template <> +inline std::string readable_typename() { + return "string"; +} + +} // namespace detail + +//----- + +class cmdline_error : public std::exception { + public: + cmdline_error(const std::string &msg) : msg(msg) {} + ~cmdline_error() throw() {} + const char *what() const throw() { return msg.c_str(); } + + private: + std::string msg; +}; + +template +struct default_reader { + T operator()(const std::string &str) { return detail::lexical_cast(str); } +}; + +template +struct range_reader { + range_reader(const T &low, const T &high) : low(low), high(high) {} + T operator()(const std::string &s) const { + T ret = default_reader()(s); + if (!(ret >= low && ret <= high)) + throw cmdline::cmdline_error("range_error"); + return ret; + } + + private: + T low, high; +}; + +template +range_reader range(const T &low, const T &high) { + return range_reader(low, high); +} + +template +struct oneof_reader { + T operator()(const std::string &s) { + T ret = default_reader()(s); + if (std::find(alt.begin(), alt.end(), ret) == alt.end()) + throw cmdline_error(""); + return ret; + } + void add(const T &v) { alt.push_back(v); } + + private: + std::vector alt; +}; + +template +oneof_reader oneof(T a1) { + oneof_reader ret; + ret.add(a1); + return ret; +} + +template +oneof_reader oneof(T a1, T a2) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3, T a4) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3, T a4, T a5) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + ret.add(a6); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + ret.add(a6); + ret.add(a7); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + ret.add(a6); + ret.add(a7); + ret.add(a8); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + ret.add(a6); + ret.add(a7); + ret.add(a8); + ret.add(a9); + return ret; +} + +template +oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, + T a10) { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + ret.add(a6); + ret.add(a7); + ret.add(a8); + ret.add(a9); + ret.add(a10); + return ret; +} + +//----- + +class parser { + public: + parser() {} + ~parser() { + for (std::map::iterator p = options.begin(); + p != options.end(); p++) + delete p->second; + } + + void add(const std::string &name, char short_name = 0, + const std::string &desc = "") { + if (options.count(name)) + throw cmdline_error("multiple definition: " + name); + options[name] = new option_without_value(name, short_name, desc); + ordered.push_back(options[name]); + } + + template + void add(const std::string &name, char short_name = 0, + const std::string &desc = "", bool need = true, const T def = T()) { + add(name, short_name, desc, need, def, default_reader()); + } + + template + void add(const std::string &name, char short_name = 0, + const std::string &desc = "", bool need = true, const T def = T(), + F reader = F()) { + if (options.count(name)) + throw cmdline_error("multiple definition: " + name); + options[name] = new option_with_value_with_reader( + name, short_name, need, def, desc, reader); + ordered.push_back(options[name]); + } + + void footer(const std::string &f) { ftr = f; } + + void set_program_name(const std::string &name) { prog_name = name; } + + bool exist(const std::string &name) const { + if (options.count(name) == 0) + throw cmdline_error("there is no flag: --" + name); + return options.find(name)->second->has_set(); + } + + template + const T &get(const std::string &name) const { + if (options.count(name) == 0) + throw cmdline_error("there is no flag: --" + name); + const option_with_value *p = + dynamic_cast *>(options.find(name)->second); + if (p == NULL) throw cmdline_error("type mismatch flag '" + name + "'"); + return p->get(); + } + + const std::vector &rest() const { return others; } + + bool parse(const std::string &arg) { + std::vector args; + + std::string buf; + bool in_quote = false; + for (std::string::size_type i = 0; i < arg.length(); i++) { + if (arg[i] == '\"') { + in_quote = !in_quote; + continue; + } + + if (arg[i] == ' ' && !in_quote) { + args.push_back(buf); + buf = ""; + continue; + } + + if (arg[i] == '\\') { + i++; + if (i >= arg.length()) { + errors.push_back("unexpected occurrence of '\\' at end of string"); + return false; + } + } + + buf += arg[i]; + } + + if (in_quote) { + errors.push_back("quote is not closed"); + return false; + } + + if (buf.length() > 0) args.push_back(buf); + + for (size_t i = 0; i < args.size(); i++) + std::cout << "\"" << args[i] << "\"" << std::endl; + + return parse(args); + } + + bool parse(const std::vector &args) { + int argc = static_cast(args.size()); + std::vector argv(argc); + + for (int i = 0; i < argc; i++) argv[i] = args[i].c_str(); + + return parse(argc, &argv[0]); + } + + bool parse(int argc, const char *const argv[]) { + errors.clear(); + others.clear(); + + if (argc < 1) { + errors.push_back("argument number must be longer than 0"); + return false; + } + if (prog_name == "") prog_name = argv[0]; + + std::map lookup; + for (std::map::iterator p = options.begin(); + p != options.end(); p++) { + if (p->first.length() == 0) continue; + char initial = p->second->short_name(); + if (initial) { + if (lookup.count(initial) > 0) { + lookup[initial] = ""; + errors.push_back(std::string("short option '") + initial + + "' is ambiguous"); + return false; + } else + lookup[initial] = p->first; + } + } + + for (int i = 1; i < argc; i++) { + if (strncmp(argv[i], "--", 2) == 0) { + const char *p = strchr(argv[i] + 2, '='); + if (p) { + std::string name(argv[i] + 2, p); + std::string val(p + 1); + set_option(name, val); + } else { + std::string name(argv[i] + 2); + if (options.count(name) == 0) { + errors.push_back("undefined option: --" + name); + continue; + } + if (options[name]->has_value()) { + if (i + 1 >= argc) { + errors.push_back("option needs value: --" + name); + continue; + } else { + i++; + set_option(name, argv[i]); + } + } else { + set_option(name); + } + } + } else if (strncmp(argv[i], "-", 1) == 0) { + if (!argv[i][1]) continue; + char last = argv[i][1]; + for (int j = 2; argv[i][j]; j++) { + last = argv[i][j]; + if (lookup.count(argv[i][j - 1]) == 0) { + errors.push_back(std::string("undefined short option: -") + + argv[i][j - 1]); + continue; + } + if (lookup[argv[i][j - 1]] == "") { + errors.push_back(std::string("ambiguous short option: -") + + argv[i][j - 1]); + continue; + } + set_option(lookup[argv[i][j - 1]]); + } + + if (lookup.count(last) == 0) { + errors.push_back(std::string("undefined short option: -") + last); + continue; + } + if (lookup[last] == "") { + errors.push_back(std::string("ambiguous short option: -") + last); + continue; + } + + if (i + 1 < argc && options[lookup[last]]->has_value()) { + set_option(lookup[last], argv[i + 1]); + i++; + } else { + set_option(lookup[last]); + } + } else { + others.push_back(argv[i]); + } + } + + for (std::map::iterator p = options.begin(); + p != options.end(); p++) + if (!p->second->valid()) + errors.push_back("need option: --" + std::string(p->first)); + + return errors.size() == 0; + } + + void parse_check(const std::string &arg) { + if (!options.count("help")) add("help", '?', "print this message"); + check(0, parse(arg)); + } + + void parse_check(const std::vector &args) { + if (!options.count("help")) add("help", '?', "print this message"); + check(args.size(), parse(args)); + } + + void parse_check(int argc, char *argv[]) { + if (!options.count("help")) add("help", '?', "print this message"); + check(argc, parse(argc, argv)); + } + + std::string error() const { return errors.size() > 0 ? errors[0] : ""; } + + std::string error_full() const { + std::ostringstream oss; + for (size_t i = 0; i < errors.size(); i++) oss << errors[i] << std::endl; + return oss.str(); + } + + std::string usage() const { + std::ostringstream oss; + oss << "usage: " << prog_name << " "; + for (size_t i = 0; i < ordered.size(); i++) { + if (ordered[i]->must()) oss << ordered[i]->short_description() << " "; + } + + oss << "[options] ... " << ftr << std::endl; + oss << "options:" << std::endl; + + size_t max_width = 0; + for (size_t i = 0; i < ordered.size(); i++) { + max_width = std::max(max_width, ordered[i]->name().length()); + } + for (size_t i = 0; i < ordered.size(); i++) { + if (ordered[i]->short_name()) { + oss << " -" << ordered[i]->short_name() << ", "; + } else { + oss << " "; + } + + oss << "--" << ordered[i]->name(); + for (size_t j = ordered[i]->name().length(); j < max_width + 4; j++) + oss << ' '; + oss << ordered[i]->description() << std::endl; + } + return oss.str(); + } + + private: + void check(int argc, bool ok) { + if ((argc == 1 && !ok) || exist("help")) { + std::cerr << usage(); + exit(0); + } + + if (!ok) { + std::cerr << error() << std::endl << usage(); + exit(1); + } + } + + void set_option(const std::string &name) { + if (options.count(name) == 0) { + errors.push_back("undefined option: --" + name); + return; + } + if (!options[name]->set()) { + errors.push_back("option needs value: --" + name); + return; + } + } + + void set_option(const std::string &name, const std::string &value) { + if (options.count(name) == 0) { + errors.push_back("undefined option: --" + name); + return; + } + if (!options[name]->set(value)) { + errors.push_back("option value is invalid: --" + name + "=" + value); + return; + } + } + + class option_base { + public: + virtual ~option_base() {} + + virtual bool has_value() const = 0; + virtual bool set() = 0; + virtual bool set(const std::string &value) = 0; + virtual bool has_set() const = 0; + virtual bool valid() const = 0; + virtual bool must() const = 0; + + virtual const std::string &name() const = 0; + virtual char short_name() const = 0; + virtual const std::string &description() const = 0; + virtual std::string short_description() const = 0; + }; + + class option_without_value : public option_base { + public: + option_without_value(const std::string &name, char short_name, + const std::string &desc) + : nam(name), snam(short_name), desc(desc), has(false) {} + ~option_without_value() {} + + bool has_value() const { return false; } + + bool set() { + has = true; + return true; + } + + bool set(const std::string &) { return false; } + + bool has_set() const { return has; } + + bool valid() const { return true; } + + bool must() const { return false; } + + const std::string &name() const { return nam; } + + char short_name() const { return snam; } + + const std::string &description() const { return desc; } + + std::string short_description() const { return "--" + nam; } + + private: + std::string nam; + char snam; + std::string desc; + bool has; + }; + + template + class option_with_value : public option_base { + public: + option_with_value(const std::string &name, char short_name, bool need, + const T &def, const std::string &desc) + : nam(name), + snam(short_name), + need(need), + has(false), + def(def), + actual(def) { + this->desc = full_description(desc); + } + ~option_with_value() {} + + const T &get() const { return actual; } + + bool has_value() const { return true; } + + bool set() { return false; } + + bool set(const std::string &value) { + try { + actual = read(value); + has = true; + } catch (const std::exception &e) { + return false; + } + return true; + } + + bool has_set() const { return has; } + + bool valid() const { + if (need && !has) return false; + return true; + } + + bool must() const { return need; } + + const std::string &name() const { return nam; } + + char short_name() const { return snam; } + + const std::string &description() const { return desc; } + + std::string short_description() const { + return "--" + nam + "=" + detail::readable_typename(); + } + + protected: + std::string full_description(const std::string &desc) { + return desc + " (" + detail::readable_typename() + + (need ? "" : " [=" + detail::default_value(def) + "]") + ")"; + } + + virtual T read(const std::string &s) = 0; + + std::string nam; + char snam; + bool need; + std::string desc; + + bool has; + T def; + T actual; + }; + + template + class option_with_value_with_reader : public option_with_value { + public: + option_with_value_with_reader(const std::string &name, char short_name, + bool need, const T def, + const std::string &desc, F reader) + : option_with_value(name, short_name, need, def, desc), + reader(reader) {} + + private: + T read(const std::string &s) { return reader(s); } + + F reader; + }; + + std::map options; + std::vector ordered; + std::string ftr; + + std::string prog_name; + std::vector others; + + std::vector errors; +}; + +} // namespace cmdline diff --git a/projects/ec_proxy/include/libzmq/zmq.h b/projects/ec_proxy/include/libzmq/zmq.h new file mode 100644 index 0000000..7b94061 --- /dev/null +++ b/projects/ec_proxy/include/libzmq/zmq.h @@ -0,0 +1,787 @@ +/* SPDX-License-Identifier: MPL-2.0 */ +/* ************************************************************************* + NOTE to contributors. This file comprises the principal public contract + for ZeroMQ API users. Any change to this file supplied in a stable + release SHOULD not break existing applications. + In practice this means that the value of constants must not change, and + that old values may not be reused for new constants. + ************************************************************************* +*/ + +#ifndef __ZMQ_H_INCLUDED__ +#define __ZMQ_H_INCLUDED__ + +/* Version macros for compile-time API version detection */ +#define ZMQ_VERSION_MAJOR 4 +#define ZMQ_VERSION_MINOR 3 +#define ZMQ_VERSION_PATCH 6 + +#define ZMQ_MAKE_VERSION(major, minor, patch) \ + ((major) *10000 + (minor) *100 + (patch)) +#define ZMQ_VERSION \ + ZMQ_MAKE_VERSION (ZMQ_VERSION_MAJOR, ZMQ_VERSION_MINOR, ZMQ_VERSION_PATCH) + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined _WIN32_WCE +#include +#endif +#include +#include + +/* Handle DSO symbol visibility */ +#if defined ZMQ_NO_EXPORT +#define ZMQ_EXPORT +#else +#if defined _WIN32 +#if defined ZMQ_STATIC +#define ZMQ_EXPORT +#elif defined DLL_EXPORT +#define ZMQ_EXPORT __declspec(dllexport) +#else +#define ZMQ_EXPORT __declspec(dllimport) +#endif +#else +#if defined __SUNPRO_C || defined __SUNPRO_CC +#define ZMQ_EXPORT __global +#elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER +#define ZMQ_EXPORT __attribute__ ((visibility ("default"))) +#else +#define ZMQ_EXPORT +#endif +#endif +#endif + +/* Define integer types needed for event interface */ +#define ZMQ_DEFINED_STDINT 1 +#if defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_OPENVMS +#include +#elif defined _MSC_VER && _MSC_VER < 1600 +#ifndef uint64_t +typedef unsigned __int64 uint64_t; +#endif +#ifndef int32_t +typedef __int32 int32_t; +#endif +#ifndef uint32_t +typedef unsigned __int32 uint32_t; +#endif +#ifndef uint16_t +typedef unsigned __int16 uint16_t; +#endif +#ifndef uint8_t +typedef unsigned __int8 uint8_t; +#endif +#else +#include +#endif + +#if !defined _WIN32 +// needed for sigset_t definition in zmq_ppoll +#include +#endif + +// 32-bit AIX's pollfd struct members are called reqevents and rtnevents so it +// defines compatibility macros for them. Need to include that header first to +// stop build failures since zmq_pollset_t defines them as events and revents. +#ifdef ZMQ_HAVE_AIX +#include +#endif + + +/******************************************************************************/ +/* 0MQ errors. */ +/******************************************************************************/ + +/* A number random enough not to collide with different errno ranges on */ +/* different OSes. The assumption is that error_t is at least 32-bit type. */ +#define ZMQ_HAUSNUMERO 156384712 + +/* On Windows platform some of the standard POSIX errnos are not defined. */ +#ifndef ENOTSUP +#define ENOTSUP (ZMQ_HAUSNUMERO + 1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (ZMQ_HAUSNUMERO + 2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (ZMQ_HAUSNUMERO + 3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (ZMQ_HAUSNUMERO + 4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (ZMQ_HAUSNUMERO + 5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (ZMQ_HAUSNUMERO + 6) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (ZMQ_HAUSNUMERO + 7) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (ZMQ_HAUSNUMERO + 8) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (ZMQ_HAUSNUMERO + 9) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (ZMQ_HAUSNUMERO + 10) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (ZMQ_HAUSNUMERO + 11) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (ZMQ_HAUSNUMERO + 12) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (ZMQ_HAUSNUMERO + 13) +#endif +#ifndef ECONNRESET +#define ECONNRESET (ZMQ_HAUSNUMERO + 14) +#endif +#ifndef ENOTCONN +#define ENOTCONN (ZMQ_HAUSNUMERO + 15) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (ZMQ_HAUSNUMERO + 16) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (ZMQ_HAUSNUMERO + 17) +#endif +#ifndef ENETRESET +#define ENETRESET (ZMQ_HAUSNUMERO + 18) +#endif + +/* Native 0MQ error codes. */ +#define EFSM (ZMQ_HAUSNUMERO + 51) +#define ENOCOMPATPROTO (ZMQ_HAUSNUMERO + 52) +#define ETERM (ZMQ_HAUSNUMERO + 53) +#define EMTHREAD (ZMQ_HAUSNUMERO + 54) + +/* This function retrieves the errno as it is known to 0MQ library. The goal */ +/* of this function is to make the code 100% portable, including where 0MQ */ +/* compiled with certain CRT library (on Windows) is linked to an */ +/* application that uses different CRT library. */ +ZMQ_EXPORT int zmq_errno (void); + +/* Resolves system errors and 0MQ errors to human-readable string. */ +ZMQ_EXPORT const char *zmq_strerror (int errnum_); + +/* Run-time API version detection */ +ZMQ_EXPORT void zmq_version (int *major_, int *minor_, int *patch_); + +/******************************************************************************/ +/* 0MQ infrastructure (a.k.a. context) initialisation & termination. */ +/******************************************************************************/ + +/* Context options */ +#define ZMQ_IO_THREADS 1 +#define ZMQ_MAX_SOCKETS 2 +#define ZMQ_SOCKET_LIMIT 3 +#define ZMQ_THREAD_PRIORITY 3 +#define ZMQ_THREAD_SCHED_POLICY 4 +#define ZMQ_MAX_MSGSZ 5 +#define ZMQ_MSG_T_SIZE 6 +#define ZMQ_THREAD_AFFINITY_CPU_ADD 7 +#define ZMQ_THREAD_AFFINITY_CPU_REMOVE 8 +#define ZMQ_THREAD_NAME_PREFIX 9 + +/* Default for new contexts */ +#define ZMQ_IO_THREADS_DFLT 1 +#define ZMQ_MAX_SOCKETS_DFLT 1023 +#define ZMQ_THREAD_PRIORITY_DFLT -1 +#define ZMQ_THREAD_SCHED_POLICY_DFLT -1 + +ZMQ_EXPORT void *zmq_ctx_new (void); +ZMQ_EXPORT int zmq_ctx_term (void *context_); +ZMQ_EXPORT int zmq_ctx_shutdown (void *context_); +ZMQ_EXPORT int zmq_ctx_set (void *context_, int option_, int optval_); +ZMQ_EXPORT int zmq_ctx_get (void *context_, int option_); + +/* Old (legacy) API */ +ZMQ_EXPORT void *zmq_init (int io_threads_); +ZMQ_EXPORT int zmq_term (void *context_); +ZMQ_EXPORT int zmq_ctx_destroy (void *context_); + + +/******************************************************************************/ +/* 0MQ message definition. */ +/******************************************************************************/ + +/* Some architectures, like sparc64 and some variants of aarch64, enforce pointer + * alignment and raise sigbus on violations. Make sure applications allocate + * zmq_msg_t on addresses aligned on a pointer-size boundary to avoid this issue. + */ +typedef struct zmq_msg_t +{ +#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) + __declspec(align (8)) unsigned char _[64]; +#elif defined(_MSC_VER) \ + && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE) || defined(_M_ARM)) + __declspec(align (4)) unsigned char _[64]; +#elif defined(__GNUC__) || defined(__INTEL_COMPILER) \ + || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590) \ + || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590) + unsigned char _[64] __attribute__ ((aligned (sizeof (void *)))); +#else + unsigned char _[64]; +#endif +} zmq_msg_t; + +typedef void (zmq_free_fn) (void *data_, void *hint_); + +ZMQ_EXPORT int zmq_msg_init (zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_init_size (zmq_msg_t *msg_, size_t size_); +ZMQ_EXPORT int zmq_msg_init_data ( + zmq_msg_t *msg_, void *data_, size_t size_, zmq_free_fn *ffn_, void *hint_); +ZMQ_EXPORT int zmq_msg_send (zmq_msg_t *msg_, void *s_, int flags_); +ZMQ_EXPORT int zmq_msg_recv (zmq_msg_t *msg_, void *s_, int flags_); +ZMQ_EXPORT int zmq_msg_close (zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_move (zmq_msg_t *dest_, zmq_msg_t *src_); +ZMQ_EXPORT int zmq_msg_copy (zmq_msg_t *dest_, zmq_msg_t *src_); +ZMQ_EXPORT void *zmq_msg_data (zmq_msg_t *msg_); +ZMQ_EXPORT size_t zmq_msg_size (const zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_more (const zmq_msg_t *msg_); +ZMQ_EXPORT int zmq_msg_get (const zmq_msg_t *msg_, int property_); +ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg_, int property_, int optval_); +ZMQ_EXPORT const char *zmq_msg_gets (const zmq_msg_t *msg_, + const char *property_); + +/******************************************************************************/ +/* 0MQ socket definition. */ +/******************************************************************************/ + +/* Socket types. */ +#define ZMQ_PAIR 0 +#define ZMQ_PUB 1 +#define ZMQ_SUB 2 +#define ZMQ_REQ 3 +#define ZMQ_REP 4 +#define ZMQ_DEALER 5 +#define ZMQ_ROUTER 6 +#define ZMQ_PULL 7 +#define ZMQ_PUSH 8 +#define ZMQ_XPUB 9 +#define ZMQ_XSUB 10 +#define ZMQ_STREAM 11 + +/* Deprecated aliases */ +#define ZMQ_XREQ ZMQ_DEALER +#define ZMQ_XREP ZMQ_ROUTER + +/* Socket options. */ +#define ZMQ_AFFINITY 4 +#define ZMQ_ROUTING_ID 5 +#define ZMQ_SUBSCRIBE 6 +#define ZMQ_UNSUBSCRIBE 7 +#define ZMQ_RATE 8 +#define ZMQ_RECOVERY_IVL 9 +#define ZMQ_SNDBUF 11 +#define ZMQ_RCVBUF 12 +#define ZMQ_RCVMORE 13 +#define ZMQ_FD 14 +#define ZMQ_EVENTS 15 +#define ZMQ_TYPE 16 +#define ZMQ_LINGER 17 +#define ZMQ_RECONNECT_IVL 18 +#define ZMQ_BACKLOG 19 +#define ZMQ_RECONNECT_IVL_MAX 21 +#define ZMQ_MAXMSGSIZE 22 +#define ZMQ_SNDHWM 23 +#define ZMQ_RCVHWM 24 +#define ZMQ_MULTICAST_HOPS 25 +#define ZMQ_RCVTIMEO 27 +#define ZMQ_SNDTIMEO 28 +#define ZMQ_LAST_ENDPOINT 32 +#define ZMQ_ROUTER_MANDATORY 33 +#define ZMQ_TCP_KEEPALIVE 34 +#define ZMQ_TCP_KEEPALIVE_CNT 35 +#define ZMQ_TCP_KEEPALIVE_IDLE 36 +#define ZMQ_TCP_KEEPALIVE_INTVL 37 +#define ZMQ_IMMEDIATE 39 +#define ZMQ_XPUB_VERBOSE 40 +#define ZMQ_ROUTER_RAW 41 +#define ZMQ_IPV6 42 +#define ZMQ_MECHANISM 43 +#define ZMQ_PLAIN_SERVER 44 +#define ZMQ_PLAIN_USERNAME 45 +#define ZMQ_PLAIN_PASSWORD 46 +#define ZMQ_CURVE_SERVER 47 +#define ZMQ_CURVE_PUBLICKEY 48 +#define ZMQ_CURVE_SECRETKEY 49 +#define ZMQ_CURVE_SERVERKEY 50 +#define ZMQ_PROBE_ROUTER 51 +#define ZMQ_REQ_CORRELATE 52 +#define ZMQ_REQ_RELAXED 53 +#define ZMQ_CONFLATE 54 +#define ZMQ_ZAP_DOMAIN 55 +#define ZMQ_ROUTER_HANDOVER 56 +#define ZMQ_TOS 57 +#define ZMQ_CONNECT_ROUTING_ID 61 +#define ZMQ_GSSAPI_SERVER 62 +#define ZMQ_GSSAPI_PRINCIPAL 63 +#define ZMQ_GSSAPI_SERVICE_PRINCIPAL 64 +#define ZMQ_GSSAPI_PLAINTEXT 65 +#define ZMQ_HANDSHAKE_IVL 66 +#define ZMQ_SOCKS_PROXY 68 +#define ZMQ_XPUB_NODROP 69 +#define ZMQ_BLOCKY 70 +#define ZMQ_XPUB_MANUAL 71 +#define ZMQ_XPUB_WELCOME_MSG 72 +#define ZMQ_STREAM_NOTIFY 73 +#define ZMQ_INVERT_MATCHING 74 +#define ZMQ_HEARTBEAT_IVL 75 +#define ZMQ_HEARTBEAT_TTL 76 +#define ZMQ_HEARTBEAT_TIMEOUT 77 +#define ZMQ_XPUB_VERBOSER 78 +#define ZMQ_CONNECT_TIMEOUT 79 +#define ZMQ_TCP_MAXRT 80 +#define ZMQ_THREAD_SAFE 81 +#define ZMQ_MULTICAST_MAXTPDU 84 +#define ZMQ_VMCI_BUFFER_SIZE 85 +#define ZMQ_VMCI_BUFFER_MIN_SIZE 86 +#define ZMQ_VMCI_BUFFER_MAX_SIZE 87 +#define ZMQ_VMCI_CONNECT_TIMEOUT 88 +#define ZMQ_USE_FD 89 +#define ZMQ_GSSAPI_PRINCIPAL_NAMETYPE 90 +#define ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE 91 +#define ZMQ_BINDTODEVICE 92 + +/* Message options */ +#define ZMQ_MORE 1 +#define ZMQ_SHARED 3 + +/* Send/recv options. */ +#define ZMQ_DONTWAIT 1 +#define ZMQ_SNDMORE 2 + +/* Security mechanisms */ +#define ZMQ_NULL 0 +#define ZMQ_PLAIN 1 +#define ZMQ_CURVE 2 +#define ZMQ_GSSAPI 3 + +/* RADIO-DISH protocol */ +#define ZMQ_GROUP_MAX_LENGTH 255 + +/* Deprecated options and aliases */ +#define ZMQ_IDENTITY ZMQ_ROUTING_ID +#define ZMQ_CONNECT_RID ZMQ_CONNECT_ROUTING_ID +#define ZMQ_TCP_ACCEPT_FILTER 38 +#define ZMQ_IPC_FILTER_PID 58 +#define ZMQ_IPC_FILTER_UID 59 +#define ZMQ_IPC_FILTER_GID 60 +#define ZMQ_IPV4ONLY 31 +#define ZMQ_DELAY_ATTACH_ON_CONNECT ZMQ_IMMEDIATE +#define ZMQ_NOBLOCK ZMQ_DONTWAIT +#define ZMQ_FAIL_UNROUTABLE ZMQ_ROUTER_MANDATORY +#define ZMQ_ROUTER_BEHAVIOR ZMQ_ROUTER_MANDATORY + +/* Deprecated Message options */ +#define ZMQ_SRCFD 2 + +/******************************************************************************/ +/* GSSAPI definitions */ +/******************************************************************************/ + +/* GSSAPI principal name types */ +#define ZMQ_GSSAPI_NT_HOSTBASED 0 +#define ZMQ_GSSAPI_NT_USER_NAME 1 +#define ZMQ_GSSAPI_NT_KRB5_PRINCIPAL 2 + +/******************************************************************************/ +/* 0MQ socket events and monitoring */ +/******************************************************************************/ + +/* Socket transport events (TCP, IPC and TIPC only) */ + +#define ZMQ_EVENT_CONNECTED 0x0001 +#define ZMQ_EVENT_CONNECT_DELAYED 0x0002 +#define ZMQ_EVENT_CONNECT_RETRIED 0x0004 +#define ZMQ_EVENT_LISTENING 0x0008 +#define ZMQ_EVENT_BIND_FAILED 0x0010 +#define ZMQ_EVENT_ACCEPTED 0x0020 +#define ZMQ_EVENT_ACCEPT_FAILED 0x0040 +#define ZMQ_EVENT_CLOSED 0x0080 +#define ZMQ_EVENT_CLOSE_FAILED 0x0100 +#define ZMQ_EVENT_DISCONNECTED 0x0200 +#define ZMQ_EVENT_MONITOR_STOPPED 0x0400 +#define ZMQ_EVENT_ALL 0xFFFF +/* Unspecified system errors during handshake. Event value is an errno. */ +#define ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL 0x0800 +/* Handshake complete successfully with successful authentication (if * + * enabled). Event value is unused. */ +#define ZMQ_EVENT_HANDSHAKE_SUCCEEDED 0x1000 +/* Protocol errors between ZMTP peers or between server and ZAP handler. * + * Event value is one of ZMQ_PROTOCOL_ERROR_* */ +#define ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL 0x2000 +/* Failed authentication requests. Event value is the numeric ZAP status * + * code, i.e. 300, 400 or 500. */ +#define ZMQ_EVENT_HANDSHAKE_FAILED_AUTH 0x4000 +#define ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED 0x10000000 +#define ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND 0x10000001 +#define ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE 0x10000002 +#define ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE 0x10000003 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED 0x10000011 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE 0x10000012 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO 0x10000013 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE 0x10000014 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR 0x10000015 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY 0x10000016 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME 0x10000017 +#define ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA 0x10000018 +// the following two may be due to erroneous configuration of a peer +#define ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC 0x11000001 +#define ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH 0x11000002 +#define ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED 0x20000000 +#define ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY 0x20000001 +#define ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID 0x20000002 +#define ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION 0x20000003 +#define ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE 0x20000004 +#define ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA 0x20000005 +#define ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED 0x30000000 + +ZMQ_EXPORT void *zmq_socket (void *, int type_); +ZMQ_EXPORT int zmq_close (void *s_); +ZMQ_EXPORT int +zmq_setsockopt (void *s_, int option_, const void *optval_, size_t optvallen_); +ZMQ_EXPORT int +zmq_getsockopt (void *s_, int option_, void *optval_, size_t *optvallen_); +ZMQ_EXPORT int zmq_bind (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_connect (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_unbind (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_disconnect (void *s_, const char *addr_); +ZMQ_EXPORT int zmq_send (void *s_, const void *buf_, size_t len_, int flags_); +ZMQ_EXPORT int +zmq_send_const (void *s_, const void *buf_, size_t len_, int flags_); +ZMQ_EXPORT int zmq_recv (void *s_, void *buf_, size_t len_, int flags_); +ZMQ_EXPORT int zmq_socket_monitor (void *s_, const char *addr_, int events_); + +/******************************************************************************/ +/* Hide socket fd type; this was before zmq_poller_event_t typedef below */ +/******************************************************************************/ + +#if defined _WIN32 +// Windows uses a pointer-sized unsigned integer to store the socket fd. +#if defined _WIN64 +typedef unsigned __int64 zmq_fd_t; +#else +typedef unsigned int zmq_fd_t; +#endif +#else +typedef int zmq_fd_t; +#endif + +/******************************************************************************/ +/* Deprecated I/O multiplexing. Prefer using zmq_poller API */ +/******************************************************************************/ + +#define ZMQ_POLLIN 1 +#define ZMQ_POLLOUT 2 +#define ZMQ_POLLERR 4 +#define ZMQ_POLLPRI 8 + +typedef struct zmq_pollitem_t +{ + void *socket; + zmq_fd_t fd; + short events; + short revents; +} zmq_pollitem_t; + +#define ZMQ_POLLITEMS_DFLT 16 + +ZMQ_EXPORT int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_); + +/******************************************************************************/ +/* Message proxying */ +/******************************************************************************/ + +ZMQ_EXPORT int zmq_proxy (void *frontend_, void *backend_, void *capture_); +ZMQ_EXPORT int zmq_proxy_steerable (void *frontend_, + void *backend_, + void *capture_, + void *control_); + +/******************************************************************************/ +/* Probe library capabilities */ +/******************************************************************************/ + +#define ZMQ_HAS_CAPABILITIES 1 +ZMQ_EXPORT int zmq_has (const char *capability_); + +/* Deprecated aliases */ +#define ZMQ_STREAMER 1 +#define ZMQ_FORWARDER 2 +#define ZMQ_QUEUE 3 + +/* Deprecated methods */ +ZMQ_EXPORT int zmq_device (int type_, void *frontend_, void *backend_); +ZMQ_EXPORT int zmq_sendmsg (void *s_, zmq_msg_t *msg_, int flags_); +ZMQ_EXPORT int zmq_recvmsg (void *s_, zmq_msg_t *msg_, int flags_); +struct iovec; +ZMQ_EXPORT int +zmq_sendiov (void *s_, struct iovec *iov_, size_t count_, int flags_); +ZMQ_EXPORT int +zmq_recviov (void *s_, struct iovec *iov_, size_t *count_, int flags_); + +/******************************************************************************/ +/* Encryption functions */ +/******************************************************************************/ + +/* Encode data with Z85 encoding. Returns encoded data */ +ZMQ_EXPORT char * +zmq_z85_encode (char *dest_, const uint8_t *data_, size_t size_); + +/* Decode data with Z85 encoding. Returns decoded data */ +ZMQ_EXPORT uint8_t *zmq_z85_decode (uint8_t *dest_, const char *string_); + +/* Generate z85-encoded public and private keypair with libsodium. */ +/* Returns 0 on success. */ +ZMQ_EXPORT int zmq_curve_keypair (char *z85_public_key_, char *z85_secret_key_); + +/* Derive the z85-encoded public key from the z85-encoded secret key. */ +/* Returns 0 on success. */ +ZMQ_EXPORT int zmq_curve_public (char *z85_public_key_, + const char *z85_secret_key_); + +/******************************************************************************/ +/* Atomic utility methods */ +/******************************************************************************/ + +ZMQ_EXPORT void *zmq_atomic_counter_new (void); +ZMQ_EXPORT void zmq_atomic_counter_set (void *counter_, int value_); +ZMQ_EXPORT int zmq_atomic_counter_inc (void *counter_); +ZMQ_EXPORT int zmq_atomic_counter_dec (void *counter_); +ZMQ_EXPORT int zmq_atomic_counter_value (void *counter_); +ZMQ_EXPORT void zmq_atomic_counter_destroy (void **counter_p_); + +/******************************************************************************/ +/* Scheduling timers */ +/******************************************************************************/ + +#define ZMQ_HAVE_TIMERS + +typedef void (zmq_timer_fn) (int timer_id, void *arg); + +ZMQ_EXPORT void *zmq_timers_new (void); +ZMQ_EXPORT int zmq_timers_destroy (void **timers_p); +ZMQ_EXPORT int +zmq_timers_add (void *timers, size_t interval, zmq_timer_fn handler, void *arg); +ZMQ_EXPORT int zmq_timers_cancel (void *timers, int timer_id); +ZMQ_EXPORT int +zmq_timers_set_interval (void *timers, int timer_id, size_t interval); +ZMQ_EXPORT int zmq_timers_reset (void *timers, int timer_id); +ZMQ_EXPORT long zmq_timers_timeout (void *timers); +ZMQ_EXPORT int zmq_timers_execute (void *timers); + + +/******************************************************************************/ +/* These functions are not documented by man pages -- use at your own risk. */ +/* If you need these to be part of the formal ZMQ API, then (a) write a man */ +/* page, and (b) write a test case in tests. */ +/******************************************************************************/ + +/* Helper functions are used by perf tests so that they don't have to care */ +/* about minutiae of time-related functions on different OS platforms. */ + +/* Starts the stopwatch. Returns the handle to the watch. */ +ZMQ_EXPORT void *zmq_stopwatch_start (void); + +/* Returns the number of microseconds elapsed since the stopwatch was */ +/* started, but does not stop or deallocate the stopwatch. */ +ZMQ_EXPORT unsigned long zmq_stopwatch_intermediate (void *watch_); + +/* Stops the stopwatch. Returns the number of microseconds elapsed since */ +/* the stopwatch was started, and deallocates that watch. */ +ZMQ_EXPORT unsigned long zmq_stopwatch_stop (void *watch_); + +/* Sleeps for specified number of seconds. */ +ZMQ_EXPORT void zmq_sleep (int seconds_); + +typedef void (zmq_thread_fn) (void *); + +/* Start a thread. Returns a handle to the thread. */ +ZMQ_EXPORT void *zmq_threadstart (zmq_thread_fn *func_, void *arg_); + +/* Wait for thread to complete then free up resources. */ +ZMQ_EXPORT void zmq_threadclose (void *thread_); + + +/******************************************************************************/ +/* These functions are DRAFT and disabled in stable releases, and subject to */ +/* change at ANY time until declared stable. */ +/******************************************************************************/ + +#ifdef ZMQ_BUILD_DRAFT_API + +/* DRAFT Socket types. */ +#define ZMQ_SERVER 12 +#define ZMQ_CLIENT 13 +#define ZMQ_RADIO 14 +#define ZMQ_DISH 15 +#define ZMQ_GATHER 16 +#define ZMQ_SCATTER 17 +#define ZMQ_DGRAM 18 +#define ZMQ_PEER 19 +#define ZMQ_CHANNEL 20 + +/* DRAFT Socket options. */ +#define ZMQ_ZAP_ENFORCE_DOMAIN 93 +#define ZMQ_LOOPBACK_FASTPATH 94 +#define ZMQ_METADATA 95 +#define ZMQ_MULTICAST_LOOP 96 +#define ZMQ_ROUTER_NOTIFY 97 +#define ZMQ_XPUB_MANUAL_LAST_VALUE 98 +#define ZMQ_SOCKS_USERNAME 99 +#define ZMQ_SOCKS_PASSWORD 100 +#define ZMQ_IN_BATCH_SIZE 101 +#define ZMQ_OUT_BATCH_SIZE 102 +#define ZMQ_WSS_KEY_PEM 103 +#define ZMQ_WSS_CERT_PEM 104 +#define ZMQ_WSS_TRUST_PEM 105 +#define ZMQ_WSS_HOSTNAME 106 +#define ZMQ_WSS_TRUST_SYSTEM 107 +#define ZMQ_ONLY_FIRST_SUBSCRIBE 108 +#define ZMQ_RECONNECT_STOP 109 +#define ZMQ_HELLO_MSG 110 +#define ZMQ_DISCONNECT_MSG 111 +#define ZMQ_PRIORITY 112 +#define ZMQ_BUSY_POLL 113 +#define ZMQ_HICCUP_MSG 114 +#define ZMQ_XSUB_VERBOSE_UNSUBSCRIBE 115 +#define ZMQ_TOPICS_COUNT 116 +#define ZMQ_NORM_MODE 117 +#define ZMQ_NORM_UNICAST_NACK 118 +#define ZMQ_NORM_BUFFER_SIZE 119 +#define ZMQ_NORM_SEGMENT_SIZE 120 +#define ZMQ_NORM_BLOCK_SIZE 121 +#define ZMQ_NORM_NUM_PARITY 122 +#define ZMQ_NORM_NUM_AUTOPARITY 123 +#define ZMQ_NORM_PUSH 124 + +/* DRAFT ZMQ_NORM_MODE options */ +#define ZMQ_NORM_FIXED 0 +#define ZMQ_NORM_CC 1 +#define ZMQ_NORM_CCL 2 +#define ZMQ_NORM_CCE 3 +#define ZMQ_NORM_CCE_ECNONLY 4 + +/* DRAFT ZMQ_RECONNECT_STOP options */ +#define ZMQ_RECONNECT_STOP_CONN_REFUSED 0x1 +#define ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED 0x2 +#define ZMQ_RECONNECT_STOP_AFTER_DISCONNECT 0x4 + +/* DRAFT Context options */ +#define ZMQ_ZERO_COPY_RECV 10 + +/* DRAFT Context methods. */ +ZMQ_EXPORT int zmq_ctx_set_ext (void *context_, + int option_, + const void *optval_, + size_t optvallen_); +ZMQ_EXPORT int zmq_ctx_get_ext (void *context_, + int option_, + void *optval_, + size_t *optvallen_); + +/* DRAFT Socket methods. */ +ZMQ_EXPORT int zmq_join (void *s, const char *group); +ZMQ_EXPORT int zmq_leave (void *s, const char *group); +ZMQ_EXPORT uint32_t zmq_connect_peer (void *s_, const char *addr_); + +/* DRAFT Msg methods. */ +ZMQ_EXPORT int zmq_msg_set_routing_id (zmq_msg_t *msg, uint32_t routing_id); +ZMQ_EXPORT uint32_t zmq_msg_routing_id (zmq_msg_t *msg); +ZMQ_EXPORT int zmq_msg_set_group (zmq_msg_t *msg, const char *group); +ZMQ_EXPORT const char *zmq_msg_group (zmq_msg_t *msg); +ZMQ_EXPORT int +zmq_msg_init_buffer (zmq_msg_t *msg_, const void *buf_, size_t size_); + +/* DRAFT Msg property names. */ +#define ZMQ_MSG_PROPERTY_ROUTING_ID "Routing-Id" +#define ZMQ_MSG_PROPERTY_SOCKET_TYPE "Socket-Type" +#define ZMQ_MSG_PROPERTY_USER_ID "User-Id" +#define ZMQ_MSG_PROPERTY_PEER_ADDRESS "Peer-Address" + +/* Router notify options */ +#define ZMQ_NOTIFY_CONNECT 1 +#define ZMQ_NOTIFY_DISCONNECT 2 + +/******************************************************************************/ +/* Poller polling on sockets,fd and thread-safe sockets */ +/******************************************************************************/ + +#define ZMQ_HAVE_POLLER + +typedef struct zmq_poller_event_t +{ + void *socket; + zmq_fd_t fd; + void *user_data; + short events; +} zmq_poller_event_t; + +ZMQ_EXPORT void *zmq_poller_new (void); +ZMQ_EXPORT int zmq_poller_destroy (void **poller_p); +ZMQ_EXPORT int zmq_poller_size (void *poller); +ZMQ_EXPORT int +zmq_poller_add (void *poller, void *socket, void *user_data, short events); +ZMQ_EXPORT int zmq_poller_modify (void *poller, void *socket, short events); +ZMQ_EXPORT int zmq_poller_remove (void *poller, void *socket); +ZMQ_EXPORT int +zmq_poller_wait (void *poller, zmq_poller_event_t *event, long timeout); +ZMQ_EXPORT int zmq_poller_wait_all (void *poller, + zmq_poller_event_t *events, + int n_events, + long timeout); +ZMQ_EXPORT int zmq_poller_fd (void *poller, zmq_fd_t *fd); + +ZMQ_EXPORT int +zmq_poller_add_fd (void *poller, zmq_fd_t fd, void *user_data, short events); +ZMQ_EXPORT int zmq_poller_modify_fd (void *poller, zmq_fd_t fd, short events); +ZMQ_EXPORT int zmq_poller_remove_fd (void *poller, zmq_fd_t fd); + +ZMQ_EXPORT int zmq_socket_get_peer_state (void *socket, + const void *routing_id, + size_t routing_id_size); + +/* DRAFT Socket monitoring events */ +#define ZMQ_EVENT_PIPES_STATS 0x10000 + +#define ZMQ_CURRENT_EVENT_VERSION 1 +#define ZMQ_CURRENT_EVENT_VERSION_DRAFT 2 + +#define ZMQ_EVENT_ALL_V1 ZMQ_EVENT_ALL +#define ZMQ_EVENT_ALL_V2 ZMQ_EVENT_ALL_V1 | ZMQ_EVENT_PIPES_STATS + +ZMQ_EXPORT int zmq_socket_monitor_versioned ( + void *s_, const char *addr_, uint64_t events_, int event_version_, int type_); +ZMQ_EXPORT int zmq_socket_monitor_pipes_stats (void *s); + +#if !defined _WIN32 +ZMQ_EXPORT int zmq_ppoll (zmq_pollitem_t *items_, + int nitems_, + long timeout_, + const sigset_t *sigmask_); +#else +// Windows has no sigset_t +ZMQ_EXPORT int zmq_ppoll (zmq_pollitem_t *items_, + int nitems_, + long timeout_, + const void *sigmask_); +#endif + +#endif // ZMQ_BUILD_DRAFT_API + + +#undef ZMQ_EXPORT + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/projects/ec_proxy/include/libzmq/zmq_utils.h b/projects/ec_proxy/include/libzmq/zmq_utils.h new file mode 100644 index 0000000..c409406 --- /dev/null +++ b/projects/ec_proxy/include/libzmq/zmq_utils.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MPL-2.0 */ + +/* This file is deprecated, and all its functionality provided by zmq.h */ +/* Note that -Wpedantic compilation requires GCC to avoid using its custom + extensions such as #warning, hence the trick below. Also, pragmas for + warnings or other messages are not standard, not portable, and not all + compilers even have an equivalent concept. + So in the worst case, this include file is treated as silently empty. */ + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) \ + || defined(_MSC_VER) +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wcpp" +#pragma GCC diagnostic ignored "-Werror" +#pragma GCC diagnostic ignored "-Wall" +#endif +#pragma message( \ + "Warning: zmq_utils.h is deprecated. All its functionality is provided by zmq.h.") +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#endif +#endif diff --git a/projects/ec_proxy/include/pzmq.hpp b/projects/ec_proxy/include/pzmq.hpp new file mode 100644 index 0000000..e321fdd --- /dev/null +++ b/projects/ec_proxy/include/pzmq.hpp @@ -0,0 +1,513 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include "libzmq/zmq.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define ZMQ_RPC_FUN (ZMQ_REP | 0x80) +#define ZMQ_RPC_CALL (ZMQ_REQ | 0x80) + +namespace StackFlows { + +class pzmq_data { +private: + zmq_msg_t msg; + +public: + pzmq_data(/* args */) + { + zmq_msg_init(&msg); + } + std::shared_ptr get_string() + { + auto len = zmq_msg_size(&msg); + return std::make_shared((const char *)zmq_msg_data(&msg), zmq_msg_size(&msg)); + } + std::string string() + { + auto len = zmq_msg_size(&msg); + return std::string((const char *)zmq_msg_data(&msg), zmq_msg_size(&msg)); + } + void *data() + { + return zmq_msg_data(&msg); + } + size_t size() + { + return zmq_msg_size(&msg); + } + zmq_msg_t *get() + { + return &msg; + } + + std::string get_param(int index, const std::string &idata = "") + { + const char *data = NULL; + int size = 0; + if (idata.length() > 0) { + data = idata.c_str(); + size = idata.length(); + } else { + data = (const char *)zmq_msg_data(&msg); + size = zmq_msg_size(&msg); + } + + if ((index % 2) == 0) { + return std::string((const char *)(data + 1), data[0]); + } else { + return std::string((const char *)(data + data[0] + 1), zmq_msg_size(&msg) - data[0] - 1); + } + } + + static std::string set_param(std::string param0, std::string param1) + { + std::string data = " " + param0 + param1; + data[0] = param0.length(); + return data; + } + + ~pzmq_data() + { + zmq_msg_close(&msg); + } +}; + +class pzmq { +public: + typedef std::function &)> rpc_callback_fun; + typedef std::function &)> msg_callback_fun; + +private: + const int rpc_url_head_length = 6; + std::string rpc_url_head_ = "ipc:///tmp/rpc."; + void *zmq_ctx_; + void *zmq_socket_; + std::unordered_map zmq_fun_; + std::mutex zmq_fun_mtx_; + std::atomic flage_; + std::unique_ptr zmq_thread_; + int mode_; + std::string rpc_server_; + std::string zmq_url_; + int timeout_; + + bool is_bind() + { + if ((mode_ == ZMQ_PUB) || (mode_ == ZMQ_PULL) || (mode_ == ZMQ_RPC_FUN)) + return true; + else + return false; + } + +public: + pzmq() : zmq_ctx_(NULL), zmq_socket_(NULL), flage_(true), timeout_(3000) + { + } + pzmq(const std::string &server) + : zmq_ctx_(NULL), zmq_socket_(NULL), rpc_server_(server), flage_(true), timeout_(3000) + { + if (server.find("://") != std::string::npos) { + rpc_url_head_.clear(); + } + } + pzmq(const std::string &url, int mode, const msg_callback_fun &raw_call = nullptr) + : zmq_ctx_(NULL), zmq_socket_(NULL), mode_(mode), flage_(true), timeout_(3000) + { + if ((url[0] != 'i') && (url[1] != 'p')) { + rpc_url_head_.clear(); + } + creat(url, raw_call); + } + void set_timeout(int ms) + { + timeout_ = ms; + } + int get_timeout() + { + return timeout_; + } + std::string get_zmq_url() + { + if ((!flage_.load()) && is_bind() && (zmq_url_.find("tcp://") != std::string::npos) && + (zmq_url_.find(":*") != std::string::npos)) { + std::vector zmq_url(zmq_url_.length() + 8, 0); + size_t addr_len = zmq_url.size(); + if (zmq_getsockopt(zmq_socket_, ZMQ_LAST_ENDPOINT, zmq_url.data(), &addr_len) != 0) { + return zmq_url_; + } + zmq_url_ = std::string(zmq_url.data()); + } + return zmq_url_; + } + std::string _rpc_list_action(pzmq *self, const std::shared_ptr &_None) + { + std::string action_list; + action_list.reserve(128); + action_list = "{\"actions\":["; + for (auto i = zmq_fun_.begin();;) { + action_list += "\""; + action_list += i->first; + action_list += "\""; + if (++i == zmq_fun_.end()) { + action_list += "]}"; + break; + } else { + action_list += ","; + } + } + return action_list; + } + int register_rpc_action(const std::string &action, const rpc_callback_fun &raw_call) + { + int ret = 0; + std::unique_lock lock(zmq_fun_mtx_); + if (zmq_fun_.find(action) != zmq_fun_.end()) { + zmq_fun_[action] = raw_call; + return ret; + } + if (zmq_fun_.empty()) { + std::string url = rpc_url_head_ + rpc_server_; + mode_ = ZMQ_RPC_FUN; + ret = creat(url); + } + zmq_fun_[action] = raw_call; + return ret; + } + void unregister_rpc_action(const std::string &action) + { + std::unique_lock lock(zmq_fun_mtx_); + if (zmq_fun_.find(action) != zmq_fun_.end()) { + zmq_fun_.erase(action); + } + } + int call_rpc_action(const std::string &action, const std::string &data, const msg_callback_fun &raw_call) + { + int ret; + std::shared_ptr msg_ptr = std::make_shared(); + try { + if (NULL == zmq_socket_) { + if (rpc_server_.empty()) return -1; + std::string url = rpc_url_head_ + rpc_server_; + mode_ = ZMQ_RPC_CALL; + ret = creat(url); + if (ret) { + throw ret; + } + } + // requist + { + zmq_send(zmq_socket_, action.c_str(), action.length(), ZMQ_SNDMORE); + zmq_send(zmq_socket_, data.c_str(), data.length(), 0); + } + // action + { + zmq_msg_recv(msg_ptr->get(), zmq_socket_, 0); + } + raw_call(this, msg_ptr); + } catch (int e) { + ret = e; + } + msg_ptr.reset(); + // close_zmq(); + return ret; + } + int creat(const std::string &url, const msg_callback_fun &raw_call = nullptr) + { + zmq_url_ = url; + do { + zmq_ctx_ = zmq_ctx_new(); + } while (zmq_ctx_ == NULL); + do { + zmq_socket_ = zmq_socket(zmq_ctx_, mode_ & 0x3f); + } while (zmq_socket_ == NULL); + + switch (mode_) { + case ZMQ_PUB: { + return creat_pub(url); + } break; + case ZMQ_SUB: { + int reconnect_interval = 100; + zmq_setsockopt(zmq_socket_, ZMQ_RECONNECT_IVL, &reconnect_interval, sizeof(reconnect_interval)); + + int max_reconnect_interval = 1000; // 5 seconds + zmq_setsockopt(zmq_socket_, ZMQ_RECONNECT_IVL_MAX, &max_reconnect_interval, + sizeof(max_reconnect_interval)); + return subscriber_url(url, raw_call); + } break; + case ZMQ_PUSH: { + int reconnect_interval = 100; + zmq_setsockopt(zmq_socket_, ZMQ_RECONNECT_IVL, &reconnect_interval, sizeof(reconnect_interval)); + int max_reconnect_interval = 1000; // 5 seconds + zmq_setsockopt(zmq_socket_, ZMQ_RECONNECT_IVL_MAX, &max_reconnect_interval, + sizeof(max_reconnect_interval)); + return creat_push(url); + } break; + case ZMQ_PULL: { + return creat_pull(url, raw_call); + } break; + case ZMQ_RPC_FUN: { + return creat_rep(url, raw_call); + } break; + case ZMQ_RPC_CALL: { + return creat_req(url); + } break; + default: + break; + } + return 0; + } + int check_zmq_errno(int code) + { + if (code == -1) { + // SLOGE("Error occurred: %s", zmq_strerror(errno)); + switch (errno) { + case EAGAIN: // In non-blocking mode, the sending buffer is full, so the message cannot be sent + // immediately. + break; + case ENOTSUP: // The requested operation is not supported. + break; + case EFSM: // Inconsistent state machine with the socket. + break; + case ETERM: // The ZeroMQ context has been terminated. + break; + case ENOTSOCK: // The provided socket is invalid. + break; + case EINTR: // The operation was interrupted by a signal. + break; + case EFAULT: // Invalid buffer or socket. + break; + default: + break; + } + } + return code; + } + int send_data(const std::string &raw) + { + return zmq_send(zmq_socket_, raw.c_str(), raw.length(), 0); + } + int send_data(const char *raw, int size) + { + return zmq_send(zmq_socket_, raw, size, 0); + } + inline int creat_pub(const std::string &url) + { + return zmq_bind(zmq_socket_, url.c_str()); + } + inline int creat_push(const std::string &url) + { + zmq_setsockopt(zmq_socket_, ZMQ_SNDTIMEO, &timeout_, sizeof(timeout_)); + return zmq_connect(zmq_socket_, url.c_str()); + } + inline int creat_pull(const std::string &url, const msg_callback_fun &raw_call) + { + int ret = zmq_bind(zmq_socket_, url.c_str()); + flage_ = false; + zmq_thread_ = std::make_unique(std::bind(&pzmq::zmq_event_loop, this, raw_call)); + return ret; + } + inline int subscriber_url(const std::string &url, const msg_callback_fun &raw_call) + { + int ret = zmq_connect(zmq_socket_, url.c_str()); + zmq_setsockopt(zmq_socket_, ZMQ_SUBSCRIBE, "", 0); + flage_ = false; + zmq_thread_ = std::make_unique(std::bind(&pzmq::zmq_event_loop, this, raw_call)); + return ret; + } + inline int creat_rep(const std::string &url, const msg_callback_fun &raw_call) + { + int ret = zmq_bind(zmq_socket_, url.c_str()); + zmq_fun_["list_action"] = + std::bind(&pzmq::_rpc_list_action, this, std::placeholders::_1, std::placeholders::_2); + flage_ = false; + zmq_thread_ = std::make_unique(std::bind(&pzmq::zmq_event_loop, this, raw_call)); + return ret; + } + inline int creat_req(const std::string &url) + { + if (!rpc_url_head_.empty()) { + std::string socket_file = url.substr(rpc_url_head_length); + if (access(socket_file.c_str(), F_OK) != 0) { + return -1; + } + } + zmq_setsockopt(zmq_socket_, ZMQ_SNDTIMEO, &timeout_, sizeof(timeout_)); + zmq_setsockopt(zmq_socket_, ZMQ_RCVTIMEO, &timeout_, sizeof(timeout_)); + return zmq_connect(zmq_socket_, url.c_str()); + } + void zmq_event_loop(const msg_callback_fun &raw_call) + { + int ret; + zmq_pollitem_t items[1]; + if (mode_ == ZMQ_PULL) { + items[0].socket = zmq_socket_; + items[0].fd = 0; + items[0].events = ZMQ_POLLIN; + items[0].revents = 0; + }; + while (!flage_.load()) { + std::shared_ptr msg_ptr = std::make_shared(); + if (mode_ == ZMQ_PULL) { + ret = zmq_poll(items, 1, -1); + if (ret == -1) { + zmq_close(zmq_socket_); + continue; + } + if (!(items[0].revents & ZMQ_POLLIN)) { + continue; + } + } + ret = zmq_msg_recv(msg_ptr->get(), zmq_socket_, 0); + if (ret <= 0) { + msg_ptr.reset(); + continue; + } + + if (mode_ == ZMQ_RPC_FUN) { + std::shared_ptr msg1_ptr = std::make_shared(); + zmq_msg_recv(msg1_ptr->get(), zmq_socket_, 0); + std::string retval; + rpc_callback_fun rpc_fun; + try { + rpc_callback_fun rpc_fun = zmq_fun_.at(msg_ptr->string()); + try { + retval = rpc_fun(this, msg1_ptr); + } catch (...) { + retval = msg_ptr->string() + " exec error"; + } + } catch (...) { + retval = msg_ptr->string() + " NotAction"; + } + zmq_send(zmq_socket_, retval.c_str(), retval.length(), 0); + msg1_ptr.reset(); + } else { + raw_call(this, msg_ptr); + } + msg_ptr.reset(); + } + } + void close_zmq() + { + int linger = 1000; + zmq_setsockopt(zmq_socket_, ZMQ_LINGER, &linger, sizeof(linger)); + zmq_close(zmq_socket_); + zmq_ctx_destroy(zmq_ctx_); + if ((mode_ == ZMQ_PUB) || (mode_ == ZMQ_PULL) || (mode_ == ZMQ_RPC_FUN)) { + if (!rpc_url_head_.empty()) { + std::string socket_file = zmq_url_.substr(rpc_url_head_length); + if (access(socket_file.c_str(), F_OK) == 0) { + remove(socket_file.c_str()); + } + } + } + zmq_socket_ = NULL; + zmq_ctx_ = NULL; + } + ~pzmq() + { + if (!zmq_socket_) { + return; + } + flage_ = true; + zmq_ctx_shutdown(zmq_ctx_); + if (zmq_thread_) { + zmq_thread_->join(); + } + close_zmq(); + } + +private: + std::shared_ptr context_ptr_; + std::weak_ptr wcontext_ptr_; + void *ctx_; + +public: + // context + void *context() + { + return ctx_; + } + void setContext(void *ctx) + { + ctx_ = ctx; + } + template + T *newContext() + { + ctx_ = new T; + return (T *)ctx_; + } + template + T *getContext() + { + return (T *)ctx_; + } + template + void deleteContext() + { + if (ctx_) { + delete (T *)ctx_; + ctx_ = NULL; + } + } + + // contextPtr + std::shared_ptr contextPtr() + { + return context_ptr_; + } + void setContextPtr(const std::shared_ptr &ctx) + { + context_ptr_ = ctx; + } + void setContextPtr(std::shared_ptr &&ctx) + { + context_ptr_ = std::move(ctx); + } + template + std::shared_ptr newContextPtr() + { + context_ptr_ = std::make_shared(); + return std::static_pointer_cast(context_ptr_); + } + template + std::shared_ptr getContextPtr() + { + return std::static_pointer_cast(context_ptr_); + } + void deleteContextPtr() + { + context_ptr_.reset(); + } + + // wcontextPtr + std::shared_ptr wcontextPtr() + { + return wcontext_ptr_.lock(); + } + void wsetContextPtr(const std::shared_ptr &ctx) + { + wcontext_ptr_ = ctx; + } + template + std::shared_ptr wgetContextPtr() + { + return std::static_pointer_cast(wcontext_ptr_.lock()); + } + void wdeleteContextPtr() + { + wcontext_ptr_.reset(); + } +}; +}; // namespace StackFlows \ No newline at end of file diff --git a/projects/ec_proxy/include/tbl_yield.h b/projects/ec_proxy/include/tbl_yield.h new file mode 100644 index 0000000..619b4ba --- /dev/null +++ b/projects/ec_proxy/include/tbl_yield.h @@ -0,0 +1,32 @@ +#ifndef _TBL_YIELD_H_ +#define _TBL_YIELD_H_ + +#define CORO_CTX(name) \ + static void * __coro_##name = NULL + +#define CORO_BEGIN(name) \ + CORO_CTX(name); \ + if (__coro_##name == NULL) { \ + __coro_##name = &&__coro_start_##name; \ + } \ + goto *__coro_##name; \ + __coro_start_##name: + + +#define CORO_END(name) \ + __coro_##name = NULL + +#define CORO_CONCAT(a, b, c) a##b##c + +#define CORO_YIELD_IMPL(name, codelin) \ + do { \ + __coro_##name = &&CORO_CONCAT(__coro_resume_, name, codelin); \ + return; \ + CORO_CONCAT(__coro_resume_, name, codelin):; \ + } while (0) + +#define CORO_YIELD(name) CORO_YIELD_IMPL(name, __LINE__) + + + +#endif \ No newline at end of file diff --git a/projects/ec_proxy/main_ec_cli/SConstruct b/projects/ec_proxy/main_ec_cli/SConstruct new file mode 100644 index 0000000..5fb2e73 --- /dev/null +++ b/projects/ec_proxy/main_ec_cli/SConstruct @@ -0,0 +1,40 @@ +import os + +Import('env') +with open(env['PROJECT_TOOL_S']) as f: + exec(f.read()) + + +SRCS = [] +INCLUDE = [ADir('../include')] +PRIVATE_INCLUDE = [] +REQUIREMENTS = ['pthread', 'utilities', 'modbus_component', 'zmq'] +STATIC_LIB = [] +DYNAMIC_LIB = [] +DEFINITIONS = [] +DEFINITIONS_PRIVATE = [] +LDFLAGS = [] +LINK_SEARCH_PATH = [] +STATIC_FILES = [] + +LINK_SEARCH_PATH += [ADir('../static')] + +LDFLAGS+=['-Wl,-rpath=/opt/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib/gcc-10.3', '-Wl,-rpath=/opt/lib', '-Wl,-rpath=/opt/usr/lib', '-Wl,-rpath=./'] + + + +SRCS = [AFile('src/ec_cli_main.cpp')] +env['COMPONENTS'].append({'target':'ec_cli-1.0', + 'SRCS':SRCS, + 'INCLUDE':INCLUDE, + 'PRIVATE_INCLUDE':PRIVATE_INCLUDE, + 'REQUIREMENTS':REQUIREMENTS, + 'STATIC_LIB':STATIC_LIB, + 'DYNAMIC_LIB':DYNAMIC_LIB, + 'DEFINITIONS':DEFINITIONS, + 'DEFINITIONS_PRIVATE':DEFINITIONS_PRIVATE, + 'LDFLAGS':LDFLAGS, + 'LINK_SEARCH_PATH':LINK_SEARCH_PATH, + 'STATIC_FILES':STATIC_FILES, + 'REGISTER':'project' + }) diff --git a/projects/ec_proxy/main_ec_cli/src/ec_cli_main.cpp b/projects/ec_proxy/main_ec_cli/src/ec_cli_main.cpp new file mode 100644 index 0000000..2371ab1 --- /dev/null +++ b/projects/ec_proxy/main_ec_cli/src/ec_cli_main.cpp @@ -0,0 +1,383 @@ +#include +#include +#include "pzmq.hpp" +#include "cmdline.hpp" +#include +#include +#include +#include +#include +#include "json.hpp" +int condition = 1; +const char *rpc_socket_path; +const char *pub_socket_path; + +void info_fun(cmdline::parser &a, std::string set_fun, std::string get_fun) +{ + if (a.exist("data") || a.exist("DataRaw")) { + if (set_fun.length() == 0) return; + std::string DataRaw = a.get("DataRaw"); + if (DataRaw.empty()) { + DataRaw = fmt::format("{{ \"data\": {} }}", a.get("data")); + } + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action(set_fun, DataRaw, + [set_fun](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call " << set_fun << " faile!" << std::endl; + } + }); + } else { + if (get_fun.length() == 0) { + std::cout << "please set data: -d or -D" << std::endl; + return; + } + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action(get_fun, "{}", + [get_fun](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call " << get_fun << " faile! " << std::endl; + } + }); + } +} + +void info_set_fun(cmdline::parser &a, std::string set_fun, std::string default_data) +{ + if (set_fun.length() == 0) return; + std::string DataRaw = default_data; + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action(set_fun, DataRaw, + [set_fun](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call " << set_fun << " faile!" << std::endl; + } + }); +} + +void lcd_putc_fun(cmdline::parser &a) +{ + if (a.exist("data")) { + std::string DataRaw = a.get("data"); + StackFlows::pzmq Context(rpc_socket_path); + for (auto i = 0; i < DataRaw.length(); i++) { + Context.call_rpc_action("lcd_putc", fmt::format("{{ \"data\": {} }}", (int)DataRaw[i]), + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + } else { + std::cout << "call lcd_putc faile!" << std::endl; + } + }); + } + } + return; +} + +void get_info_fun(cmdline::parser &a, std::string fun) +{ + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action(fun, "{\"data\":\"None\"}", + [fun](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call " << fun << " faile!" << std::endl; + } + }); +} + +void set_info_fun(cmdline::parser &a, std::string fun) +{ + if (a.exist("data") || a.exist("DataRaw")) { + std::string DataRaw = a.get("DataRaw"); + if (DataRaw.empty()) { + DataRaw = fmt::format("{{ \"data\": {} }}", a.get("data")); + } + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action(fun, DataRaw, + [fun](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call " << fun << "faile!" << std::endl; + } + }); + } +} + +void fan_speed_fun(cmdline::parser &a) +{ + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action("fan_get_speed", "{}", + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call fan_get_speed faile!" << std::endl; + } + }); +} + +void fan_pwm_fun(cmdline::parser &a) +{ + if (a.exist("data") || a.exist("DataRaw")) { + std::string DataRaw = a.get("DataRaw"); + if (DataRaw.empty()) { + DataRaw = fmt::format("{{ \"data\": {} }}", a.get("data")); + } + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action("fan_set_pwm", DataRaw, + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call fan_set_pwm faile!" << std::endl; + } + }); + } else { + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action("fan_get_pwm", "{}", + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call fan_get_pwm faile!" << std::endl; + } + }); + } +} + +void rgb_fun(cmdline::parser &a) +{ + if (a.exist("data") || a.exist("DataRaw")) { + std::string DataRaw = a.get("DataRaw"); + if (DataRaw.empty()) { + DataRaw = fmt::format("{{ \"data\": {} }}", a.get("data")); + } + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action("rgb_set_mode", DataRaw, + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call rgb_set_mode faile!" << std::endl; + } + }); + } else { + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action("rgb_get_mode", "{}", + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << data->string() << std::endl; + } else { + std::cout << "call rgb_get_mode faile!" << std::endl; + } + }); + } +} + +void exec_fun(cmdline::parser &a) +{ + if (a.exist("list")) { + StackFlows::pzmq Context(rpc_socket_path); + std::cout << "list" << std::endl; + Context.call_rpc_action("list_action", "None", + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + try { + nlohmann::json _data = nlohmann::json::parse(data->string()); + std::cout << _data.dump(4) << std::endl; + } catch (const nlohmann::json::parse_error &e) { + std::cerr << "JSON parsing failed for list_action response. Error: " << e.what() + << ". Invalid JSON data received: " << data->string() << std::endl; + } + } else { + std::cout << "call list_action faile!" << std::endl; + } + }); + return; + } + + const std::string fun = a.get("fun"); + const std::string data = a.get("data"); + if (fun.length() == 0) { + std::cout << "fun is empty! cli exec -f -d " << std::endl; + return; + } + + StackFlows::pzmq Context(rpc_socket_path); + Context.call_rpc_action(fun, data, [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + if (data->size() > 0) { + std::cout << "" << data->string() << std::endl; + } else { + std::cout << "call ax650_ec_prox faile!" << std::endl; + } + }); +} + +void echo_fun(cmdline::parser &a) +{ + if (a.exist("button")) { + StackFlows::pzmq Context(pub_socket_path, ZMQ_SUB, + [](StackFlows::pzmq *_pzmq, const std::shared_ptr &data) { + std::cout << data->string() << std::endl; + }); + while (condition) { + usleep(100 * 1000); + } + exit(0); + } +} + +void signalHandler(int signum) +{ + condition = 0; + std::exit(0); // 这会调用全局对象的析构函数 +} + +struct call_fun { + std::string fun; + char flage; + std::string dec; + std::function fun_call; +}; + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + error_print_exit: + std::cout << R"help( +用法:git [--version] [--help] + <命令> [<参数>] + +这些是各种场合常见的 cli 命令: + +直接操作 ec (参见:cli help device) + device 直接执行一个操作,使用指令传递参数 + +自定义操作 ec (参见:git help exec) + exec 执行一个原始函数调用 + +订阅 prox 的通道数据(参见:git help echo) + echo 显示 prox 的通道数据 + +命令 'cli help -a' 和 'cli help -g' 显示可用的子命令和一些概念帮助。 +查看 'cli help <命令>' 或 'cli help <概念>' 以获取给定子命令或概念的 +帮助。 +)help" << std::endl; + return 0; + } + signal(SIGTERM, signalHandler); + signal(SIGINT, signalHandler); + rpc_socket_path = getenv("AX650C_EC_PROXY_RPC_SOCKET"); + if (rpc_socket_path == NULL) { + rpc_socket_path = "ipc:///tmp/rpc.ec_prox"; + } + pub_socket_path = getenv("AX650C_EC_PROXY_PUB_SOCKET"); + if (pub_socket_path == NULL) { + pub_socket_path = "ipc:///tmp/llm/ec_prox.event.socket"; + } + if (std::string(argv[1]) == "device") { + cmdline::parser a; + // clang-format off + std::list cmd_list = { + {"rgb", 'r', "Get or set RGB LED display mode", std::bind(&info_fun, std::placeholders::_1, "rgb_set_mode", "rgb_get_mode")}, + {"poweron_time", 0, "Get or set the scheduled power-on time", std::bind(&info_fun, std::placeholders::_1, "poweron_set_time", "poweron_get_time")}, + {"poweroff_time", 0, "Get or set the scheduled power-off time", std::bind(&info_fun, std::placeholders::_1, "poweroff_set_time", "poweroff_get_time")}, + {"rgb_size", 0, "Get or set the number of RGB LEDs in the array", std::bind(&info_fun, std::placeholders::_1, "rgb_set_size", "rgb_get_size")}, + {"rgb_get_color", 0, "Get the color value of a specific RGB LED", std::bind(&info_fun, std::placeholders::_1, "rgb_get_color", "")}, + {"rgb_set_color", 0, "Set color for a specific RGB LED, example: cli --rgb_set_color -d '{\"rgb_index\":0,\"rgb_color\":255}'", std::bind(&info_fun, std::placeholders::_1, "rgb_set_color", "")}, + {"fan", 'f', "Get or set the fan PWM duty cycle", std::bind(&info_fun, std::placeholders::_1, "fan_set_pwm", "fan_get_pwm")}, + {"fanspeed", 'F', "Get the current fan rotation speed in RPM", std::bind(&info_fun, std::placeholders::_1, "", "fan_get_speed")}, + {"pd_power_info", 0, "Get the USB PD power delivery information", std::bind(&info_fun, std::placeholders::_1, "", "pd_power_info")}, + {"board", 'B', "Get the board power consumption information", std::bind(&info_fun, std::placeholders::_1, "", "board_get_power_info")}, + {"ip_eth0", 0, "Get or set the IP address for Ethernet interface eth0", std::bind(&info_fun, std::placeholders::_1, "eth0_ip_set", "eth0_ip_get")}, + {"ip_eth1", 0, "Get or set the IP address for Ethernet interface eth1", std::bind(&info_fun, std::placeholders::_1, "eth1_ip_set", "eth1_ip_get")}, + {"ip_wlan", 0, "Get or set the IP address for wireless LAN interface", std::bind(&info_fun, std::placeholders::_1, "wlan_ip_set", "wlan_ip_get")}, + {"lcd", 'l', "Get or set the LCD display mode", std::bind(&info_fun, std::placeholders::_1, "lcd_set_mode", "lcd_get_mode")}, + {"lcd_ram", 0, "Set the LCD RAM buffer data directly", std::bind(&info_fun, std::placeholders::_1, "lcd_set_ram", "")}, + {"vddcpu", 'c', "Get or set the CPU core voltage (VDD)", std::bind(&info_fun, std::placeholders::_1, "vddcpu_set", "vddcpu_get")}, + {"modbus_speed", 0, "Get or set the Modbus communication baud rate", std::bind(&info_fun, std::placeholders::_1, "modbus_set_speed", "modbus_get_speed")}, + {"ext_power", 'p', "Control the external power supply output", std::bind(&info_fun, std::placeholders::_1, "ext_power", "")}, + {"board_power", 'b', "Control the main board power switch", std::bind(&info_fun, std::placeholders::_1, "board_power", "")}, + {"pcie0", 0, "Set the PCIe slot 0 switch on/off state", std::bind(&info_fun, std::placeholders::_1, "pcie0_set_switch", "")}, + {"pcie1", 0, "Set the PCIe slot 1 switch on/off state", std::bind(&info_fun, std::placeholders::_1, "pcie1_set_switch", "")}, + {"gl3510_reset", 0, "Reset the GL3510 USB hub controller", std::bind(&info_fun, std::placeholders::_1, "gl3510_reset", "")}, + {"usbds1_big", 0, "Set the high-power mode for USB downstream port 1", std::bind(&info_fun, std::placeholders::_1, "usbds1_set_big_power", "")}, + {"usbds2_big", 0, "Set the high-power mode for USB downstream port 2", std::bind(&info_fun, std::placeholders::_1, "usbds2_set_big_power", "")}, + {"usbds1", 0, "Set the switch on/off state for USB downstream port 1", std::bind(&info_fun, std::placeholders::_1, "usbds1_set_switch", "")}, + {"usbds2", 0, "Set the switch on/off state for USB downstream port 2", std::bind(&info_fun, std::placeholders::_1, "usbds2_set_switch", "")}, + {"usbds3", 0, "Set the switch on/off state for USB downstream port 3", std::bind(&info_fun, std::placeholders::_1, "usbds3_set_switch", "")}, + {"hdmi_loop_en", 0, "Switch HDMI OUT port input source between IN and AX8850", std::bind(&info_fun, std::placeholders::_1, "usbds3_set_switch", "")}, + {"grove_uart", 0, "Set the switch on/off state for Grove UART interface", std::bind(&info_fun, std::placeholders::_1, "grove_uart_set_switch", "")}, + {"grove_iic", 0, "Set the switch on/off state for Grove I2C interface", std::bind(&info_fun, std::placeholders::_1, "grove_iic_set_switch", "")}, + {"flash_switch", 0, "Save switch configuration to flash memory (stores coil registers 4-14)", std::bind(&info_set_fun, std::placeholders::_1, "flash_save_switch", "{\"data\":1}")}, + {"flash_value", 0, "Save value configuration to flash memory (stores holding registers 1, 2, 11, 12, 14, 16-21)", std::bind(&info_set_fun, std::placeholders::_1, "flash_save_value", "{\"data\":1}")}, + {"poweroff", 0, "Trigger the system power-off sequence", std::bind(&info_fun, std::placeholders::_1, "poweroff", "")}, + {"lcd_brightness", 0, "Get or set the LCD backlight brightness level", std::bind(&info_fun, std::placeholders::_1, "lcd_set_brightness", "lcd_get_brightness")}, + {"lcd_putc", 'P', "Output a character or string to the LCD display", lcd_putc_fun}, + {"i2c_set_reg", 0, "Write a value to an I2C device register", std::bind(&info_fun, std::placeholders::_1, "i2c_set_reg", "i2c_set_reg")}, + {"i2c_get_reg", 0, "Read a value from an I2C device register", std::bind(&info_fun, std::placeholders::_1, "i2c_get_reg", "i2c_get_reg")}, + {"ec_button_head_event", 0, "Get or set the event handler for EC head button press", std::bind(&info_fun, std::placeholders::_1, "ec_button_set_head_event", "ec_button_get_head_event")}, + {"soc_button_head_event", 0, "Get or set the event handler for SoC head button press", std::bind(&info_fun, std::placeholders::_1, "soc_button_set_head_event", "soc_button_get_head_event")}, + {"ec_button_lcd_event", 0, "Get or set the event handler for EC LCD button press", std::bind(&info_fun, std::placeholders::_1, "ec_button_set_lcd_event", "ec_button_get_lcd_event")}, + {"fun_auto", 0, "Get or set the automatic control mode for the proxy service", std::bind(&info_fun, std::placeholders::_1, "fun_set_auto", "fun_get_auto")}, + {"ec_modbus_set_bit", 0, "Set a specific bit in the EC Modbus coil register", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_set_bit", "ec_modbus_set_bit")}, + {"ec_modbus_get_bit", 0, "Get a specific bit value from the EC Modbus coil register", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_bit", "ec_modbus_get_bit")}, + {"ec_modbus_input_bits", 0, "Read the EC Modbus discrete input bits status", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_input_bits", "ec_modbus_get_input_bits")}, + {"ec_modbus_input_registers", 0, "Read the EC Modbus input registers values", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_input_registers", "ec_modbus_get_input_registers")}, + {"ec_modbus_set_hold_registers", 0, "Write values to the EC Modbus holding registers", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_set_hold_registers", "ec_modbus_set_hold_registers")}, + {"ec_modbus_get_hold_registers", 0, "Read values from the EC Modbus holding registers", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_hold_registers", "ec_modbus_get_hold_registers")}, + + {"pcie0_exists", 0, "Check if a PCIe device is present in slot 0", std::bind(&info_fun, std::placeholders::_1, "", "pcie0_exists")}, + {"pcie1_exists", 0, "Check if a PCIe device is present in slot 1", std::bind(&info_fun, std::placeholders::_1, "", "pcie1_exists")}, + {"V3_3_good", 0, "Check if the 3.3V power rail is within normal operating range", std::bind(&info_fun, std::placeholders::_1, "", "V3_3_good")}, + {"V1_8_good", 0, "Check if the 1.8V power rail is within normal operating range", std::bind(&info_fun, std::placeholders::_1, "", "V1_8_good")}, + {"head_button", 0, "Get the current state of the head button (pressed/released)", std::bind(&info_fun, std::placeholders::_1, "", "head_button")}, + {"lcd_button", 0, "Get the current state of the LCD button (pressed/released)", std::bind(&info_fun, std::placeholders::_1, "", "lcd_button")}, + {"version", 0, "Get the EC firmware version information", std::bind(&info_fun, std::placeholders::_1, "", "version")}, + + }; + // clang-format on + for (auto &cmd : cmd_list) { + a.add(cmd.fun, cmd.flage, cmd.dec); + } + a.add("data", 'd', "call param", false); + a.add("DataRaw", 'D', "call param raw", false); + a.parse_check(argc, argv); + int not_find_index = 1; + for (auto &cmd : cmd_list) { + if (a.exist(cmd.fun)) { + cmd.fun_call(a); + not_find_index = 0; + } + } + if (not_find_index) { + std::cout << a.usage() << std::endl; + } + } else if (std::string(argv[1]) == "exec") { + cmdline::parser a; + a.add("list", 'l', "list call function"); + a.add("fun", 'f', "call ax650c_ec_proxy function", false); + a.add("data", 'd', "call ax650c_ec_proxy function input data", false); + a.parse_check(argc, argv); + exec_fun(a); + } else if (std::string(argv[1]) == "echo") { + cmdline::parser a; + a.add("button", 'b', "button event"); + a.parse_check(argc, argv); + echo_fun(a); + std::cout << a.usage() << std::endl; + return 0; + } else { + goto error_print_exit; + } + + return 0; +} diff --git a/projects/ec_proxy/main_ec_proxy/SConstruct b/projects/ec_proxy/main_ec_proxy/SConstruct new file mode 100644 index 0000000..d42d34e --- /dev/null +++ b/projects/ec_proxy/main_ec_proxy/SConstruct @@ -0,0 +1,40 @@ +import os + +Import('env') +with open(env['PROJECT_TOOL_S']) as f: + exec(f.read()) + + +SRCS = [] +INCLUDE = [ADir('../include')] +PRIVATE_INCLUDE = [] +REQUIREMENTS = ['pthread', 'utilities', 'modbus_component', 'zmq'] +STATIC_LIB = [] +DYNAMIC_LIB = [] +DEFINITIONS = [] +DEFINITIONS_PRIVATE = [] +LDFLAGS = [] +LINK_SEARCH_PATH = [] +STATIC_FILES = [] + +LINK_SEARCH_PATH += [ADir('../static')] + +LDFLAGS+=['-Wl,-rpath=/opt/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib/gcc-10.3', '-Wl,-rpath=/opt/lib', '-Wl,-rpath=/opt/usr/lib', '-Wl,-rpath=./'] + + + +SRCS = [AFile('src/ec_proxy_main.cpp')] +env['COMPONENTS'].append({'target':'ec_proxy-1.0', + 'SRCS':SRCS, + 'INCLUDE':INCLUDE, + 'PRIVATE_INCLUDE':PRIVATE_INCLUDE, + 'REQUIREMENTS':REQUIREMENTS, + 'STATIC_LIB':STATIC_LIB, + 'DYNAMIC_LIB':DYNAMIC_LIB, + 'DEFINITIONS':DEFINITIONS, + 'DEFINITIONS_PRIVATE':DEFINITIONS_PRIVATE, + 'LDFLAGS':LDFLAGS, + 'LINK_SEARCH_PATH':LINK_SEARCH_PATH, + 'STATIC_FILES':STATIC_FILES, + 'REGISTER':'project' + }) diff --git a/projects/ec_proxy/main_ec_proxy/src/ec_proxy_main.cpp b/projects/ec_proxy/main_ec_proxy/src/ec_proxy_main.cpp new file mode 100644 index 0000000..00202fb --- /dev/null +++ b/projects/ec_proxy/main_ec_proxy/src/ec_proxy_main.cpp @@ -0,0 +1,1262 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "pzmq.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// #include "cmdline.hpp" +#include +#include +#include "json.hpp" +#include "sample_log.h" +#include + +#include "tbl_yield.h" + +int main_exit_flage = 0; +static void __sigint(int iSigNo) +{ + printf("llm_ec_prox will be exit!\n"); + main_exit_flage = 1; +} + +#define CONFIG_AUTO_SET(obj, key) \ + if (config_body.contains(#key)) \ + mode_config_.key = config_body[#key]; \ + else if (obj.contains(#key)) \ + mode_config_.key = obj[#key]; + +using namespace StackFlows; + +#include +#include +#include +#include +#include +#include +#include + +typedef struct ifconfig_s { + char name[128]; + char ip[16]; + char mask[16]; + char broadcast[16]; + char mac[20]; +} ifconfig_t; + +int ifconfig(std::vector &ifcs) +{ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + return -10; + } + + struct ifconf ifc; + char buf[1024]; + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + + int iRet = ioctl(sock, SIOCGIFCONF, &ifc); + if (iRet != 0) { + close(sock); + return iRet; + } + + int cnt = ifc.ifc_len / sizeof(struct ifreq); + // printf("ifc.size=%d\n", cnt); + if (cnt == 0) { + close(sock); + return -20; + } + + struct ifreq ifr; + ifcs.clear(); + ifconfig_t tmp; + for (int i = 0; i < cnt; ++i) { + // name + strcpy(ifr.ifr_name, ifc.ifc_req[i].ifr_name); + // printf("name: %s\n", ifr.ifr_name); + strncpy(tmp.name, ifr.ifr_name, sizeof(tmp.name)); + // flags + // iRet = ioctl(sock, SIOCGIFFLAGS, &ifr); + // short flags = ifr.ifr_flags; + // addr + iRet = ioctl(sock, SIOCGIFADDR, &ifr); + struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr; + char *ip = inet_ntoa(addr->sin_addr); + // printf("ip: %s\n", ip); + strncpy(tmp.ip, ip, sizeof(tmp.ip)); + // netmask + iRet = ioctl(sock, SIOCGIFNETMASK, &ifr); + addr = (struct sockaddr_in *)&ifr.ifr_netmask; + char *netmask = inet_ntoa(addr->sin_addr); + // printf("netmask: %s\n", netmask); + strncpy(tmp.mask, netmask, sizeof(tmp.mask)); + // broadaddr + iRet = ioctl(sock, SIOCGIFBRDADDR, &ifr); + addr = (struct sockaddr_in *)&ifr.ifr_broadaddr; + char *broadaddr = inet_ntoa(addr->sin_addr); + // printf("broadaddr: %s\n", broadaddr); + strncpy(tmp.broadcast, broadaddr, sizeof(tmp.broadcast)); + // hwaddr + iRet = ioctl(sock, SIOCGIFHWADDR, &ifr); + snprintf(tmp.mac, sizeof(tmp.mac), "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)ifr.ifr_hwaddr.sa_data[0], + (unsigned char)ifr.ifr_hwaddr.sa_data[1], (unsigned char)ifr.ifr_hwaddr.sa_data[2], + (unsigned char)ifr.ifr_hwaddr.sa_data[3], (unsigned char)ifr.ifr_hwaddr.sa_data[4], + (unsigned char)ifr.ifr_hwaddr.sa_data[5]); + // printf("mac: %s\n", tmp.mac); + // printf("\n"); + + if (strcmp(tmp.ip, "0.0.0.0") == 0 || strcmp(tmp.ip, "127.0.0.1") == 0 || + strcmp(tmp.mac, "00:00:00:00:00:00") == 0) { + continue; + } + + ifcs.push_back(tmp); + } + + close(sock); + return 0; +} + +class llm_ec_prox { +private: + std::string ec_prox_event_channel = "ipc:///tmp/llm/ec_prox.event.socket"; + modbus_t *modbus_ctx; + std::mutex modbus_mtx_; + typedef struct { + int reg_index; // 16位寄存器索引 + int byte_offset; // 在寄存器中字节偏移(0-低字节,1-高字节) + } RegPosition; + std::unique_ptr pub_ctx_; + std::unique_ptr rpc_ctx_; + std::string StackFlow_pack_ERROR_temp = + R"format({{ "created":{},"error":{{"code":{},"message":{}}},"object":{},"request_id":{},"work_id":"{}" }})format"; + std::string StackFlow_pack_temp = + R"format({{ "created":{},"data":{},"object":{},"request_id":{},"work_id":"{}" }})format"; + std::string StackFlow_pack_all_temp = + R"format({{ "created":{},"data":{},"error":{{"code":{},"message":{}}},"object":{},"request_id":{},"work_id":"{}" }})format"; + std::string return_success_result(std::string work_id, std::string data, std::string tmp = "", + std::string object = "\"\"", std::string request_id = "\"\"") + { + return fmt::format(tmp.empty() ? StackFlow_pack_temp : tmp, std::time(nullptr), data, object, request_id, + work_id); + } + std::string return_err_result(std::string work_id, int code, std::string data, std::string tmp = "", + std::string object = "\"\"", std::string request_id = "\"\"") + { + return fmt::format(tmp.empty() ? StackFlow_pack_ERROR_temp : tmp, std::time(nullptr), code, data, object, + request_id, work_id); + } + static llm_ec_prox *self; + // 输入寄存器读 + std::string get_input_registers(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, + int reg) + { + uint16_t tab_reg; + int rc = modbus_read_input_registers(modbus_ctx, reg, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + // 离散输入读 + std::string get_input_bit(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + uint8_t tab_reg; + int rc = modbus_read_input_bits(modbus_ctx, reg, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + // 线圈读 + std::string get_bit(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + uint8_t tab_reg; + int rc = modbus_read_bits(modbus_ctx, reg, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + // 线圈写 + std::string set_bit(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + int tab_reg; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + tab_reg = fan_data["data"]; + } catch (...) { + tab_reg = 1; + } + int rc = modbus_write_bit(modbus_ctx, reg, tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "write failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + // 保持寄存器读 + std::string get_register(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + uint16_t tab_reg; + int rc = modbus_read_registers(modbus_ctx, reg, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + + // 保持寄存器写 + std::string set_register(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + uint16_t tab_reg; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + tab_reg = (int)fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + int rc = modbus_write_registers(modbus_ctx, reg, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "write failed") : return_success_result(__func__, "\"ok\""); + } + + std::string rgb_set_register(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + uint16_t tab_reg; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + tab_reg = (int)fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + rgb_old_mode = tab_reg; + int rc = modbus_write_registers(modbus_ctx, reg, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "write failed") : return_success_result(__func__, "\"ok\""); + } + + int fun_pwm_val = 200; + std::string fun_set_pwm(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + uint16_t tab_reg; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + fun_pwm_val = (int)fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + if (fun_pwm_val <= 100) { + tab_reg = fun_pwm_val; + int rc = modbus_write_registers(modbus_ctx, reg, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "write failed") + : return_success_result(__func__, "\"ok\""); + } else { + return return_err_result(__func__, -1, "fun_pwm is over 100, will enable auto fun control"); + } + } + + std::string board_get_power_info(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + // mbpoll -m rtu -b 115200 -P none -a 1 -r 1 -t 3 -c 13 -l 10 /dev/ttyS3 + uint16_t tab_reg[12]; + int rc = modbus_read_input_registers(modbus_ctx, 0, 12, tab_reg); + if (rc == -1) { + return return_err_result(__func__, -1, "read failed"); + } else { + return return_success_result( + __func__, + fmt::format( + R"format({{ "pcie0_mv": {} , "pcie0_ma": {} , "pcie1_mv": {} , "pcie1_ma": {} , "usb1_mv": {} , "usb1_ma": {} , "usb2_mv": {} , "usb2_ma": {} , "INVDD_mv": {} , "INVDD_ma": {} , "EXTVDD_mv": {} , "EXTVDD_ma": {} }})format", + tab_reg[0], tab_reg[1], tab_reg[2], tab_reg[3], tab_reg[4], tab_reg[5], tab_reg[6], tab_reg[7], + tab_reg[8], tab_reg[9], tab_reg[10], tab_reg[11])); + } + } + + uint16_t rgb888_to_rgb565(uint32_t rgb888) + { + // 提取红、绿、蓝分量 + uint8_t r = (rgb888 >> 16) & 0xFF; + uint8_t g = (rgb888 >> 8) & 0xFF; + uint8_t b = rgb888 & 0xFF; + + // RGB888到RGB565转换 + uint16_t r5 = (r >> 3) & 0x1F; // 取高5位 + uint16_t g6 = (g >> 2) & 0x3F; // 取高6位 + uint16_t b5 = (b >> 3) & 0x1F; // 取高5位 + + // 拼接输出(高位到低位:红5、绿6、蓝5) + uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5; + + return rgb565; + } + uint32_t rgb565_to_rgb888(uint16_t rgb565) + { + // 提取各通道分量 + uint8_t r5 = (rgb565 >> 11) & 0x1F; // 高5位红 + uint8_t g6 = (rgb565 >> 5) & 0x3F; // 中6位绿 + uint8_t b5 = rgb565 & 0x1F; // 低5位蓝 + + // 将5-6位扩展成8位 + uint8_t r8 = (r5 << 3) | (r5 >> 2); // 5位扩展到8位 + uint8_t g8 = (g6 << 2) | (g6 >> 4); // 6位扩展到8位 + uint8_t b8 = (b5 << 3) | (b5 >> 2); // 5位扩展到8位 + + // 拼装成RGB888的0x00RRGGBB格式 + uint32_t rgb888 = (r8 << 16) | (g8 << 8) | b8; + return rgb888; + } + std::string rgb_get_color(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int rgb_index = 0; + int rgb_color = 0; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + rgb_index = (int)fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint16_t tab_reg; + int rc = modbus_read_registers(modbus_ctx, rgb_index + 63, 1, &tab_reg); + if (rc == -1) { + return return_err_result(__func__, -1, "read failed"); + } else { + rgb_color = rgb565_to_rgb888(tab_reg); + return return_success_result(__func__, fmt::format("{}", rgb_color)); + } + } + + std::string rgb_set_color(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int rgb_index = 0; + int rgb_color = 0; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + rgb_index = (int)fan_data["data"]["rgb_index"]; + rgb_color = (int)fan_data["data"]["rgb_color"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + if (rgb_index == -1) { + uint16_t tab_reg = rgb888_to_rgb565(rgb_color); + uint16_t tab_regs[64]; + uint16_t tab_reg_size; + for (int i = 0; i < 64; i++) tab_regs[i] = tab_reg; + int rc = modbus_read_registers(modbus_ctx, 10, 1, &tab_reg_size); + if (rc == -1) return return_err_result(__func__, -1, "write failed"); + rc = modbus_write_registers(modbus_ctx, 63, tab_reg_size, tab_regs); + return rc == -1 ? return_err_result(__func__, -1, "write failed") + : return_success_result(__func__, "\"ok\""); + } else { + uint16_t tab_reg = rgb888_to_rgb565(rgb_color); + int rc = modbus_write_registers(modbus_ctx, rgb_index + 63, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "write failed") + : return_success_result(__func__, "\"ok\""); + } + } + + std::string lcd_set_ram(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + std::string rgb_ram_base64; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + rgb_ram_base64 = fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint16_t *rgb_ram = (uint16_t *)malloc(((BASE64_DECODE_OUT_SIZE(rgb_ram_base64.length()) / 2) + 1) * 2); + int ram_size = base64_decode(rgb_ram_base64.c_str(), rgb_ram_base64.length(), (uint8_t *)rgb_ram); + ram_size = ram_size > 1870 ? 1870 : ram_size; + int rc = modbus_write_registers(modbus_ctx, 255, ram_size / 2, rgb_ram); + free(rgb_ram); + return rc == -1 ? return_err_result(__func__, -1, "write failed") : return_success_result(__func__, "\"ok\""); + } + + std::string ip_get(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int port) + { + uint32_t value = 0; + uint16_t tab_reg[2] = {0}; + + int rc = modbus_read_registers(modbus_ctx, 15 + port * 2, 2, tab_reg); + if (rc == -1) { + return return_err_result(__func__, -1, "read failed"); + } else { + for (int i = 0; i < 2; i++) tab_reg[i] = ntohs(tab_reg[i]); + value = ((uint32_t)tab_reg[0] << 16) | tab_reg[1]; + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &value, ip_str, INET_ADDRSTRLEN); + return return_success_result(__func__, fmt::format("\"{}\"", ip_str)); + } + } + std::string ip_set(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int port) + { + uint32_t value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + std::string ip_str = fan_data["data"]; + if (inet_pton(AF_INET, ip_str.c_str(), &value) != 1) { + return std::string("Error: Invalid IP address\n"); + } + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint16_t tab_reg[2]; + tab_reg[0] = (value >> 16) & 0xFFFF; + tab_reg[1] = value & 0xFFFF; + for (int i = 0; i < 2; i++) tab_reg[i] = htons(tab_reg[i]); + int rc = modbus_write_registers(modbus_ctx, 15 + port * 2, 2, tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "write failed") : return_success_result(__func__, "\"ok\""); + } + + std::string i2c_get_reg(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int addr = 0; + int reg = 0; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + addr = (int)fan_data["data"]["addr"]; + reg = (int)fan_data["data"]["reg"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed;-D {\"data\":{\"addr\":1,\"reg\":1}} "); + } + uint16_t tab_reg = (addr & 0xff) << 8 | (reg & 0xff); + int rc = modbus_write_registers(modbus_ctx, 7, 1, &tab_reg); + rc = modbus_read_registers(modbus_ctx, 8, 2, &tab_reg); + if (rc == -1) { + return return_err_result(__func__, -1, "read failed"); + } else { + return return_success_result(__func__, fmt::format("{}", tab_reg)); + } + } + + std::string i2c_set_reg(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int addr = 0; + int reg = 0; + int value = 0; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + addr = (int)fan_data["data"]["addr"]; + reg = (int)fan_data["data"]["reg"]; + value = (int)fan_data["data"]["value"]; + } catch (...) { + return return_err_result(__func__, -2, + "json decode failed ;-D {\"data\":{\"addr\":1,\"reg\":1,\"value\":1}} "); + } + uint16_t tab_reg = (addr & 0xff) << 8 | (reg & 0xff); + int rc = modbus_write_registers(modbus_ctx, 7, 1, &tab_reg); + tab_reg = value & 0xff; + rc = modbus_write_registers(modbus_ctx, 9, 2, &tab_reg); + if (rc == -1) { + return return_err_result(__func__, -1, "write failed"); + } else { + return return_success_result(__func__, "\"Ok\""); + } + } + + std::string poweron_time(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int reg) + { + if (reg == 0) { + uint32_t value = 0; + uint16_t tab_reg[2] = {0}; + + int rc = modbus_read_registers(modbus_ctx, 5, 2, tab_reg); + if (rc == -1) { + return return_err_result(__func__, -1, "read failed"); + } else { + value = ((uint32_t)tab_reg[0] << 16) | tab_reg[1]; + return return_success_result(__func__, fmt::format("{}", value)); + } + } else { + uint32_t value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + value = (int)fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint16_t tab_reg[2]; + tab_reg[0] = (value >> 16) & 0xFFFF; + tab_reg[1] = value & 0xFFFF; + int rc = modbus_write_registers(modbus_ctx, 5, 2, tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "write failed") + : return_success_result(__func__, "\"ok\""); + } + } + + int ec_button[4] = {0}; + std::string ec_fun_auto(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int set) + { + if (set == 0) { + return return_success_result(__func__, fmt::format("{}", ec_button[3])); + } else { + uint32_t value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + value = (int)fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + ec_button[3] = value; + return return_success_result(__func__, "\"ok\""); + } + } + + std::string ec_button_head(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int set) + { + if (set == 0) { + return return_success_result(__func__, fmt::format("{}", ec_button[0])); + } else { + uint32_t value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + value = (int)fan_data["data"]; + + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + ec_button[0] = value; + return return_success_result(__func__, "\"ok\""); + } + } + std::string soc_button_head(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int set) + { + if (set == 0) { + return return_success_result(__func__, fmt::format("{}", ec_button[2])); + } else { + uint32_t value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + value = (int)fan_data["data"]; + + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + ec_button[2] = value; + return return_success_result(__func__, "\"ok\""); + } + } + std::string ec_button_lcd(StackFlows::pzmq *_pzmq, const std::shared_ptr &data, int set) + { + if (set == 0) { + return return_success_result(__func__, fmt::format("{}", ec_button[1])); + } else { + uint32_t value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + value = (int)fan_data["data"]; + + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + ec_button[1] = value; + return return_success_result(__func__, "\"ok\""); + } + } + void set_modbus_speed(int modbus_speed) + { + uint16_t tab_reg[2]; + tab_reg[0] = (modbus_speed >> 16) & 0xFFFF; + tab_reg[1] = modbus_speed & 0xFFFF; + int rc = modbus_write_registers(modbus_ctx, 3, 2, tab_reg); + if (rc == -1) { + fprintf(stderr, "Write failed: %s\n", modbus_strerror(errno)); + std::exit(-1); + } + modbus_close(modbus_ctx); + modbus_free(modbus_ctx); + modbus_ctx = NULL; + usleep(100 * 1000); + modbus_ctx = modbus_new_rtu("/dev/ttyS3", modbus_speed, 'N', 8, 1); + if (modbus_ctx == NULL) { + fprintf(stderr, "Unable to create the context\n"); + std::exit(-1); + } + modbus_set_slave(modbus_ctx, 1); // 设置 Modbus 从站地址为1 + if (modbus_connect(modbus_ctx) == -1) { + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + modbus_free(modbus_ctx); + std::exit(-1); + } + _modbus_speed = modbus_speed; + } + int _modbus_speed = 921600; + + void _init_modbus() + { + modbus_ctx = modbus_new_rtu("/dev/ttyS3", 921600, 'N', 8, 1); + if (modbus_ctx == NULL) { + fprintf(stderr, "Unable to create the context\n"); + std::exit(-1); + } + modbus_set_slave(modbus_ctx, 1); // 设置 Modbus 从站地址为1 + if (modbus_connect(modbus_ctx) == -1) { + fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno)); + modbus_free(modbus_ctx); + std::exit(-1); + } + } + + std::string _None(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + return return_success_result(__func__, "\"None\""); + } + + std::string ec_modbus_get_bit(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int index; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + index = fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint8_t tab_reg; + int rc = modbus_read_bits(modbus_ctx, index, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + std::string ec_modbus_set_bit(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int index; + int value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + index = fan_data["data"]["index"]; + value = fan_data["data"]["value"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed ;-D {\"data\":{\"index\":1, \"value\":1}}"); + } + int rc = modbus_write_bit(modbus_ctx, index, value); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", value)); + } + + std::string ec_modbus_get_input_bits(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int index; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + index = fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint8_t tab_reg; + int rc = modbus_read_input_bits(modbus_ctx, index, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + std::string ec_modbus_get_input_registers(StackFlows::pzmq *_pzmq, + const std::shared_ptr &data) + { + int index; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + index = fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint16_t tab_reg; + int rc = modbus_read_input_registers(modbus_ctx, index, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + + std::string ec_modbus_get_hold_registers(StackFlows::pzmq *_pzmq, + const std::shared_ptr &data) + { + int index; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + index = fan_data["data"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed"); + } + uint16_t tab_reg; + int rc = modbus_read_registers(modbus_ctx, index, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + std::string ec_modbus_set_hold_registers(StackFlows::pzmq *_pzmq, + const std::shared_ptr &data) + { + int index; + int value; + try { + nlohmann::json fan_data = nlohmann::json::parse(data->string()); + index = fan_data["data"]["index"]; + value = fan_data["data"]["value"]; + } catch (...) { + return return_err_result(__func__, -2, "json decode failed ;-D {\"data\":{\"index\":1, \"value\":1}}"); + } + uint16_t tab_reg = value; + int rc = modbus_write_registers(modbus_ctx, index, 1, &tab_reg); + return rc == -1 ? return_err_result(__func__, -1, "read failed") + : return_success_result(__func__, fmt::format("{}", tab_reg)); + } + + std::string pd_info(StackFlows::pzmq *_pzmq, const std::shared_ptr &data) + { + int addr = 8; + int reg = 0; + uint16_t tab_reg = (addr & 0xff) << 8 | (reg & 0xff); + int rc = modbus_write_registers(modbus_ctx, 7, 1, &tab_reg); + rc = modbus_read_registers(modbus_ctx, 8, 2, &tab_reg); + std::string voltage_map[] = {"Unattached", "5 V", "9 V", "12 V", "15 V", "18 V", "20 V", "NULL", + "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL", "NULL"}; + std::string current_map[] = {"0.5 A", "0.7 A", "1 A", "1.25 A", "1.5 A", "1.75 A", "2 A", "2.25 A", + "2.5 A", "2.75 A", "3 A", "3.25 A", "3.5 A", "4 A", "4.5 A", "5 A"}; + if (rc == -1) { + return return_err_result(__func__, -1, "read failed"); + } else { + return return_success_result( + __func__, fmt::format("{{\"voltage\":\"{}\",\"current\":\"{}\"}}", voltage_map[(tab_reg >> 4) & 0x0f], + current_map[tab_reg & 0x0F])); + } + } + +public: + llm_ec_prox() + { + // setup("", "audio.play", "{\"None\":\"None\"}"); + self = this; + // prob modbus speed + _init_modbus(); + _init_ec(); + pub_ctx_ = std::make_unique("ipc:///tmp/llm/ec_prox.event.socket", ZMQ_PUB); + rpc_ctx_ = std::make_unique("ipc:///tmp/rpc.ec_prox", ZMQ_RPC_FUN); + + // clang-format off + rpc_ctx_->register_rpc_action("setup", std::bind(&llm_ec_prox::_None, this, std::placeholders::_1, std::placeholders::_2)); + rpc_ctx_->register_rpc_action("pause", std::bind(&llm_ec_prox::_None, this, std::placeholders::_1, std::placeholders::_2)); + rpc_ctx_->register_rpc_action("work", std::bind(&llm_ec_prox::_None, this, std::placeholders::_1, std::placeholders::_2)); + rpc_ctx_->register_rpc_action("exit", std::bind(&llm_ec_prox::_None, this, std::placeholders::_1, std::placeholders::_2)); + rpc_ctx_->register_rpc_action("link", std::bind(&llm_ec_prox::_None, this, std::placeholders::_1, std::placeholders::_2)); + rpc_ctx_->register_rpc_action("unlink", std::bind(&llm_ec_prox::_None, this, std::placeholders::_1, std::placeholders::_2)); + rpc_ctx_->register_rpc_action("taskinfo", std::bind(&llm_ec_prox::_None, this, std::placeholders::_1, std::placeholders::_2)); + +#define REGISTER_RPC_ACTION(name, func) \ + rpc_ctx_->register_rpc_action(name, [this](StackFlows::pzmq *_pzmq, const std::shared_ptr &data){ \ + std::unique_lock lock(this->modbus_mtx_); \ + return this->func(_pzmq, data); \ + }); + +#define REGISTER_RPC_ACTION_REG(name, func, reg) \ + rpc_ctx_->register_rpc_action(name, [this](StackFlows::pzmq *_pzmq, const std::shared_ptr &data){ \ + std::unique_lock lock(this->modbus_mtx_); \ + return this->func(_pzmq, data, reg); \ + }); + + + REGISTER_RPC_ACTION_REG("fan_get_speed", get_input_registers, 12); + REGISTER_RPC_ACTION_REG("version", get_input_registers, 13); + REGISTER_RPC_ACTION_REG("fan_get_pwm", get_register, 1); + REGISTER_RPC_ACTION_REG("rgb_get_mode", get_register, 11); + REGISTER_RPC_ACTION_REG("rgb_get_size", get_register, 10); + REGISTER_RPC_ACTION_REG("lcd_get_mode", get_register, 13); + REGISTER_RPC_ACTION_REG("lcd_get_brightness", get_register, 12); + REGISTER_RPC_ACTION_REG("poweroff_get_time", get_register, 0); + + REGISTER_RPC_ACTION_REG("fan_set_pwm", fun_set_pwm, 1); + REGISTER_RPC_ACTION_REG("rgb_set_mode", rgb_set_register, 11); + REGISTER_RPC_ACTION_REG("rgb_set_size", set_register, 10); + REGISTER_RPC_ACTION_REG("lcd_set_mode", set_register, 13); + REGISTER_RPC_ACTION_REG("lcd_set_brightness", set_register, 12); + REGISTER_RPC_ACTION_REG("poweroff_set_time", set_register, 0); + + REGISTER_RPC_ACTION_REG("lcd_putc", set_register, 14); + + + REGISTER_RPC_ACTION_REG("vddcpu_get", get_register, 2); + REGISTER_RPC_ACTION_REG("vddcpu_set", set_register, 2); + + REGISTER_RPC_ACTION("rgb_get_color", rgb_get_color); + REGISTER_RPC_ACTION("rgb_set_color", rgb_set_color); + REGISTER_RPC_ACTION("board_get_power_info", board_get_power_info); + REGISTER_RPC_ACTION_REG("eth0_ip_get", ip_get, 0); + REGISTER_RPC_ACTION_REG("eth0_ip_set", ip_set, 0); + REGISTER_RPC_ACTION_REG("eth1_ip_get", ip_get, 1); + REGISTER_RPC_ACTION_REG("eth1_ip_set", ip_set, 1); + REGISTER_RPC_ACTION_REG("wlan_ip_get", ip_get, 2); + REGISTER_RPC_ACTION_REG("wlan_ip_set", ip_set, 2); + + + + REGISTER_RPC_ACTION("lcd_set_ram", lcd_set_ram); + REGISTER_RPC_ACTION_REG("poweron_get_time", poweron_time, 0); + REGISTER_RPC_ACTION_REG("poweron_set_time", poweron_time, 1); + + //将下面的REGISTER_RPC_ACTION转换成REGISTER_RPC_ACTION_REG,注意查寻相关寄存器 + REGISTER_RPC_ACTION_REG("ext_power", set_bit, 0); + REGISTER_RPC_ACTION_REG("board_poweroff", set_bit, 1); + REGISTER_RPC_ACTION_REG("pcie0_set_switch", set_bit, 4); + REGISTER_RPC_ACTION_REG("pcie1_set_switch", set_bit, 5); + REGISTER_RPC_ACTION_REG("gl3510_reset", set_bit, 6); + REGISTER_RPC_ACTION_REG("usbds1_set_big_power", set_bit, 7); + REGISTER_RPC_ACTION_REG("usbds2_set_big_power", set_bit, 8); + REGISTER_RPC_ACTION_REG("usbds1_set_switch", set_bit, 9); + REGISTER_RPC_ACTION_REG("usbds2_set_switch", set_bit, 10); + REGISTER_RPC_ACTION_REG("usbds3_set_switch", set_bit, 11); + REGISTER_RPC_ACTION_REG("grove_iic_set_switch", set_bit, 12); + REGISTER_RPC_ACTION_REG("grove_uart_set_switch", set_bit, 13); + REGISTER_RPC_ACTION_REG("flash_save_switch", set_bit, 14); + REGISTER_RPC_ACTION_REG("flash_save_value", set_bit, 15); + REGISTER_RPC_ACTION_REG("poweroff", set_bit, 16); + + REGISTER_RPC_ACTION("i2c_set_reg", i2c_set_reg); + REGISTER_RPC_ACTION("i2c_get_reg", i2c_get_reg); + + ec_button[2] = 1; + REGISTER_RPC_ACTION_REG("ec_button_get_head_event", ec_button_head, 0); + REGISTER_RPC_ACTION_REG("ec_button_set_head_event", ec_button_head, 1); + REGISTER_RPC_ACTION_REG("ec_button_get_lcd_event", ec_button_lcd, 0); + REGISTER_RPC_ACTION_REG("ec_button_set_lcd_event", ec_button_lcd, 1); + REGISTER_RPC_ACTION_REG("soc_button_get_head_event", soc_button_head, 0); + REGISTER_RPC_ACTION_REG("soc_button_set_head_event", soc_button_head, 1); + ec_button[3] = 1; + REGISTER_RPC_ACTION_REG("fun_get_auto", ec_fun_auto, 0); + REGISTER_RPC_ACTION_REG("fun_set_auto", ec_fun_auto, 1); + + + REGISTER_RPC_ACTION("ec_modbus_get_bit", ec_modbus_get_bit); + REGISTER_RPC_ACTION("ec_modbus_set_bit", ec_modbus_set_bit); + + REGISTER_RPC_ACTION("ec_modbus_get_input_bits", ec_modbus_get_input_bits); + REGISTER_RPC_ACTION("ec_modbus_get_input_registers", ec_modbus_get_input_registers); + + REGISTER_RPC_ACTION("ec_modbus_get_hold_registers", ec_modbus_get_hold_registers); + REGISTER_RPC_ACTION("ec_modbus_set_hold_registers", ec_modbus_set_hold_registers); + + REGISTER_RPC_ACTION("pd_power_info", pd_info); + + REGISTER_RPC_ACTION_REG("pcie0_exists", get_input_bit, 0); + REGISTER_RPC_ACTION_REG("pcie1_exists", get_input_bit, 1); + REGISTER_RPC_ACTION_REG("V3_3_good", get_input_bit, 2); + REGISTER_RPC_ACTION_REG("V1_8_good", get_input_bit, 3); + REGISTER_RPC_ACTION_REG("head_button", get_input_bit, 4); + REGISTER_RPC_ACTION_REG("lcd_button", get_input_bit, 8); + + + + +#undef REGISTER_RPC_ACTION +#undef REGISTER_RPC_ACTION_REG + // clang-format on + } + // int setup(const std::string &work_id, const std::string &object, const std::string &data) override + // { + // send(std::string("None"), std::string("None"), std::string(""), unit_name_); + // return -1; + // } + static inline uint64_t get_uptime_ms(void) + { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); // 单调时钟:适合计算运行时/耗时 + return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL; + } + + void eth_ec_thread() + { + static uint32_t value_old[3] = {0}; + static int value_status[3] = {0}; + static uint64_t T_last = 0; + CORO_BEGIN(eth_ec); + for (;;) { + if (get_uptime_ms() - T_last < 2000) { + CORO_YIELD(eth_ec); + continue; + } + T_last = get_uptime_ms(); + memset(value_status, 0, sizeof(value_status)); + std::vector eth_info; + ifconfig(eth_info); + for (auto eth : eth_info) { + if (std::string(eth.name) == "eth0") { + value_status[0] = 1; + uint32_t value = inet_addr(eth.ip); + if (value == value_old[0]) { + continue; + } + value_old[0] = value; + uint16_t tab_reg[2]; + tab_reg[1] = (value >> 16) & 0xFFFF; + tab_reg[0] = value & 0xFFFF; + for (int i = 0; i < 2; i++) tab_reg[i] = htons(tab_reg[i]); + int rc = modbus_write_registers(modbus_ctx, 15 + 0 * 2, 2, tab_reg); + } + if (std::string(eth.name) == "eth1") { + value_status[1] = 1; + // std::cout << eth.name << " : " << eth.ip << std::endl; + uint32_t value = inet_addr(eth.ip); + if (value == value_old[1]) { + continue; + } + value_old[1] = value; + uint16_t tab_reg[2]; + tab_reg[1] = (value >> 16) & 0xFFFF; + tab_reg[0] = value & 0xFFFF; + for (int i = 0; i < 2; i++) tab_reg[i] = htons(tab_reg[i]); + int rc = modbus_write_registers(modbus_ctx, 15 + 1 * 2, 2, tab_reg); + // std::cout << eth.name << " :--- " << eth.ip << std::endl; + } + if (std::string(eth.name) == "wlan0") { + value_status[2] = 1; + uint32_t value = inet_addr(eth.ip); + if (value == value_old[2]) { + continue; + } + value_old[2] = value; + uint16_t tab_reg[2]; + tab_reg[1] = (value >> 16) & 0xFFFF; + tab_reg[0] = value & 0xFFFF; + for (int i = 0; i < 2; i++) tab_reg[i] = htons(tab_reg[i]); + int rc = modbus_write_registers(modbus_ctx, 15 + 2 * 2, 2, tab_reg); + } + } + if (value_status[0] == 0 && value_old[0] != 0) { + value_old[0] = 0; + uint16_t tab_reg[2] = {0}; + int rc = modbus_write_registers(modbus_ctx, 15 + 0 * 2, 2, tab_reg); + } + if (value_status[1] == 0 && value_old[1] != 0) { + value_old[1] = 0; + uint16_t tab_reg[2] = {0}; + int rc = modbus_write_registers(modbus_ctx, 15 + 1 * 2, 2, tab_reg); + } + if (value_status[2] == 0 && value_old[2] != 0) { + value_old[2] = 0; + uint16_t tab_reg[2] = {0}; + int rc = modbus_write_registers(modbus_ctx, 15 + 2 * 2, 2, tab_reg); + } + } + CORO_END(eth_ec); + } + + void buttons_ec_thread() + { + static int fd = 0; + struct input_event ev; + fd_set readfds; + int ret; + CORO_BEGIN(ec); + for (;;) { + if (ec_button[0]) { + uint8_t tab_reg[4]; + { + std::unique_lock lock(this->modbus_mtx_); + int rc = modbus_read_input_bits(modbus_ctx, 4, 4, tab_reg); + if (rc == -1) { + CORO_YIELD(ec); + continue; + } + } + if (tab_reg[1]) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":0,\"vale\":0}")); + } + if (tab_reg[2]) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":1,\"vale\":0}")); + } + if (tab_reg[3]) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":2,\"vale\":0}")); + } + } + if (ec_button[1]) { + uint8_t tab_reg[4]; + { + std::unique_lock lock(this->modbus_mtx_); + int rc = modbus_read_input_bits(modbus_ctx, 8, 4, tab_reg); + if (rc == -1) { + CORO_YIELD(ec); + continue; + } + } + if (tab_reg[1]) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":0,\"vale\":1}")); + } + if (tab_reg[2]) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":1,\"vale\":1}")); + } + if (tab_reg[3]) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":2,\"vale\":1}")); + } + } + CORO_YIELD(ec); + continue; + } + CORO_END(ec); + } + + void buttons_thread() + { + static int fd = 0; + struct input_event ev; + fd_set readfds; + int ret; + if (!ec_button[2]) { + usleep(100000); + return; + } + CORO_BEGIN(buttons); + for (;;) { + if (fd == 0) { + fd = open("/dev/input/by-path/platform-buttons-event", O_RDONLY); + if (fd < 0) { + fd = 0; + CORO_YIELD(buttons); + usleep(100000); + continue; + } + } + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + struct timeval tv = {0}; + tv.tv_usec = 100000; // 0.1秒 + ret = select(fd + 1, &readfds, NULL, NULL, &tv); + if (ret > 0) { + if (FD_ISSET(fd, &readfds)) { + ssize_t n = read(fd, &ev, sizeof(ev)); + if (n == (ssize_t)sizeof(ev)) { + // printf("type:%d, code:%d, value:%d\n", ev.type, ev.code, ev.value); + if (ev.value == 1 && ev.code == 204) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":1,\"vale\":204}")); + } + if (ev.value == 0 && ev.code == 204) { + pub_ctx_->send_data(return_success_result(__func__, "{\"code\":0,\"vale\":204}")); + } + } + } + } + if (ret < 0) { + close(fd); + fd = 0; + } + CORO_YIELD(buttons); + continue; + } + CORO_END(buttons); + } + void fun_thread() + { + static int fd = 0; + static uint16_t pwm_value_old; + if (!ec_button[3]) { + pwm_value_old = -1; + return; + } + CORO_BEGIN(fun); + for (;;) { + if (fun_pwm_val <= 100) { + CORO_YIELD(fun); + continue; + } + fd = open("/sys/class/thermal/cooling_device0/fan_cur_pwm", O_RDONLY); + if (fd < 0) { + fd = 0; + CORO_YIELD(fun); + continue; + } + char pwm_buff[32]; + ssize_t n = read(fd, pwm_buff, sizeof(pwm_buff)); + if (n > 0) { + uint16_t tab_reg = atoi(pwm_buff); + if (pwm_value_old != tab_reg) { + std::unique_lock lock(this->modbus_mtx_); + modbus_write_registers(modbus_ctx, 1, 1, &tab_reg); + pwm_value_old = tab_reg; + } + } + close(fd); + fd = 0; + CORO_YIELD(fun); + continue; + } + CORO_END(fun); + } + + void llm_kws_callback(pzmq *_pzmq, const std::shared_ptr &raw) + { + uint16_t tab_reg = 10; + ec_button[3] = 0; + modbus_write_registers(modbus_ctx, 1, 1, &tab_reg); + tab_reg = 5; + modbus_write_registers(modbus_ctx, 11, 1, &tab_reg); + } + void llm_asr_callback(pzmq *_pzmq, const std::shared_ptr &raw) + { + std::string data = raw->string(); + // std::cout << "llm_asr_callback data: " << data << std::endl; + std::size_t pos = data.find("\"finish\""); + // std::cout << "substr data: " << data.substr(pos, 16) << std::endl; + if ((pos != std::string::npos) && (data.substr(pos, 16).find("true") != std::string::npos)) { + ec_button[3] = 1; + uint16_t tab_reg = rgb_old_mode; + modbus_write_registers(modbus_ctx, 11, 1, &tab_reg); + // std::cout << "find end" << std::endl; + } + } + + std::unique_ptr llm_asr_pub_ctx_; + std::unique_ptr llm_kws_pub_ctx_; + void llm_status_thread() + { + const char *asr_pattern = "/tmp/llm/*.sock.asr.output_url"; + const char *kws_pattern = "/tmp/llm/*.sock.kws.output_url"; + CORO_BEGIN(llm_status); + for (;;) { + if (llm_asr_pub_ctx_ == nullptr) { + glob_t glob_result; + memset(&glob_result, 0, sizeof(glob_result)); + if (glob(asr_pattern, GLOB_TILDE, NULL, &glob_result) == 0) { + if (glob_result.gl_pathc > 0) { + llm_asr_pub_ctx_ = + std::make_unique(fmt::format("ipc://{}", glob_result.gl_pathv[0]), ZMQ_SUB, + std::bind(&llm_ec_prox::llm_asr_callback, this, + std::placeholders::_1, std::placeholders::_2)); + } + } + globfree(&glob_result); + } + if (llm_asr_pub_ctx_ == nullptr) { + CORO_YIELD(llm_status); + continue; + } + break; + } + for (;;) { + if (llm_kws_pub_ctx_ == nullptr) { + glob_t glob_result; + memset(&glob_result, 0, sizeof(glob_result)); + if (glob(kws_pattern, GLOB_TILDE, NULL, &glob_result) == 0) { + if (glob_result.gl_pathc > 0) { + llm_kws_pub_ctx_ = + std::make_unique(fmt::format("ipc://{}", glob_result.gl_pathv[0]), ZMQ_SUB, + std::bind(&llm_ec_prox::llm_kws_callback, this, + std::placeholders::_1, std::placeholders::_2)); + } + } + globfree(&glob_result); + } + if (llm_kws_pub_ctx_ == nullptr) { + CORO_YIELD(llm_status); + continue; + } + break; + } + for (;;) { + CORO_YIELD(llm_status); + } + CORO_END(llm_status); + } + + void loop() + { + while (!main_exit_flage) { + buttons_ec_thread(); + buttons_thread(); + fun_thread(); + eth_ec_thread(); + llm_status_thread(); + } + } + int rgb_old_mode; + void _init_ec() + { + uint16_t tab_reg; + + char *env_param = getenv("AX650_EC_RGB_MODE"); + tab_reg = env_param ? atoi(env_param) : 1; + rgb_old_mode = tab_reg; + modbus_write_registers(modbus_ctx, 11, 1, &tab_reg); + env_param = getenv("AX650_EC_LCD_MODE"); + tab_reg = env_param ? atoi(env_param) : 2; + modbus_write_registers(modbus_ctx, 13, 1, &tab_reg); + + tab_reg = 30000; + env_param = getenv("AX650_EC_POWER_OFF_TIME"); + if (env_param) { + tab_reg = atoi(env_param); + } + modbus_write_registers(modbus_ctx, 0, 1, &tab_reg); + } + + int is_poweroff_or_reboot() + { + FILE *fp; + char buffer[1024]; + int is_poweroff = 0; + int is_reboot = 0; + + fp = popen("systemctl list-jobs", "r"); + if (fp == NULL) { + return -1; + } + + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + if (strstr(buffer, "poweroff.target") != NULL) { + is_poweroff = 1; + break; + } + if (strstr(buffer, "reboot.target") != NULL) { + is_reboot = 1; + break; + } + } + + pclose(fp); + + if (is_poweroff) return 0; // poweroff + if (is_reboot) return 1; // reboot + return -1; // 未知 + } + + void ax650_ec_prox_exit() + { + // rgb 模式复位 + uint16_t tab_reg = 0; + std::cout << "reg reset" << std::endl; + modbus_write_registers(modbus_ctx, 11, 1, &tab_reg); + std::cout << "lcd_reset" << std::endl; + // lcd mode reset + modbus_write_registers(modbus_ctx, 13, 1, &tab_reg); + // fun full speed + tab_reg = 100; + modbus_write_registers(modbus_ctx, 1, 1, &tab_reg); + { + int runlevel = is_poweroff_or_reboot(); + if (runlevel == 0) { + tab_reg = 1; + modbus_write_bit(modbus_ctx, 16, tab_reg); + } + } + pub_ctx_.reset(); + modbus_close(modbus_ctx); + modbus_free(modbus_ctx); + std::cout << "all exit!" << std::endl; + } + ~llm_ec_prox() + { + ax650_ec_prox_exit(); + } +}; + +llm_ec_prox *llm_ec_prox::self; +int main(int argc, char *argv[]) +{ + signal(SIGTERM, __sigint); + signal(SIGINT, __sigint); + mkdir("/tmp/llm", 0777); + llm_ec_prox ec_prox; + ec_prox.loop(); + return 0; +} diff --git a/projects/ec_proxy/static/libzmq.a b/projects/ec_proxy/static/libzmq.a new file mode 100644 index 0000000..3ad08f4 Binary files /dev/null and b/projects/ec_proxy/static/libzmq.a differ