From 98f4de74d2e30913827cb2512bd983102ae1e908 Mon Sep 17 00:00:00 2001 From: myq Date: Thu, 27 Mar 2025 22:29:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=99=A8=E5=BE=85=E5=AE=8C?= =?UTF-8?q?=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 11 + AbilitySDK | 1 + .../include/ConveyorBeltAbility.h.old | 31 + .../include/conveyorBeltAbility.hpp | 64 ++ .../include/conveyorBeltGrpcServer.h | 57 ++ .../src/conveyorBeltAbility.cpp | 17 + .../src/conveyorGrpcServer.cpp | 97 ++++ ConveyorBeltAbility/src/main.cpp | 28 + ConveyorBeltControlUnit/include/color.h | 29 + .../include/conveyorBeltControlUnit.h | 74 +++ .../src/conveyorBeltControlUnit.cpp | 223 +++++++ .../include/conveyorBeltController.hpp | 84 +++ .../include/conveyorBeltGrpcClient.hpp | 85 +++ .../include/http_server.h.txt | 36 ++ .../src/conveyorBeltController.cpp | 549 ++++++++++++++++++ .../src/http_server.cpp.txt | 123 ++++ ConveyorBeltController/src/main.cpp | 48 ++ Readme.md | 1 + package-index.lua | 32 + protos/abilityProto.proto | 35 ++ protos/conveyorBelt.proto | 29 + protos/makefile | 13 + test/test-controller-api.py | 51 ++ xmake.lua | 61 ++ 24 files changed, 1779 insertions(+) create mode 100644 .gitignore create mode 160000 AbilitySDK create mode 100644 ConveyorBeltAbility/include/ConveyorBeltAbility.h.old create mode 100644 ConveyorBeltAbility/include/conveyorBeltAbility.hpp create mode 100644 ConveyorBeltAbility/include/conveyorBeltGrpcServer.h create mode 100644 ConveyorBeltAbility/src/conveyorBeltAbility.cpp create mode 100644 ConveyorBeltAbility/src/conveyorGrpcServer.cpp create mode 100644 ConveyorBeltAbility/src/main.cpp create mode 100644 ConveyorBeltControlUnit/include/color.h create mode 100644 ConveyorBeltControlUnit/include/conveyorBeltControlUnit.h create mode 100644 ConveyorBeltControlUnit/src/conveyorBeltControlUnit.cpp create mode 100644 ConveyorBeltController/include/conveyorBeltController.hpp create mode 100644 ConveyorBeltController/include/conveyorBeltGrpcClient.hpp create mode 100644 ConveyorBeltController/include/http_server.h.txt create mode 100644 ConveyorBeltController/src/conveyorBeltController.cpp create mode 100644 ConveyorBeltController/src/http_server.cpp.txt create mode 100644 ConveyorBeltController/src/main.cpp create mode 100644 Readme.md create mode 100644 package-index.lua create mode 100644 protos/abilityProto.proto create mode 100644 protos/conveyorBelt.proto create mode 100644 protos/makefile create mode 100644 test/test-controller-api.py create mode 100644 xmake.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..79ddc0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/.cache +/.vscode +/bin +/build +/unused +/log +/packages +/crs +/.xmake +/compile_commands.json + diff --git a/AbilitySDK b/AbilitySDK new file mode 160000 index 0000000..e20893b --- /dev/null +++ b/AbilitySDK @@ -0,0 +1 @@ +Subproject commit e20893b78a520157efeb28aeadb5914c5435d8f8 diff --git a/ConveyorBeltAbility/include/ConveyorBeltAbility.h.old b/ConveyorBeltAbility/include/ConveyorBeltAbility.h.old new file mode 100644 index 0000000..7e4aaf0 --- /dev/null +++ b/ConveyorBeltAbility/include/ConveyorBeltAbility.h.old @@ -0,0 +1,31 @@ + +#include "ability_sdk/AbilityStub.hpp" +#include "conveyorBeltGrpcServer.h" + + +class ConveyorBeltAbility : public ability_sdk::AbilityStub { +public: + using AbilityTemplate::AbilityTemplate; + + void create_ability_server() { + ConveyorBeltServer service_ability; + ServerBuilder builder; + builder.AddListeningPort("0.0.0.0:0", grpc::InsecureServerCredentials(), &g_abilityPort); + cout << "add listening port" << endl; + builder.RegisterService(&service_ability); + cout << "register service" << endl; + ability_cq = builder.AddCompletionQueue(); + ability_server = builder.BuildAndStart(); + std::cout << "Server listening on port: " << g_abilityPort << std::endl; + sendStateMsg(); + ability_server->Wait(); + std::cout << "finish ipc server" << std::endl; + } + + void runServer() { + g_GLOBAL_STATUS = STATUS_INIT; + std::cout << "NOW GLOBAL STATUS IS: " << global2string(g_GLOBAL_STATUS) << std::endl; + std::thread heartbeat_thread(&ConveyorBeltAbility::heartbeat_loop, this); + heartbeat_thread.detach(); + } +}; \ No newline at end of file diff --git a/ConveyorBeltAbility/include/conveyorBeltAbility.hpp b/ConveyorBeltAbility/include/conveyorBeltAbility.hpp new file mode 100644 index 0000000..6e4a28d --- /dev/null +++ b/ConveyorBeltAbility/include/conveyorBeltAbility.hpp @@ -0,0 +1,64 @@ +#include "ability_sdk/AbilityStub.hpp" +#include "conveyorBeltGrpcServer.h" +#include +#include + + +class ConveyorBeltAbility : public ability_sdk::AbilityInterface { +public: + + int g_abilityPort = 0; + std::unique_ptr ability_server; + std::unique_ptr ability_cq; + + // GRPC 服务端 + void create_ability_server(); + + void onStart() override{ + + LOG(WARNING) << "OnStart"; + + } + + void onConnect() override { + + LOG(WARNING) << "OnConnect"; + std::thread t_abilityserver(&ConveyorBeltAbility::create_ability_server, this); + t_abilityserver.detach(); + + // 主线程轮询等待端口号更新 + while (g_abilityPort == 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + LOG(WARNING) << "GRPC Server listening on port: "<< g_abilityPort; + + } + + void onDisconnect() override { + + LOG(WARNING) << "OnDisconnect"; + ability_server->Shutdown(); + ability_cq->Shutdown(); + g_abilityPort = 0; + + } + + void onTerminate() override { + + LOG(WARNING) << "OnTerminate"; + onDisconnect(); + exit(0); + + } + + // 获取端口 + int abilityPort() const override { + return g_abilityPort; + } + + void onSubabilityError(const ability_sdk::SubabilityErrorParam& param) override { + LOG(INFO) << "onSubabilityError called in ConveyorBeltAbility"; + } + + +}; \ No newline at end of file diff --git a/ConveyorBeltAbility/include/conveyorBeltGrpcServer.h b/ConveyorBeltAbility/include/conveyorBeltGrpcServer.h new file mode 100644 index 0000000..c641e9f --- /dev/null +++ b/ConveyorBeltAbility/include/conveyorBeltGrpcServer.h @@ -0,0 +1,57 @@ +#ifndef CONVEYOR_BELT_GRPC_SERVER_H_ +#define CONVEYOR_BELT_GRPC_SERVER_H_ +#include +#include +#include +#include +#include + +#include "conveyorBelt.grpc.pb.h" +#include "conveyorBelt.pb.h" +#include "conveyorBeltControlUnit.h" +#include +#include + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; + +using ConveyorBelt::ConveyorBeltService; + +typedef int RunningState; + +class ConveyorBeltServer : public ConveyorBeltService::Service { +private: + /* data */ +public: + ::grpc::Status open( + ::grpc::ServerContext* context, + const ::ConveyorBelt::SpeedInfo* request, + ::ConveyorBelt::Response* response + ) override; + ::grpc::Status close( + ::grpc::ServerContext* context, + const ::ConveyorBelt::DeviceID* request, + ::ConveyorBelt::Response* response + ) override; + ::grpc::Status setSpeed( + ::grpc::ServerContext* context, + const ::ConveyorBelt::SpeedInfo* request, + ::ConveyorBelt::Response* response + ) override; + ::grpc::Status setDirection( + ::grpc::ServerContext* context, + const ::ConveyorBelt::DirectionInfo* request, + ::ConveyorBelt::Response* response + ) override; + ::grpc::Status getWorkState( + ::grpc::ServerContext* context, + const ::ConveyorBelt::DeviceID* request, + ::ConveyorBelt::Response* response + ) override; + ConveyorBeltServer(); + ~ConveyorBeltServer(); +}; + +#endif diff --git a/ConveyorBeltAbility/src/conveyorBeltAbility.cpp b/ConveyorBeltAbility/src/conveyorBeltAbility.cpp new file mode 100644 index 0000000..d80fa89 --- /dev/null +++ b/ConveyorBeltAbility/src/conveyorBeltAbility.cpp @@ -0,0 +1,17 @@ +#include "conveyorBeltAbility.hpp" + + +// GRPC 服务端 +void ConveyorBeltAbility::create_ability_server() { + ConveyorBeltServer service_ability; + ServerBuilder builder; + builder.AddListeningPort("0.0.0.0:0", grpc::InsecureServerCredentials(), &g_abilityPort); + builder.RegisterService(&service_ability); + LOG(WARNING) << "GRPC Server register service"; + ability_cq = builder.AddCompletionQueue(); + ability_server = builder.BuildAndStart(); + //LOG(WARNING) << "GRPC Server add listening port"<< g_abilityPort; + ability_server->Wait(); + LOG(WARNING) << "finish ipc server"; + } + diff --git a/ConveyorBeltAbility/src/conveyorGrpcServer.cpp b/ConveyorBeltAbility/src/conveyorGrpcServer.cpp new file mode 100644 index 0000000..3d67d4a --- /dev/null +++ b/ConveyorBeltAbility/src/conveyorGrpcServer.cpp @@ -0,0 +1,97 @@ +#include "conveyorBeltGrpcServer.h" + +ConveyorBeltControlUnit controlUnit; + +::grpc::Status ConveyorBeltServer::open( + ::grpc::ServerContext* context, + const ::ConveyorBelt::SpeedInfo* request, + ::ConveyorBelt::Response* response +) { + std::cout << "grpc call open start" << std::endl; + if (controlUnit.getWorkState() == 0) { + response->set_code(-1); + return ::grpc::Status::OK; + } + + float speed = request->speed(); + if (speed > 0.16) { + response->set_code(-1); + return ::grpc::Status::OK; + } + controlUnit.init(); + int ret = controlUnit.setSpeed(speed); + if (ret < 0) { + response->set_code(-1); + return ::grpc::Status::OK; + } + controlUnit.startConveyorBelt(); + std::thread workThread(&ConveyorBeltControlUnit::conveyorBeltWorkingProcess, &controlUnit); + workThread.detach(); + std::cout << "grpc call open finish" << std::endl; + response->set_code(0); + return ::grpc::Status::OK; +} + +::grpc::Status ConveyorBeltServer::close( + ::grpc::ServerContext* context, + const ::ConveyorBelt::DeviceID* request, + ::ConveyorBelt::Response* response +) { + std::cout << "grpc call close start" << std::endl; + if (controlUnit.getWorkState() < 0) { + response->set_code(-1); + return ::grpc::Status::OK; + } + + controlUnit.stopConveyorBelt(); + std::cout << "grpc call close finish" << std::endl; + response->set_code(0); + return ::grpc::Status::OK; +} + +::grpc::Status ConveyorBeltServer::setSpeed( + ::grpc::ServerContext* context, + const ::ConveyorBelt::SpeedInfo* request, + ::ConveyorBelt::Response* response +) { + std::cout << "grpc call set speed start" << std::endl; + int ret = controlUnit.setSpeed(request->speed()); + if (ret < 0) { + response->set_code(-1); + ::grpc::Status::OK; + } + std::cout << "grpc call set speed finish" << std::endl; + response->set_code(0); + return ::grpc::Status::OK; +} + +::grpc::Status ConveyorBeltServer::setDirection( + ::grpc::ServerContext* context, + const ::ConveyorBelt::DirectionInfo* request, + ::ConveyorBelt::Response* response +) { + std::cout << "grpc call set direction start" << std::endl; + int ret = controlUnit.setDirection(request->direction()); + if (ret < 0) { + response->set_code(-1); + ::grpc::Status::OK; + } + std::cout << "grpc call set direction finish" << std::endl; + response->set_code(0); + return ::grpc::Status::OK; +} + +::grpc::Status ConveyorBeltServer::getWorkState( + ::grpc::ServerContext* context, + const ::ConveyorBelt::DeviceID* request, + ::ConveyorBelt::Response* response +) { + std::cout << "grpc call get state start" << std::endl; + response->set_code(controlUnit.getWorkState()); + std::cout << "grpc call get state finish" << std::endl; + return ::grpc::Status::OK; +} + +ConveyorBeltServer::ConveyorBeltServer() {} + +ConveyorBeltServer::~ConveyorBeltServer() {} diff --git a/ConveyorBeltAbility/src/main.cpp b/ConveyorBeltAbility/src/main.cpp new file mode 100644 index 0000000..3e4fef1 --- /dev/null +++ b/ConveyorBeltAbility/src/main.cpp @@ -0,0 +1,28 @@ +#include "conveyorBeltAbility.hpp" +#include "ability_sdk/AbilityStub.hpp" + + +int main(int argc, const char** argv) { + + ability_sdk::configure_glog(argv[0]); + ConveyorBeltAbility ability1; + ability_sdk::IpcServerHandle handle(&ability1); + handle.init(argc, argv); + handle.run(); + return 0; + +} + + +/* Init 阶段 +通过环境变量,尝试设置目录路径,启动glog +启动 redis 客户端 +向框架读取,获取能力CR,填充ability_info, 并设置 heartbeater +设置生命周期ipc服务器 +启动心跳包 + +Start 阶段 + +如果有子能力,则初始化SubabilityMgr,并读取子能力cr +如果有子能力,尝试启动子能力 +启动状态同步机制 */ \ No newline at end of file diff --git a/ConveyorBeltControlUnit/include/color.h b/ConveyorBeltControlUnit/include/color.h new file mode 100644 index 0000000..6e00bce --- /dev/null +++ b/ConveyorBeltControlUnit/include/color.h @@ -0,0 +1,29 @@ +#ifndef UTILS_COLOR_H_ +#define UTILS_COLOR_H_ + +#define EMPTY "\e[0m" +#define BLACK "\e[0;30m" +#define L_BLACK "\e[1;30m" +#define RED "\e[0;31m" +#define L_RED "\e[1;31m" +#define GREEN "\e[0;32m" +#define L_GREEN "\e[1;32m" +#define BROWN "\e[0;33m" +#define YELLOW "\e[1;33m" +#define BLUE "\e[0;34m" +#define L_BLUE "\e[1;34m" +#define PURPLE "\e[0;35m" +#define L_PURPLE "\e[1;35m" +#define CYAN "\e[0;36m" +#define L_CYAN "\e[1;36m" +#define GRAY "\e[0;37m" +#define WHITE "\e[1;37m" +#define BOLD "\e[1m" +#define UNDERLINE "\e[4m" +#define BLINK "\e[5m" +#define REVERSE "\e[7m" +#define HIDE "\e[8m" +#define CLEAR "\e[2J" +#define CLRLINE "\r\e[K" + +#endif // UTILS_COLOR_H_ \ No newline at end of file diff --git a/ConveyorBeltControlUnit/include/conveyorBeltControlUnit.h b/ConveyorBeltControlUnit/include/conveyorBeltControlUnit.h new file mode 100644 index 0000000..c40107d --- /dev/null +++ b/ConveyorBeltControlUnit/include/conveyorBeltControlUnit.h @@ -0,0 +1,74 @@ +#include "color.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include +//#include // 树莓派GPIO库 +// src中202行记得修改 +} + +class ConveyorBeltControlUnit { +private: + // 所有的输入端口配置为内部上拉,低有效 + // 悬空时为高,视为逻辑0 + // 外部拉低时,视为逻辑1 + const int IN_LOGIC_0 = 1; + const int IN_LOGIC_1 = 0; + + // 输出端口为Push-Pull模式 + // 逻辑开时输出高电平 + // 逻辑低时输出低电平 + const int OUT_ON = 1; + const int OUT_OFF = 0; + + const int BTN_DIRSWITCH = 26; // 运行方向切换按钮 + const int BTN_START = 23; // 启动按钮 + const int BTN_STOP = 24; // 停止按钮 + const int BTN_ESTOP = 11; // 急停 + + int INPUT_FSENSOR = 28; // 正转边沿传感器 + int INPUT_RSENSOR = 29; // 反转边沿传感器 + int OUT_FRUN = 14; // 正向运行输出 + int OUT_RRUN = 10; // 反向运行输出 + + // 定义事件 + const int EVENT_ESTOP_PUSH = 0; // 急停按下(电平触发) + const int EVENT_STOP_PUSH = 1; // 停止按下(电平触发) + const int EVENT_FSENSOR_OBSTRUCT = 2; // 正转传感器触发(电平触发) + const int EVENT_RSENSOR_OBSTRUCT = 3; // 反转传感器触发(电平触发) + const int EVENT_DIRSWITCH_RISING = 4; // 切换运转方向(逻辑上升沿触发) + const int EVENT_START_RISING = 5; // 开始运行(逻辑上升沿触发) + + // 定义unit工作状态 + const int RUNNING = 1; + const int STANDBY = 0; + + int current_start_signal; + int current_stop_signal; + int currentMode; + float speed; + int workState; + std::map> modeSwitchMap; + void action(int event); + std::string modeToString(); + int getWaitCount(); + +public: + void conveyorBeltWorkingProcess(); + int init(); + int setSpeed(float speed); + int setDirection(int direction); + int startConveyorBelt(); + int stopConveyorBelt(); + int getWorkState(); + ConveyorBeltControlUnit(/* args */); + ~ConveyorBeltControlUnit(); +}; diff --git a/ConveyorBeltControlUnit/src/conveyorBeltControlUnit.cpp b/ConveyorBeltControlUnit/src/conveyorBeltControlUnit.cpp new file mode 100644 index 0000000..71b1a66 --- /dev/null +++ b/ConveyorBeltControlUnit/src/conveyorBeltControlUnit.cpp @@ -0,0 +1,223 @@ +#include "conveyorBeltControlUnit.h" + +// 202行记得修改 +// x86 无法运行 + +// 定义状态 +const int MODE_INIT = 0; // 初始状态 +const int MODE_FRUN = 1; // 正转运行状态 +const int MODE_RRUN = 2; // 反转运行状态 +const int MODE_CLOSING_STEP1 = 3; // 进入关闭状态步骤1 +const int MODE_CLOSING_STEP2 = 4; // 进入关闭状态步骤2 +const int MODE_STOP = 5; +int ConveyorBeltControlUnit::getWaitCount() { + return 2 * (0.8 / speed) / 0.1; +} + +std::string ConveyorBeltControlUnit::modeToString() { + switch (currentMode) { + case MODE_INIT: return "MODE_INIT"; + case MODE_FRUN: return "MODE_FRUN"; + case MODE_RRUN: return "MODE_RRUN"; + case MODE_CLOSING_STEP1: return "MODE_CLOSING_STEP1"; + case MODE_CLOSING_STEP2: return "MODE_CLOSING_STEP2"; + case MODE_STOP: return "MODE_STOP"; + default: return "INVALID"; + } +} + +void ConveyorBeltControlUnit::action(int event) { + /* int destMode = modeSwitchMap[currentMode][event]; + if (destMode == currentMode) { return; } + + switch (destMode) { + case MODE_INIT: + digitalWrite(OUT_FRUN, OUT_OFF); + digitalWrite(OUT_RRUN, OUT_OFF); + break; + case MODE_FRUN: + digitalWrite(OUT_FRUN, OUT_ON); + digitalWrite(OUT_RRUN, OUT_OFF); + break; + case MODE_RRUN: + digitalWrite(OUT_FRUN, OUT_OFF); + digitalWrite(OUT_RRUN, OUT_ON); + break; + case MODE_CLOSING_STEP1: break; + case MODE_CLOSING_STEP2: + digitalWrite(OUT_FRUN, OUT_OFF); + digitalWrite(OUT_RRUN, OUT_ON); + break; + case MODE_STOP: + digitalWrite(OUT_FRUN, OUT_OFF); + digitalWrite(OUT_RRUN, OUT_OFF); + workState = STANDBY; + break; + default: break; + } + currentMode = destMode; + LOG(INFO) << GREEN << "now mode is " << modeToString() << EMPTY; */ +} + +void ConveyorBeltControlUnit::conveyorBeltWorkingProcess() { + /* workState = RUNNING; + int waitCount = 0; + int count = 0; + while (workState == RUNNING) { + waitCount = getWaitCount(); + if (currentMode == MODE_CLOSING_STEP1 || currentMode == MODE_CLOSING_STEP2) { + count--; + if (count < 0) { action(EVENT_RSENSOR_OBSTRUCT); } + } + else { count = waitCount; } + + if (current_start_signal == 1) { + LOG(INFO) << YELLOW << "find a start signal" << EMPTY; + current_start_signal = 0; + action(EVENT_START_RISING); + } + + if (digitalRead(INPUT_FSENSOR) == IN_LOGIC_1) { + // LOG(INFO) << YELLOW << "find a Fsensor signal" << EMPTY; + action(EVENT_FSENSOR_OBSTRUCT); + } + + if (digitalRead(INPUT_RSENSOR) == IN_LOGIC_1) { + // LOG(INFO) << YELLOW << "find a Rsensor signal" << EMPTY; + action(EVENT_RSENSOR_OBSTRUCT); + } + + if (current_stop_signal == 1) { + LOG(INFO) << YELLOW << "find a stop signal" << EMPTY; + LOG(INFO) << YELLOW << "wait count is " << waitCount << " last wait count left " + << count << EMPTY; + current_stop_signal = 0; + action(EVENT_STOP_PUSH); + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } */ +} + +int ConveyorBeltControlUnit::init() { + current_start_signal = 0; + current_stop_signal = 0; + currentMode = MODE_INIT; + speed = 0; + workState = STANDBY; + return 0; +} + +int ConveyorBeltControlUnit::setSpeed(float speed) { + // if (workState == RUNNING) + // { + // return -1; + // } + // 原本已经有的注释 + + /* int regValue = speed * 62500; + modbus_t* ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 2); + modbus_set_slave(ctx, 1); + if (modbus_connect(ctx) == -1) { + fprintf(stderr, "Connection failed:%s\n", modbus_strerror(errno)); + modbus_free(ctx); + return -1; + } + LOG(INFO) << YELLOW << "set speed:" << speed << " set register:" << regValue << EMPTY; + int result = modbus_write_register(ctx, 4096, regValue); + if (result == -1) { + fprintf(stderr, "write failed:%s\n", modbus_strerror(errno)); + modbus_free(ctx); + return -1; + } + LOG(INFO) << YELLOW << "get result from com" << ":" << result << EMPTY; + uint16_t* speedBuffer = (uint16_t*)malloc(sizeof(uint16_t)); + int getBytes = modbus_read_registers(ctx, 4096, 2, speedBuffer); + LOG(INFO) << YELLOW << "get result from com : " << getBytes << " register:" << *speedBuffer + << EMPTY; + this->speed = speed; + modbus_free(ctx); */ + return 0; +} + +int ConveyorBeltControlUnit::setDirection(int direction) { + if (workState == RUNNING) { return -1; } + + if (direction == 0) { + LOG(INFO) << YELLOW << "set direction to foreward" << EMPTY; + INPUT_FSENSOR = 28; + INPUT_RSENSOR = 29; + OUT_FRUN = 14; + OUT_RRUN = 10; + } + else if (direction == 1) { + LOG(INFO) << YELLOW << "set direction to reversal" << EMPTY; + INPUT_FSENSOR = 29; + INPUT_RSENSOR = 28; + OUT_FRUN = 10; + OUT_RRUN = 14; + } + else { return -1; } + return 0; +} + +int ConveyorBeltControlUnit::startConveyorBelt() { + current_start_signal = 1; + return 0; +} + +int ConveyorBeltControlUnit::stopConveyorBelt() { + current_stop_signal = 1; + return 0; +} + +int ConveyorBeltControlUnit::getWorkState() { + if (workState == RUNNING) { return 0; } + else { return -1; } +} + +ConveyorBeltControlUnit::ConveyorBeltControlUnit(/* args */) { + modeSwitchMap[MODE_INIT] + = {{EVENT_START_RISING, MODE_FRUN}, + {EVENT_FSENSOR_OBSTRUCT, MODE_INIT}, + {EVENT_RSENSOR_OBSTRUCT, MODE_INIT}, + {EVENT_STOP_PUSH, MODE_INIT}}; + modeSwitchMap[MODE_FRUN] + = {{EVENT_START_RISING, MODE_FRUN}, + {EVENT_FSENSOR_OBSTRUCT, MODE_RRUN}, + {EVENT_RSENSOR_OBSTRUCT, MODE_FRUN}, + {EVENT_STOP_PUSH, MODE_CLOSING_STEP1}}; + modeSwitchMap[MODE_RRUN] + = {{EVENT_START_RISING, MODE_RRUN}, + {EVENT_FSENSOR_OBSTRUCT, MODE_RRUN}, + {EVENT_RSENSOR_OBSTRUCT, MODE_FRUN}, + {EVENT_STOP_PUSH, MODE_CLOSING_STEP2}}; + modeSwitchMap[MODE_CLOSING_STEP1] + = {{EVENT_START_RISING, MODE_CLOSING_STEP1}, + {EVENT_FSENSOR_OBSTRUCT, MODE_CLOSING_STEP2}, + {EVENT_RSENSOR_OBSTRUCT, MODE_STOP}, + {EVENT_STOP_PUSH, MODE_CLOSING_STEP1}}; + modeSwitchMap[MODE_CLOSING_STEP2] + = {{EVENT_START_RISING, MODE_CLOSING_STEP2}, + {EVENT_FSENSOR_OBSTRUCT, MODE_CLOSING_STEP2}, + {EVENT_RSENSOR_OBSTRUCT, MODE_STOP}, + {EVENT_STOP_PUSH, MODE_CLOSING_STEP2}}; +// x86 无法运行 +/* wiringPiSetup(); + pinMode(BTN_START, INPUT); + pinMode(BTN_DIRSWITCH, INPUT); + pinMode(BTN_STOP, INPUT); + pinMode(INPUT_FSENSOR, INPUT); + pinMode(INPUT_RSENSOR, INPUT); + pullUpDnControl(BTN_START, PUD_UP); + pullUpDnControl(BTN_DIRSWITCH, PUD_UP); + pullUpDnControl(BTN_STOP, PUD_UP); + pullUpDnControl(INPUT_FSENSOR, PUD_UP); + pullUpDnControl(INPUT_RSENSOR, PUD_UP); + pinMode(OUT_RRUN, OUTPUT); + pinMode(OUT_FRUN, OUTPUT); + digitalWrite(OUT_FRUN, OUT_OFF); + digitalWrite(OUT_RRUN, OUT_OFF); */ + +} + +ConveyorBeltControlUnit::~ConveyorBeltControlUnit() {} diff --git a/ConveyorBeltController/include/conveyorBeltController.hpp b/ConveyorBeltController/include/conveyorBeltController.hpp new file mode 100644 index 0000000..551348a --- /dev/null +++ b/ConveyorBeltController/include/conveyorBeltController.hpp @@ -0,0 +1,84 @@ +#ifndef BELT_CONTROLLER_H_ +#define BELT_CONTROLLER_H_ +#include "color.h" +#include "conveyorBeltGrpcClient.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ability_sdk/ControllerHeartbeat.hpp" +#include "ability_sdk/Heartbeat.hpp" + +//#include + +//#include "http_server.h" +// 前向声明 HttpServer 类,避免循环依赖 +//class HttpServer; + + +typedef int ConveyorBeltStatus; + + +// 控制器类 +class ConveyorBeltController { + +public: + + httplib::Server http_server; + void configure_http_server(); + std::map directionMap; + //std::map ipMap; + int port; + + // 控制器心跳包 + ability_sdk::ControllerHeartbeat heartbeat_pack; + + // 能力心跳获取 + std::map ability_heartbeats; + bool gather_abilities(); + bool check_ability_status(); + + void init(); + void Run(); + + // 控制器心跳线程 + std::thread th_heartbeat; + void routine_heartbeat(); + + int handleOpen(float speed, int direction); + int handleClose(); + int handleCheck(); + void setServerIp(std::string serverIp); + ConveyorBeltStatus getCurrentStatus(); + ConveyorBeltController(); + ~ConveyorBeltController(); + +private: + std::string ip; + ConveyorBeltStatus status; + std::shared_mutex mutex_; + int IPCPort; + int abilityPort; + std::map ipMap; + // 状态转换 + int stateSwitch(ConveyorBeltStatus destState, std::string cmd); + // grpc连接 + int grpcConnect(float speed, int direction); + // grpc断开 + int grpcDisconnect(); + // grpc检查 + int grpcCheck(); + // 同步状态 + int synchState(std::string json); + // 简单状态检查 + bool simpleStateCheck(); + +}; + +#endif diff --git a/ConveyorBeltController/include/conveyorBeltGrpcClient.hpp b/ConveyorBeltController/include/conveyorBeltGrpcClient.hpp new file mode 100644 index 0000000..2b9b124 --- /dev/null +++ b/ConveyorBeltController/include/conveyorBeltGrpcClient.hpp @@ -0,0 +1,85 @@ +#ifndef CONVEYOR_BELT_GRPC_CLIENT_H_ +#define CONVEYOR_BELT_GRPC_CLIENT_H_ +#include +#include +#include +#include +#include + +#include "conveyorBelt.grpc.pb.h" +#include "conveyorBelt.pb.h" +#include "conveyorBeltControlUnit.h" + +#include +#include +#include +#include +#include +#include +#include + +using grpc::Channel; +using grpc::ClientContext; +using grpc::ClientReader; +using grpc::ClientWriter; +using grpc::Status; + +class ConveyorBeltGrpcClient { +private: + std::unique_ptr stub_; + +public: + // 启动带速度和方向的传送带 + int startWithSpeedAndDirection(float speed, int direction) { + LOG(INFO) << GRAY << "start connect to belt" << EMPTY; + ConveyorBelt::SpeedInfo speedInfo; + speedInfo.set_speed(speed); + ConveyorBelt::DirectionInfo directionInfo; + directionInfo.set_direction(direction); + ConveyorBelt::Response response; + ClientContext context_direction; + grpc::Status ret = stub_->setDirection(&context_direction, directionInfo, &response); + if (!ret.ok()) { + LOG(INFO) << GRAY << "grpc server cannot connect" << EMPTY; + return -2; + } + if (response.code() < 0) { return -1; } + ClientContext context_start; + stub_->open(&context_start, speedInfo, &response); + if (response.code() < 0) { return -1; } + LOG(INFO) << GRAY << "finish connect to belt" << EMPTY; + return 0; + } + + // 关闭传送带 + int close() { + LOG(INFO) << GRAY << "start connect to belt" << EMPTY; + ConveyorBelt::DeviceID id; + id.set_conveyorbeltid(0); + ClientContext context_close; + ConveyorBelt::Response response; + stub_->close(&context_close, id, &response); + if (response.code() < 0) { return -1; } + LOG(INFO) << GRAY << "start connect to belt" << EMPTY; + return 0; + } + + // 获取传送带状态 + int getWorkState() { + LOG(INFO) << GRAY << "start connect to belt" << EMPTY; + ConveyorBelt::DeviceID id; + id.set_conveyorbeltid(0); + ClientContext context_get; + ConveyorBelt::Response response; + stub_->getWorkState(&context_get, id, &response); + LOG(INFO) << GRAY << "start connect to belt" << EMPTY; + return response.code(); + } + + ConveyorBeltGrpcClient(std::shared_ptr channel) + : stub_(ConveyorBelt::ConveyorBeltService::NewStub(channel)) {} + + ~ConveyorBeltGrpcClient() {} +}; + +#endif diff --git a/ConveyorBeltController/include/http_server.h.txt b/ConveyorBeltController/include/http_server.h.txt new file mode 100644 index 0000000..0aa2f97 --- /dev/null +++ b/ConveyorBeltController/include/http_server.h.txt @@ -0,0 +1,36 @@ +#ifndef HTTP_SERVER_H_ +#define HTTP_SERVER_H_ + +#include +#include "color.h" +#include "conveyorBeltController.hpp" +#include +#include +#include +#include +#include + +class HttpServer { +public: + + // 构造函数,接收 ConveyorBeltController 的引用 + explicit HttpServer(ConveyorBeltController& controller); + + void Init(); + void Run(); + +private: + // http服务器 + std::shared_ptr server; + + // 控制器 + //std::shared_ptr controller; + + std::map directionMap; + std::map ipMap; + + // 引用 ConveyorBeltController 实例 + ConveyorBeltController& controller; +}; + +#endif // HTTP_SERVER_H_ diff --git a/ConveyorBeltController/src/conveyorBeltController.cpp b/ConveyorBeltController/src/conveyorBeltController.cpp new file mode 100644 index 0000000..53a1cbe --- /dev/null +++ b/ConveyorBeltController/src/conveyorBeltController.cpp @@ -0,0 +1,549 @@ +#include "conveyorBeltController.hpp" + + +const ConveyorBeltStatus OFFLINE = 0; +const ConveyorBeltStatus READY = 1; +const ConveyorBeltStatus RUNNING = 2; + +ConveyorBeltController::ConveyorBeltController(/* args */) {} +ConveyorBeltController::~ConveyorBeltController() { + if (th_heartbeat.joinable()) { + th_heartbeat.join(); // 等待线程结束 + } +} + +// 配置http服务器 +void ConveyorBeltController::configure_http_server(){ + FLAGS_logtostderr = 1; + LOG(INFO) << L_GREEN << "init http server" << EMPTY; + + ipMap[108] = "192.168.1.217"; + ipMap[109] = "192.168.1.218"; + directionMap[108] = 0; + directionMap[109] = 0; + + // GET 请求 /api/processGet 处理函数,通过id参数获取状态 + http_server.Get( + "/api/processGet", + [this](const httplib::Request& req, httplib::Response& res) { + LOG(INFO) << L_BLUE << "httpserver receive: method: GET URL: /api/processGet" + << EMPTY; + std::string conveyorBeltId; + int result = 200; + //Json::Value response; + nlohmann::json response; + try { + conveyorBeltId = req.get_param_value("id"); + LOG(INFO) << YELLOW << "GET param id:" << conveyorBeltId << EMPTY; + } + catch (const std::exception&) { + res.status = 400; // Bad Request + response["status"] = 400; + response["processStatus"] = this->getCurrentStatus(); + res.set_content(response.dump(4), "application/json"); + return; + } + std::map::iterator it = ipMap.find(std::stoi(conveyorBeltId)); + if (it != ipMap.end()) { this->setServerIp(it->second); } + else { + response["status"] = 400; + response["processStatus"] = this->getCurrentStatus(); + res.set_content(response.dump(4), "application/json"); + return; + } + if (this->handleCheck() < 0) { result = 400; } + + // status = 200 + response["status"] = result; + response["processStatus"] = this->getCurrentStatus(); + res.set_content(response.dump(4), "application/json"); + } + ); + + + // POST 请求 /api/processPost 处理函数 + http_server.Post( + "/api/processPost", + [this](const httplib::Request& req, httplib::Response& res) { + LOG(INFO) << L_BLUE << "httpserver receive: method: POST URL: /api/processPost" + << EMPTY; + LOG(INFO) << YELLOW << req.get_header_value("Content-Type") << EMPTY; + //Json::Value root; + nlohmann::json root; + int status = 200; + if (req.get_header_value("Content-Type").find("application/json")!= std::string::npos) { + //Json::Reader reader; + /* nlohmann::json reader; + bool success = reader.parse(req.body, root); + if (!success) { + LOG(ERROR) << "json parse error"; + res.status = 400; + status = 400; + } */ + + try { + root = nlohmann::json::parse(req.body); // 使用静态方法解析 JSON + } catch (const nlohmann::json::parse_error& e) { + // json解析错误 + LOG(ERROR) << "json parse error: " << e.what(); + res.status = 400; + status = 400; + return; + } + + // 以下缩进需要调整 + //else { + LOG(INFO) << YELLOW << "id:" << root["id"].get() + << " action:" << root["action"].get() + << " speed:" << root["speed"].get() << EMPTY; + //Json::Value result; + nlohmann::json result; + std::map::iterator it = ipMap.find(root["id"].get()); + + // 根据传送带id,检查该id是否存在于ipMap中 + // it->first 为传送带id + // it->second 为传送带对应的ip地址 + if (it != ipMap.end()) { this->setServerIp(it->second); } + else { + result["status"] = 400; + result["processStatus"] = this->getCurrentStatus(); + res.set_content(result.dump(4), "application/json"); + return; + } + // 启动 + if (root["action"].get() == 1) { + LOG(INFO) << YELLOW << "http server try to handle open" << EMPTY; + std::thread openThread( + &ConveyorBeltController::handleOpen, + this, + root["speed"].get(), + directionMap[root["id"].get()] + ); + openThread.detach(); + } + + // 关闭 + else if (root["action"].get() == 0) { + LOG(INFO) << YELLOW << "http server try to handle close" << EMPTY; + std::thread closeThread( + &ConveyorBeltController::handleClose, this + ); + closeThread.detach(); + } + else { + LOG(INFO) << YELLOW << "invalid action code" << EMPTY; + status = 400; + } + // } + } + else { status = 400; } + //Json::Value result; + nlohmann::json result; + result["status"] = status; + result["processStatus"] = this->getCurrentStatus(); + res.set_content(result.dump(4), "application/json"); + } + ); + +} + + +// 设置服务器ip +void ConveyorBeltController::setServerIp(std::string conveyorBeltIp) { + this->ip = conveyorBeltIp; +} + +// 初始化 +void ConveyorBeltController::init() { + + std::unique_lock w_lock(mutex_); + LOG(INFO) << WHITE << "init controller state" << EMPTY; + status = OFFLINE; + abilityPort = 0; + IPCPort = 0; + //configure_http_server(); +} + +namespace { + void send_one_heartbeat(const ability_sdk::ControllerHeartbeat& heartbeat_pack) { + try { + httplib::Client cli("127.0.0.1", 8080); // 连接到框架 + // 生成 JSON 格式的心跳包 + nlohmann::json hb_json; + try { + hb_json = nlohmann::json(heartbeat_pack); + //LOG(INFO) << "heartbeat_pack to json success"; + } catch (const nlohmann::json::exception& e) { + LOG(ERROR) << "JSON serialization error: " << e.what(); + return; + } + + auto post_res = cli.Post("/api/controller-heartbeat", hb_json.dump(2), "application/json"); + if (!post_res) { + LOG(ERROR) << "failed to connect to framework: " << post_res.error(); + } else if (post_res->status != 200) { + LOG(ERROR) << "failed to connect to framework: " << post_res->body; + } + LOG(INFO) << "send one heartbeat success"; + } catch (const std::exception& e) { + LOG(ERROR) << "Exception in send_one_heartbeat: " << e.what(); + } catch (...) { + LOG(ERROR) << "Unknown exception in send_one_heartbeat"; + } + } +} // namespace + + +// 心跳包 +void ConveyorBeltController::routine_heartbeat() { + LOG(INFO) << "start heart beat routine"; + while (true) { + try { + send_one_heartbeat(heartbeat_pack); + //gather_abilities(); + //check_ability_status(); + }catch (const std::exception& e) { + LOG(ERROR) << "Exception in routine_heartbeat: " << e.what(); + } catch (...) { + LOG(ERROR) << "Unknown exception in routine_heartbeat"; + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + } +} + +void ConveyorBeltController::Run() { + + //http_server.Init(); + //http_server.Run(); + LOG(INFO) << "ConveyorBeltController::Run()"; + + configure_http_server(); + port = http_server.bind_to_any_port("0.0.0.0"); + CHECK_GE(port, 0) << "http server bind port failed"; + LOG(WARNING) << "http server listen on: " << port; + // 设置心跳包中的端口 + this->heartbeat_pack.port = port; + + // 启动心跳包线程 + this->th_heartbeat = std::thread(&ConveyorBeltController::routine_heartbeat, this); + // 启动http服务器 + http_server.listen_after_bind(); + +} + + +bool ConveyorBeltController::gather_abilities() { + httplib::Client cli{"localhost", 8080}; + auto get_res = cli.Get("/api/ability-heartbeat"); + if (!get_res) { + LOG(ERROR) << "failed to update ability info: failed to connect to framework"; + return false; + } + if (get_res.value().status != 200) { + LOG(ERROR) << "failed to gather ability information: " << get_res.value().body; + return false; + } + + auto json_arr_res = nlohmann::json::parse(get_res->body); + DLOG(INFO) << "get ablility info:" << json_arr_res; + if (json_arr_res.is_null() || (!json_arr_res.is_array())) { + DLOG(INFO) << "ability info is null, return"; + return false; + } + std::lock_guard _lk(m); + DLOG(INFO) << "iterating ablility info: " << json_arr_res.dump(); + for (int i = 0; i < json_arr_res.size(); ++i) { + auto sub_json = json_arr_res.at(i); + if (!sub_json.is_object()) { continue; } + try { + auto entry = sub_json.get(); + if (entry.abilityName != ABILITY_NAME) { continue; } + DLOG(INFO) << "update ablility " << entry.id << " with " + << nlohmann::json(entry).dump(); + ability_heartbeats[entry.id] = entry; + } + catch (nlohmann::json::exception& e) { + LOG(ERROR) << "invalid heartbeat info: " << e.what() << "\n" << sub_json.dump(2); + continue; + } + } + return true; +} + + + + +int ConveyorBeltController::handleCheck() { + std::unique_lock w_lock(mutex_); + if (simpleStateCheck()) { return 0; } + else { return -1; } +} +// 获取当前状态,并且根据gRPC检查状态 +int ConveyorBeltController::getCurrentStatus() { + std::shared_lock r_lock(mutex_); + if (status == RUNNING) { + if (grpcCheck() < 0) { return READY; } + } + return status; +} + +int ConveyorBeltController::handleOpen(float speed, int direction) { + std::unique_lock w_lock(mutex_); + int result = 0; + if (simpleStateCheck() == false) { return -1; } + switch (status) { + case RUNNING: + if (grpcCheck() < 0) { + result = grpcConnect(speed, direction); + break; + } + LOG(INFO) << WHITE << "ConveyorBelt is already running, do not open twice" << EMPTY; + break; + case READY: + LOG(INFO) << WHITE << "ConveyorBelt is ready, start handle open request" << EMPTY; + result = stateSwitch(RUNNING, "connect"); + if (result < 0) { break; } + result = grpcConnect(speed, direction); + break; + case OFFLINE: + LOG(INFO) << WHITE << "ConveyorBelt is offline, start handle open request" << EMPTY; + result = stateSwitch(READY, "start"); + if (result < 0) { break; } + result = stateSwitch(RUNNING, "connect"); + if (result < 0) { break; } + result = grpcConnect(speed, direction); + break; + default: break; + } + return result; +} + +int ConveyorBeltController::handleClose() { + std::unique_lock w_lock(mutex_); + int result = 0; + if (simpleStateCheck() == false) { return -1; } + switch (status) { + case READY: + case OFFLINE: + LOG(INFO) << WHITE << "ConveyorBelt has not been opened, do not close twice" << EMPTY; + break; + case RUNNING: + LOG(INFO) << WHITE << "ConveyorBelt is now running, start handle ConveyorBelt close" + << EMPTY; + result = grpcDisconnect(); + if (result < 0) { break; } + result = stateSwitch(READY, "disconnect"); + break; + default: break; + } + return result; +} + +bool ConveyorBeltController::simpleStateCheck() { + LOG(INFO) << WHITE << "start simple check" << EMPTY; + httplib::Client client(ip, 8080); + int mStatus; + auto res = client.Get("/api/AbilityRunning"); + if (res) { + LOG(INFO) << WHITE << "GET response:" << res->status << EMPTY; + mStatus = synchState(res->body); + if (mStatus == status) { + LOG(INFO) << WHITE + << "simple check pass current status is correct with abilityFrameWork" + << EMPTY; + return true; + } + else { + LOG(INFO) << WHITE << "simple check failed incorrect state" << EMPTY; + return false; + } + } + else { + LOG(ERROR) << "FIRST STEP: can not connect with abilityFrameWork" << EMPTY; + LOG(INFO) << WHITE << "now ConveyorBelt is OFFLINE" << EMPTY; + status = OFFLINE; + abilityPort = 0; + IPCPort = 0; + return false; + } +} + + +int ConveyorBeltController::synchState(std::string json) { + nlohmann::json states; + try { + states = nlohmann::json::parse(json); // 使用 nlohmann::json 解析 JSON 字符串 + for (const auto& state : states) { // 遍历 JSON 数组 + LOG(INFO) << WHITE << "IPCPort:" << state["IPCPort"].get() + << " abilityName:" << state["abilityName"].get() + << " status:" << state["status"].get() << EMPTY; + + if (state["IPCPort"].get() == IPCPort || + (IPCPort == 0 && state["abilityName"].get() == "conveyorBelt")) { + if (state["status"].get() == "RUNNING") { + LOG(INFO) << WHITE << "now ConveyorBelt is RUNNING" << EMPTY; + status = RUNNING; + IPCPort = state["IPCPort"].get(); + abilityPort = state["abilityPort"].get(); + if (abilityPort == 0) { + return READY; + } else { + return RUNNING; + } + } else if (state["status"].get() == "STANDBY" || + state["status"].get() == "SUSPEND") { + LOG(INFO) << WHITE << "now ConveyorBelt is READY" << EMPTY; + status = READY; + IPCPort = state["IPCPort"].get(); + abilityPort = 0; + return READY; + } + } + } + } catch (const nlohmann::json::parse_error& e) { + LOG(ERROR) << "JSON parse error: " << e.what(); + // 如果解析失败,返回 OFFLINE 状态 + status = OFFLINE; + abilityPort = 0; + IPCPort = 0; + return OFFLINE; + } + + // 如果没有匹配的状态,设置为 OFFLINE + LOG(INFO) << WHITE << "now ConveyorBelt is OFFLINE" << EMPTY; + status = OFFLINE; + abilityPort = 0; + IPCPort = 0; + return OFFLINE; +} + + + +/* int ConveyorBeltController::synchState(std::string json) { + Json::Reader reader; + Json::Value states; + if (reader.parse(json, states)) { + for (int i = 0; i < states.size(); i++) { + LOG(INFO) << WHITE << "IPCPort:" << states[i]["IPCPort"].asString() + << " abilityName:" << states[i]["abilityName"].asString() << "status" + << states[i]["status"].asString() << EMPTY; + if (states[i]["IPCPort"].asInt() == IPCPort + || (IPCPort == 0 && states[i]["abilityName"].asString().compare("conveyorBelt") == 0 + )) { + if (states[i]["status"].asString() == "RUNNING") { + // std::unique_lock w_lock(mutex_); + LOG(INFO) << WHITE << "now ConveyorBelt is RUNNING" << EMPTY; + status = RUNNING; + IPCPort = states[i]["IPCPort"].asInt(); + abilityPort = states[i]["abilityPort"].asInt(); + if (abilityPort == 0) { return READY; } + else { return RUNNING; } + } + else if (states[i]["status"].asString() == "STANDBY" + || states[i]["status"].asString() == "SUSPEND") { + // std::unique_lock w_lock(mutex_); + LOG(INFO) << WHITE << "now ConveyorBelt is READY" << EMPTY; + status = READY; + IPCPort = states[i]["IPCPort"].asInt(); + abilityPort = 0; + // w_lock.unlock(); + return READY; + } + } + } + } + // std::unique_lock w_lock(mutex_); + LOG(INFO) << WHITE << "now ConveyorBelt is OFFLINE" << EMPTY; + status = OFFLINE; + abilityPort = 0; + IPCPort = 0; + // w_lock.unlock(); + return OFFLINE; +} */ + + +int ConveyorBeltController::stateSwitch(ConveyorBeltStatus destState, std::string cmd) { + httplib::Client client(ip, 8080); + httplib::Params params; + ConveyorBeltStatus mStatus; + // 发出请求 + params.emplace("abilityName", "conveyorBelt"); + params.emplace("IPCPort", std::to_string(IPCPort)); + params.emplace("cmd", cmd); + params.emplace("connectIP", "0.0.0.0"); + params.emplace("connectPort", "0"); + auto res = client.Post("/api/AbilityRequest", params); + if (res) { LOG(INFO) << WHITE << "POST response:" << res->status << EMPTY; } + else { + LOG(ERROR) << "SECOND STEP: can not connect with abilityFrameWork" << EMPTY; + return -1; + } + params.clear(); + // 查询当前状态 + for (int i = 0; i < 10; i++) { + res = client.Get("/api/AbilityRunning"); + if (res) { + LOG(INFO) << WHITE << "GET response:" << res->status << EMPTY; + mStatus = synchState(res->body); + if (mStatus == destState) { + LOG(INFO) << WHITE << "switch state success" << EMPTY; + return 0; + } + } + else { + LOG(ERROR) << "THIRD STEP: can not connect with abilityFrameWork" << EMPTY; + return -1; + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + LOG(ERROR) << "ability state change failed" << EMPTY; + return -1; +} + +int ConveyorBeltController::grpcConnect(float speed, int direction) { + if (abilityPort == 0) { return -1; } + + std::string url; + url.append(ip); + url.append(":"); + url.append(std::to_string(abilityPort)); + LOG(INFO) << WHITE << url << EMPTY; + int result; + for (int i = 0; i < 5; i++) { + ConveyorBeltGrpcClient client(grpc::CreateChannel(url, grpc::InsecureChannelCredentials())); + result = client.startWithSpeedAndDirection(speed, direction); + if (result != -2) { break; } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return result; +} + +int ConveyorBeltController::grpcDisconnect() { + if (abilityPort == 0) { return -1; } + std::string url; + url.append(ip); + url.append(":"); + url.append(std::to_string(abilityPort)); + LOG(INFO) << WHITE << url << EMPTY; + ConveyorBeltGrpcClient client(grpc::CreateChannel(url, grpc::InsecureChannelCredentials())); + if (client.close() == -1) { + LOG(ERROR) << "grpc call failed" << EMPTY; + return -1; + } + return 0; +} + +int ConveyorBeltController::grpcCheck() { + if (abilityPort == 0) { return -1; } + std::string url; + url.append(ip); + url.append(":"); + url.append(std::to_string(abilityPort)); + LOG(INFO) << WHITE << url << EMPTY; + ConveyorBeltGrpcClient client(grpc::CreateChannel(url, grpc::InsecureChannelCredentials())); + if (client.getWorkState() == -1) { + LOG(ERROR) << "grpc work state is false" << EMPTY; + return -1; + } + return 0; +} diff --git a/ConveyorBeltController/src/http_server.cpp.txt b/ConveyorBeltController/src/http_server.cpp.txt new file mode 100644 index 0000000..3cc8be2 --- /dev/null +++ b/ConveyorBeltController/src/http_server.cpp.txt @@ -0,0 +1,123 @@ +#include "http_server.h" + +HttpServer::HttpServer(ConveyorBeltController& controller) + : controller(controller) {} // 初始化引用 + + + +void HttpServer::Init() { + FLAGS_logtostderr = 1; + LOG(INFO) << L_GREEN << "init http server" << EMPTY; + // 初始化 http 服务器 + this->server = std::make_shared(); + + // 初始化控制器 + //this->controller = std::make_shared(); + // 初始化控制器 + //this->controller->init(); + + ipMap[108] = "192.168.1.217"; + ipMap[109] = "192.168.1.218"; + directionMap[108] = 0; + directionMap[109] = 0; + + this->server->Get( + "/api/processGet", + [this](const httplib::Request& req, httplib::Response& res) { + LOG(INFO) << L_BLUE << "httpserver receive: method: GET URL: /api/processGet" + << EMPTY; + std::string conveyorBeltId; + int result = 200; + Json::Value response; + try { + conveyorBeltId = req.get_param_value("id"); + LOG(INFO) << YELLOW << "GET param id:" << conveyorBeltId << EMPTY; + } + catch (const std::exception&) { + res.status = 400; // Bad Request + response["status"] = 400; + response["processStatus"] = this->controller.getCurrentStatus(); + res.set_content(response.toStyledString(), "application/json"); + return; + } + std::map::iterator it = ipMap.find(std::stoi(conveyorBeltId)); + if (it != ipMap.end()) { this->controller.setServerIp(it->second); } + else { + response["status"] = 400; + response["processStatus"] = this->controller.getCurrentStatus(); + res.set_content(response.toStyledString(), "application/json"); + return; + } + if (this->controller.handleCheck() < 0) { result = 400; } + response["status"] = result; + response["processStatus"] = this->controller.getCurrentStatus(); + res.set_content(response.toStyledString(), "application/json"); + } + ); + + this->server->Post( + "/api/processPost", + [this](const httplib::Request& req, httplib::Response& res) { + LOG(INFO) << L_BLUE << "httpserver receive: method: POST URL: /api/processPost" + << EMPTY; + LOG(INFO) << YELLOW << req.get_header_value("Content-Type") << EMPTY; + Json::Value root; + int status = 200; + if (req.get_header_value("Content-Type").find("application/json") + != std::string::npos) { + Json::Reader reader; + bool success = reader.parse(req.body, root); + if (!success) { + LOG(ERROR) << "json parse error"; + res.status = 400; + status = 400; + } + else { + LOG(INFO) << YELLOW << "id:" << root["id"].asInt() + << " action:" << root["action"].asInt() + << " speed:" << root["speed"].asFloat() << EMPTY; + Json::Value result; + std::map::iterator it = ipMap.find(root["id"].asInt()); + if (it != ipMap.end()) { this->controller.setServerIp(it->second); } + else { + result["status"] = 400; + result["processStatus"] = this->controller.getCurrentStatus(); + res.set_content(result.toStyledString(), "application/json"); + return; + } + if (root["action"].asInt() == 1) { + LOG(INFO) << YELLOW << "http server try to handle open" << EMPTY; + std::thread openThread( + &ConveyorBeltController::handleOpen, + this->controller, + root["speed"].asFloat(), + directionMap[root["id"].asInt()] + ); + openThread.detach(); + } + else if (root["action"].asInt() == 0) { + LOG(INFO) << YELLOW << "http server try to handle close" << EMPTY; + std::thread closeThread( + &ConveyorBeltController::handleClose, this->controller + ); + closeThread.detach(); + } + else { + LOG(INFO) << YELLOW << "invalid action code" << EMPTY; + status = 400; + } + } + } + else { status = 400; } + Json::Value result; + result["status"] = status; + result["processStatus"] = this->controller.getCurrentStatus(); + res.set_content(result.toStyledString(), "application/json"); + } + ); +} + +void HttpServer::Run() { + LOG(INFO) << L_GREEN << "http server start" << EMPTY; + this->server->listen("0.0.0.0", 8001); +} diff --git a/ConveyorBeltController/src/main.cpp b/ConveyorBeltController/src/main.cpp new file mode 100644 index 0000000..c0bcd25 --- /dev/null +++ b/ConveyorBeltController/src/main.cpp @@ -0,0 +1,48 @@ +#include "conveyorBeltController.hpp" +#include "ability_sdk/AbilityStub.hpp" +#include "ability_sdk/util/parse_cmd.hpp" + + +int main(int argc, char const* argv[]) { + // 初始化glog + ability_sdk::configure_glog(argv[0]); + + ability_sdk::CmdInfo cmd(argc, argv); + ConveyorBeltController controller; + + // 配置心跳包 + controller.heartbeat_pack.controllerInstanceId = uuids::to_string(cmd.instance_id); + controller.heartbeat_pack.abilityName = "ConveyorBelt"; + controller.heartbeat_pack.package = "ConveyorBelt.test.org"; + controller.heartbeat_pack.protocol = "http"; + controller.heartbeat_pack.version = "0.1.0"; + + controller.Run(); + return 0; +} + + +/* struct ControllerHeartbeat { + int port; // 控制器基础服务器对应的端口 + std::string controllerInstanceId; // 控制器的实例id 能力的类id + std::string package; // 控制器所对应能力的包名称 + std::string version; // 控制器所对应能力的版本名 + std::string abilityName; // 控制器所对应能力的类型名 + std::string protocol; // 控制器所使用的协议 + NLOHMANN_DEFINE_TYPE_INTRUSIVE( + ControllerHeartbeat, port, controllerInstanceId, package, version, abilityName, protocol + ); +}; */ + + +/* #include "http_server.h" + +// 控制器在http服务器中启动 +int main(int argc, char* argv[]) { + google::InitGoogleLogging(argv[0]); + HttpServer server; + server.Init(); + server.Run(); +} */ + + diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..f7dd046 --- /dev/null +++ b/Readme.md @@ -0,0 +1 @@ +传送带控制单元 conveyorBeltControlUnit \ No newline at end of file diff --git a/package-index.lua b/package-index.lua new file mode 100644 index 0000000..7dd8717 --- /dev/null +++ b/package-index.lua @@ -0,0 +1,32 @@ +package("wiringPi") + add_urls("https://github.com/WiringPi/WiringPi/archive/refs/tags/$(version).tar.gz") + add_versions("3.14","71d8b4c3bc967cf77ac15fad38791e9976cded31798715cdd3abbf8cca5cd401") + if is_plat("linux") then + add_extsources("apt::wiringpi") + end + on_install(function(package) + io.writefile( + "xmake.lua", + [[add_rules("mode.debug", "mode.release") + target("wiringPi") + set_kind("static") + add_files("wiringPi/*.c") + add_headerfiles("wiringPi/wiringPi.h") + add_includedirs("wiringPi") + ]]) + import("package.tools.xmake").install(package) + end) +package_end() + +package("my-libmodbus") + set_base("libmodbus") + on_install(function(package) + local configs = {"--disable-tests"}; + if not package:config("shared") then + table.insert(configs,"--enable-static=yes") + table.insert(configs,"--enable-shared=no") + end + import("package.tools.autoconf").install(package,configs) + end) +package_end() + diff --git a/protos/abilityProto.proto b/protos/abilityProto.proto new file mode 100644 index 0000000..35f03e3 --- /dev/null +++ b/protos/abilityProto.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; +package abilityUnit; +option java_package = "ability.proto"; +option java_outer_classname = "AbilityProto"; +option java_multiple_files = true; + +service Ability{ + rpc Start(StartInfo) returns (Response){} + rpc Connect (ConnectInfo) returns (Response) {} + rpc Disconnect(DisconnectInfo) returns (Response){} + rpc Terminate(TerminateInfo) returns (Response){} +} + +message Response{ + int32 code = 1; + string msg = 2; +} + +message StartInfo{ + int32 timestamp = 1; +} + +message ConnectInfo{ + string ip = 1; + int32 port = 2; + int32 timestamp = 3; +} + +message DisconnectInfo{ + int32 timestamp = 1; +} + +message TerminateInfo{ + int32 timestamp = 1; +} \ No newline at end of file diff --git a/protos/conveyorBelt.proto b/protos/conveyorBelt.proto new file mode 100644 index 0000000..1ee0f52 --- /dev/null +++ b/protos/conveyorBelt.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; +package ConveyorBelt; +option java_multiple_files = true; +option java_package = "ConveyorBelt.proto"; + +service ConveyorBeltService { + rpc open(SpeedInfo) returns (Response) {} + rpc close(DeviceID) returns (Response) {} + rpc setSpeed(SpeedInfo) returns (Response) {} + rpc setDirection(DirectionInfo) returns (Response) {} + rpc getWorkState(DeviceID) returns (Response) {} +} + +message Response{ + int32 code = 1; + string msg = 2; +} + +message SpeedInfo{ + float speed = 1; +} + +message DeviceID{ + int32 conveyorBeltId = 1; +} + +message DirectionInfo{ + int32 direction = 1; +} \ No newline at end of file diff --git a/protos/makefile b/protos/makefile new file mode 100644 index 0000000..bc356c7 --- /dev/null +++ b/protos/makefile @@ -0,0 +1,13 @@ +all: abilityProto.grpc.pb.cc abilityProto.grpc.pb.h conveyorBelt.grpc.pb.cc conveyorBelt.grpc.pb.h abilityProto.pb.cc abilityProto.pb.h conveyorBelt.pb.cc conveyorBelt.pb.h +abilityProto.grpc.pb.cc abilityProto.grpc.pb.h: + protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` abilityProto.proto +conveyorBelt.grpc.pb.cc conveyorBelt.grpc.pb.h: + protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` conveyorBelt.proto + +abilityProto.pb.cc abilityProto.pb.h: + protoc -I . --cpp_out=. abilityProto.proto +conveyorBelt.pb.cc conveyorBelt.pb.h: + protoc -I . --cpp_out=. conveyorBelt.proto + +clean: + rm abilityProto.grpc.pb.cc abilityProto.grpc.pb.h conveyorBelt.grpc.pb.cc conveyorBelt.grpc.pb.h abilityProto.pb.cc abilityProto.pb.h conveyorBelt.pb.cc conveyorBelt.pb.h \ No newline at end of file diff --git a/test/test-controller-api.py b/test/test-controller-api.py new file mode 100644 index 0000000..f3abd57 --- /dev/null +++ b/test/test-controller-api.py @@ -0,0 +1,51 @@ +import requests +import json + +# 设置服务器地址和端口 +BASE_URL = "http://127.0.0.1:1" + +def test_process_get(conveyor_belt_id): + """ + 测试 /api/processGet 接口 + """ + url = f"{BASE_URL}/api/processGet" + params = {"id": conveyor_belt_id} + print(f"Testing GET {url} with params: {params}") + + try: + response = requests.get(url, params=params) + print(f"Response Status Code: {response.status_code}") + print(f"Response Body: {response.json()}") + except Exception as e: + print(f"Error during GET request: {e}") + +def test_process_post(conveyor_belt_id, action, speed): + """ + 测试 /api/processPost 接口 + """ + url = f"{BASE_URL}/api/processPost" + headers = {"Content-Type": "application/json"} + payload = { + "id": conveyor_belt_id, + "action": action, + "speed": speed + } + print(f"Testing POST {url} with payload: {json.dumps(payload, indent=4)}") + + try: + response = requests.post(url, headers=headers, json=payload) + print(f"Response Status Code: {response.status_code}") + print(f"Response Body: {response.json()}") + except Exception as e: + print(f"Error during POST request: {e}") + +if __name__ == "__main__": + # 测试 /api/processGet + print("=== Testing /api/processGet ===") + test_process_get(conveyor_belt_id=108) # 替换为实际的传送带 ID + + # 测试 /api/processPost + print("\n=== Testing /api/processPost ===") + test_process_post(conveyor_belt_id=108, action=1, speed=100) # 启动传送带 + test_process_post(conveyor_belt_id=108, action=0, speed=0) # 停止传送带 + test_process_post(conveyor_belt_id=108, action=2, speed=50) # 无效操作测试 \ No newline at end of file diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..9fb1265 --- /dev/null +++ b/xmake.lua @@ -0,0 +1,61 @@ +-- 设置项目基本配置 +add_rules("mode.debug", "mode.release") +add_languages("c++20") +set_policy("package.install_locally", true) + +-- 包含 SDK 的构建配置 +includes("AbilitySDK/xmake.lua") + + +add_requires("wiringPi") +add_requires("my-libmodbus",{alias="libmodbus"}) + +add_requires("jsoncpp") + +add_requires("protobuf-cpp") +add_requires("grpc") + +add_requires("abseil", {alias = "abseil"}) + +includes("package-index.lua") + +-- 添加包含路径 +add_includedirs("AbilitySDK/include") + + +target("protos") + set_kind("object") + add_packages("protobuf-cpp","grpc",{public=true}) + add_rules("protobuf.cpp") + add_files("protos/conveyorBelt.proto", {proto_public=true,proto_rootdir = "protos", proto_grpc_cpp_plugin = true}) + -- 这是旧版api使用的protos,移植到新版后可以去除 + add_files("protos/abilityProto.proto", {proto_public=true,proto_rootdir = "protos", proto_grpc_cpp_plugin = true}) + + +-- ConveyorBeltControlUnit 库 +target("conveyor-control-unit") + set_kind("static") + add_includedirs("ConveyorBeltControlUnit/include", {public = true}) + add_files("ConveyorBeltControlUnit/src/*.cpp") + --add_packages("cpp-httplib","jsoncpp","glog","libmodbus","wiringPi","abseil",{public=true}) + add_packages("cpp-httplib","jsoncpp","glog","libmodbus","abseil",{public=true}) + + +-- ConveyorBeltAbility 库 +target("conveyor-ability") + set_kind("binary") + add_includedirs("ConveyorBeltAbility/include", {public = true}) + add_files("ConveyorBeltAbility/src/*.cpp") + add_deps("AbilitySDK", "conveyor-control-unit", "protos") + add_packages("cpp-httplib","jsoncpp","glog","abseil",{public=true}) + + +-- ConveyorBeltController 库 +target("conveyor-controller") + set_kind("binary") + add_includedirs("ConveyorBeltControlUnit/include", {public = true}) + add_includedirs("ConveyorBeltController/include", {public = true}) + add_files("ConveyorBeltController/src/*.cpp") + add_deps("AbilitySDK", "protos") + add_packages("cpp-httplib","jsoncpp","libmodbus","glog","abseil",{public=true}) +