mirror of
https://github.com/eddyem/BTA_utils.git
synced 2025-12-06 02:35:13 +03:00
pre-pre alpha of Network_CAN_clientserver
This commit is contained in:
parent
882c782c52
commit
c16e343961
73
Network_CAN_clientserver/CMakeLists.txt
Normal file
73
Network_CAN_clientserver/CMakeLists.txt
Normal file
@ -0,0 +1,73 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
set(PROJ canserv)
|
||||
set(MINOR_VERSION "1")
|
||||
set(MID_VERSION "0")
|
||||
set(MAJOR_VERSION "0")
|
||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||
|
||||
project(${PROJ} VERSION ${VERSION} LANGUAGES C)
|
||||
|
||||
message("VERSION: ${VERSION}")
|
||||
|
||||
# list of options
|
||||
option(DEBUG "Compile in debug mode" OFF)
|
||||
option(EXAMPLES "Compile also all examples" ON)
|
||||
|
||||
# default flags
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=gnu99")
|
||||
set(CMAKE_COLOR_MAKEFILE ON)
|
||||
|
||||
# here is one of two variants: all .c in directory or .c files in list
|
||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||
|
||||
# cmake -DDEBUG=yes -> debugging
|
||||
if(DEBUG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -fno-builtin-strlen -Werror")
|
||||
add_definitions(-DEBUG)
|
||||
set(CMAKE_BUILD_TYPE DEBUG)
|
||||
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
|
||||
set(CMAKE_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
###### pkgconfig ######
|
||||
# pkg-config modules (for pkg-check-modules)
|
||||
set(MODULES usefull_macros>=0.3.2)
|
||||
# find packages:
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
|
||||
|
||||
# external modules like OpenMP:
|
||||
include(FindOpenMP)
|
||||
if(OPENMP_FOUND)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
|
||||
add_definitions(-DOMP_FOUND)
|
||||
endif()
|
||||
|
||||
###### additional flags ######
|
||||
#list(APPEND ${PROJ}_LIBRARIES "-lfftw3_threads")
|
||||
add_executable(${PROJ} ${SOURCES} ${PO_FILE})
|
||||
# -I
|
||||
target_include_directories(${PROJ} PUBLIC ${${PROJ}_INCLUDE_DIRS} .)
|
||||
# -L
|
||||
target_link_directories(${PROJ} PUBLIC ${${PROJ}_LIBRARY_DIRS} )
|
||||
# -D
|
||||
add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE -DPACKAGE_VERSION=\"${VERSION}\"
|
||||
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
|
||||
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
|
||||
# -l
|
||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
|
||||
|
||||
# Installation of the program
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS ${PROJ} DESTINATION "bin")
|
||||
|
||||
# EXAMPLES
|
||||
if(EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
1
Network_CAN_clientserver/Readme
Normal file
1
Network_CAN_clientserver/Readme
Normal file
@ -0,0 +1 @@
|
||||
simple server for operate with FX3U-clone over TCP sockets
|
||||
92
Network_CAN_clientserver/canproto.h
Normal file
92
Network_CAN_clientserver/canproto.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of the fx3u project.
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// command parameter flag means this is a setter
|
||||
#define SETTER_FLAG (0x80)
|
||||
#define ISSETTER(data) ((data[2] & SETTER_FLAG))
|
||||
// parameter number 127 means there no parameter number at all (don't need paremeter or get all)
|
||||
#define NO_PARNO (0x7f)
|
||||
// base value of parameter (even if it is a setter)
|
||||
#define PARBASE(x) (x & 0x7f)
|
||||
// get parameter value of msg->data
|
||||
#define PARVAL(data) (data[2] & 0x7f)
|
||||
|
||||
// make error for CAN answer
|
||||
#define FORMERR(m, err) do{m->data[3] = err; if(m->length < 4) m->length = 4;}while(0)
|
||||
|
||||
// error codes for answer message
|
||||
typedef enum{
|
||||
ERR_OK, // 0 - all OK
|
||||
ERR_BADPAR, // 1 - parameter is wrong
|
||||
ERR_BADVAL, // 2 - wrong value
|
||||
ERR_WRONGLEN, // 3 - wrong message length
|
||||
ERR_BADCMD, // 4 - unknown command
|
||||
ERR_CANTRUN, // 5 - can't run given command due to bad parameters or other
|
||||
ERR_AMOUNT // amount of error codes
|
||||
} errcodes;
|
||||
|
||||
// set command bytes in CAN message
|
||||
#define MSG_SET_CMD(msg, cmd) do{*((uint16_t*)msg.data) = (cmd);}while(0)
|
||||
#define MSGP_SET_CMD(msg, cmd) do{*((uint16_t*)msg->data) = (cmd);}while(0)
|
||||
#define MSG_GET_CMD(msg) (*(uint16_t*)msg.data)
|
||||
#define MSGP_GET_CMD(msg) (*(uint16_t*)msg->data)
|
||||
// set command parameter number
|
||||
#define MSG_SET_PARNO(msg, n) do{msg.data[2] = (n);}while(0)
|
||||
#define MSGP_SET_PARNO(msg, n) do{msg->data[2] = (n);}while(0)
|
||||
// set error
|
||||
#define MSG_SET_ERR(msg, err) do{msg.data[3] = (err);}while(0)
|
||||
#define MSGP_SET_ERR(msg, err) do{msg->data[3] = (err);}while(0)
|
||||
// set uint32_t data
|
||||
#define MSG_SET_U32(msg, d) do{*((uint32_t*)(&msg.data[4])) = (d);}while(0)
|
||||
#define MSGP_SET_U32(msg, d) do{*((uint32_t*)(&msg->data[4])) = (d);}while(0)
|
||||
// get uint32_t data
|
||||
#define MSG_GET_U32(msg) (*(uint32_t*)&msg.data[4])
|
||||
#define MSGP_GET_U32(msg) (*(uint32_t*)&msg->data[4])
|
||||
|
||||
// CAN commands indexes
|
||||
enum{
|
||||
CMD_PING, // just ping
|
||||
CMD_RESET, // reset MCU
|
||||
CMD_TIME, // get/set Tms
|
||||
CMD_MCUTEMP, // get MCU temperature (*10)
|
||||
CMD_ADCRAW, // get ADC raw values
|
||||
CMD_CANSPEED, // get/set CAN speed (kbps)
|
||||
CMD_CANID, // get/set common CAN ID (both in and out)
|
||||
CMD_CANIDin, // input CAN ID
|
||||
CMD_CANIDout, // output CAN ID
|
||||
CMD_SAVECONF, // save configuration
|
||||
CMD_ERASESTOR, // erase all flash storage
|
||||
CMD_RELAY, // switch relay ON/OFF
|
||||
CMD_GETESW, // current ESW state, bounce-free
|
||||
CMD_GETESWNOW, // current ESW state, absolute
|
||||
CMD_BOUNCE, // get/set bounce constant (ms)
|
||||
CMD_USARTSPEED, // get/set USART1 speed
|
||||
CMD_LED, // onboard LED
|
||||
CMD_FLAGS, // flags setter/getter
|
||||
CMD_INCHNLS, // all bits set are active supported IN channels
|
||||
CMD_OUTCHNLS, // all bits set are active supported OUT channels
|
||||
CMD_MODBUSID, // set/get modbus slave ID (or 0 if master)
|
||||
CMD_MODBUSIDOUT,// slave ID to send modbus relay command if IN changes
|
||||
CMD_MODBUSSPEED,// speed of modbus interface
|
||||
// should be the last:
|
||||
CMD_AMOUNT // amount of CAN commands
|
||||
};
|
||||
|
||||
|
||||
1
Network_CAN_clientserver/canserver.cflags
Normal file
1
Network_CAN_clientserver/canserver.cflags
Normal file
@ -0,0 +1 @@
|
||||
-std=c17
|
||||
5
Network_CAN_clientserver/canserver.config
Normal file
5
Network_CAN_clientserver/canserver.config
Normal file
@ -0,0 +1,5 @@
|
||||
// Add predefined macros for your project here. For example:
|
||||
// #define THE_ANSWER 42
|
||||
#define XOPEN_SOURCE 1234
|
||||
#define DEFAULT_SOURCE
|
||||
#define GNU_SOURCE
|
||||
1
Network_CAN_clientserver/canserver.creator
Normal file
1
Network_CAN_clientserver/canserver.creator
Normal file
@ -0,0 +1 @@
|
||||
[General]
|
||||
184
Network_CAN_clientserver/canserver.creator.user
Normal file
184
Network_CAN_clientserver/canserver.creator.user
Normal file
@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 16.0.0, 2025-04-28T11:38:19. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="qlonglong">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
|
||||
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||
</valuemap>
|
||||
<value type="bool" key="AutoTest.ApplyFilter">false</value>
|
||||
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
|
||||
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||
<valuemap type="QVariantMap" key="ClangTools">
|
||||
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">4</value>
|
||||
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/eddy/Docs/SAO/BTA/ACS-АСУ/Network_CAN_clientserver</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
1
Network_CAN_clientserver/canserver.cxxflags
Normal file
1
Network_CAN_clientserver/canserver.cxxflags
Normal file
@ -0,0 +1 @@
|
||||
-std=c++17
|
||||
8
Network_CAN_clientserver/canserver.files
Normal file
8
Network_CAN_clientserver/canserver.files
Normal file
@ -0,0 +1,8 @@
|
||||
canproto.h
|
||||
clientserver.c
|
||||
clientserver.h
|
||||
globopts.c
|
||||
globopts.h
|
||||
main.c
|
||||
parsecanmsgs.c
|
||||
parsecanmsgs.h
|
||||
0
Network_CAN_clientserver/canserver.includes
Normal file
0
Network_CAN_clientserver/canserver.includes
Normal file
245
Network_CAN_clientserver/clientserver.c
Normal file
245
Network_CAN_clientserver/clientserver.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* This file is part of the canserver project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "clientserver.h"
|
||||
#include "parsecanmsgs.h"
|
||||
|
||||
static pthread_t clientthread;
|
||||
sl_sock_t *serialsock = NULL;
|
||||
|
||||
#define CMDIN "in"
|
||||
#define CMDOUT "out"
|
||||
#define CMDMCUT "mcut"
|
||||
#define CMDLED "led"
|
||||
#define CMDINCH "inchnls"
|
||||
#define CMDOUTCH "outchnls"
|
||||
#define CMDTIME "time"
|
||||
#define CMDPING "ping"
|
||||
|
||||
typedef struct{
|
||||
const char *textcmd;
|
||||
int cancmd;
|
||||
int havesetter;
|
||||
} commands;
|
||||
|
||||
// list of all supported commands
|
||||
static commands allcommands[] = {
|
||||
{CMDIN, CMD_GETESW, 0},
|
||||
{CMDOUT, CMD_RELAY, 1},
|
||||
{CMDMCUT, CMD_MCUTEMP, 0},
|
||||
{CMDLED, CMD_LED, 1},
|
||||
{CMDINCH, CMD_INCHNLS, 0},
|
||||
{CMDOUTCH, CMD_OUTCHNLS, 0},
|
||||
{CMDTIME, CMD_TIME, 1},
|
||||
{CMDPING, CMD_PING, 1},
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static int Relay1cmds(CAN_message *msg, char buf[BUFSIZ]){
|
||||
int L = 0;
|
||||
uint16_t cmd = MSGP_GET_CMD(msg);
|
||||
uint8_t par = PARVAL(msg->data);
|
||||
uint32_t data = MSGP_GET_U32(msg);
|
||||
commands *c = allcommands;
|
||||
while(c->textcmd){
|
||||
if(cmd == c->cancmd) break;
|
||||
++c;
|
||||
}
|
||||
if(!c->textcmd) return 0;
|
||||
DBG("found text cmd is %s (%u)", c->textcmd, data);
|
||||
if(par == NO_PARNO) L = snprintf(buf, BUFSIZ-1, "%s=%u\n", c->textcmd, data);
|
||||
else L = snprintf(buf, BUFSIZ-1, "%s[%d]=%u\n", c->textcmd, par, data);
|
||||
return L;
|
||||
}
|
||||
|
||||
// check CAN_message and if recognize send ans to all clients
|
||||
static void gotCANans(CAN_message *msg){
|
||||
if(!msg) return;
|
||||
char buf[BUFSIZ];
|
||||
int L = 0;
|
||||
switch(msg->ID){
|
||||
case CANIDOUT:
|
||||
L = Relay1cmds(msg, buf);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if(L < 1) return;
|
||||
DBG("BUF: %s", buf);
|
||||
int N = sl_sock_sendall((uint8_t*) buf, L);
|
||||
green("Send to %d clients\n", N);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief clientproc - process data received from serial terminal
|
||||
* @param par - socket
|
||||
* @return NULL
|
||||
*/
|
||||
void *clientproc(void _U_ *par){
|
||||
FNAME();
|
||||
char rbuf[BUFSIZ];
|
||||
if(!serialsock) return NULL;
|
||||
do{
|
||||
ssize_t got = sl_sock_readline(serialsock, rbuf, BUFSIZ);
|
||||
if(got < 0){ // disconnected
|
||||
WARNX("Serial server disconnected");
|
||||
if(serialsock) sl_sock_delete(&serialsock);
|
||||
return NULL;
|
||||
}else if(got == 0){ // nothing to read from serial port
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
// process data
|
||||
DBG("GOT: %s", rbuf);
|
||||
CAN_message msg;
|
||||
if(parseCANstr(rbuf, &msg)){
|
||||
DBG("Got CAN message");
|
||||
gotCANans(&msg);
|
||||
}
|
||||
} while(serialsock && serialsock->connected);
|
||||
WARNX("disconnected");
|
||||
if(serialsock) sl_sock_delete(&serialsock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e dtimeh(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
char buf[32];
|
||||
snprintf(buf, 31, "UNIXT=%.2f\n", sl_dtime());
|
||||
sl_sock_sendstrmessage(client, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e procUserCmd(sl_sock_t _U_ *client, sl_sock_hitem_t *item, const char *req){
|
||||
#define CBUFLEN (128)
|
||||
char buf[CBUFLEN];
|
||||
int buflen = 0;
|
||||
CAN_message msg;
|
||||
msg.ID = CANIDIN;
|
||||
int issetter = 0;
|
||||
uint8_t parno = NO_PARNO;
|
||||
uint32_t setterval = 0;
|
||||
if(req){
|
||||
int N;
|
||||
if(!sl_str2i(&N, req) || N < 0){
|
||||
DBG("BAD value %d", N);
|
||||
return RESULT_BADVAL;
|
||||
}
|
||||
setterval = (uint32_t) N;
|
||||
issetter = 1;
|
||||
}
|
||||
commands *c = allcommands;
|
||||
while(c->textcmd){
|
||||
if(0 == strcmp(c->textcmd, item->key)) break;
|
||||
++c;
|
||||
}
|
||||
if(!c->textcmd) return RESULT_BADKEY;
|
||||
if(issetter){
|
||||
DBG("setter");
|
||||
if(!c->havesetter) return RESULT_BADVAL;
|
||||
if(CANu32setter(c->cancmd, parno, setterval, &msg) && (buflen = formCANmsg(&msg, buf, CBUFLEN-1)) > 0){
|
||||
}else{
|
||||
WARNX("Can't form message");
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
}else{
|
||||
DBG("getter");
|
||||
if(CANu32getter(c->cancmd, parno, &msg) && (buflen = formCANmsg(&msg, buf, CBUFLEN-1)) > 0){
|
||||
}else{
|
||||
WARNX("Can't form message");
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
}
|
||||
if(buflen) sl_sock_sendstrmessage(serialsock, buf);
|
||||
return RESULT_SILENCE;
|
||||
#undef CBUFLEN
|
||||
}
|
||||
/*
|
||||
static sl_sock_hresult_e show(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(!client) return RESULT_FAIL;
|
||||
if(client->type != SOCKT_UNIX){
|
||||
if(*client->IP){
|
||||
printf("Client \"%s\" (fd=%d) ask for flags:\n", client->IP, client->fd);
|
||||
}else printf("Can't get client's IP, flags:\n");
|
||||
}else printf("Socket fd=%d asks for flags:\n", client->fd);
|
||||
return RESULT_OK;
|
||||
}*/
|
||||
|
||||
// Too much clients handler
|
||||
static void toomuch(int fd){
|
||||
const char m[] = "Try later: too much clients connected\n";
|
||||
send(fd, m, sizeof(m)-1, MSG_NOSIGNAL);
|
||||
shutdown(fd, SHUT_WR);
|
||||
DBG("shutdown, wait");
|
||||
double t0 = sl_dtime();
|
||||
uint8_t buf[8];
|
||||
while(sl_dtime() - t0 < 11.){
|
||||
if(sl_canread(fd)){
|
||||
ssize_t got = read(fd, buf, 8);
|
||||
DBG("Got=%zd", got);
|
||||
if(got < 1) break;
|
||||
}
|
||||
}
|
||||
DBG("Disc after %gs", sl_dtime() - t0);
|
||||
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
||||
}
|
||||
// new connections handler
|
||||
static void connected(sl_sock_t *c){
|
||||
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
|
||||
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
||||
}
|
||||
// disconnected handler
|
||||
static void disconnected(sl_sock_t *c){
|
||||
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
|
||||
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
||||
}
|
||||
|
||||
static sl_sock_hitem_t handlers[] = {
|
||||
{dtimeh, "dtime", "get server's UNIX time for all clients connected", NULL},
|
||||
{procUserCmd, CMDOUT, "outputs setter/getter", NULL},
|
||||
{procUserCmd, CMDIN, "inputs getter", NULL},
|
||||
{procUserCmd, CMDMCUT, "get MCU temperature (/10degC)", NULL},
|
||||
{procUserCmd, CMDLED, "onboard LED set/get", NULL},
|
||||
{procUserCmd, CMDINCH, "get available input channels mask", NULL},
|
||||
{procUserCmd, CMDOUTCH, "get available output channels mask", NULL},
|
||||
{procUserCmd, CMDTIME, "get MCU milliseconds counter value", NULL},
|
||||
{procUserCmd, CMDPING, "ping controller", NULL},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
sl_sock_t *RunSrv(sl_socktype_e type, const char *node){
|
||||
sl_sock_maxclhandler(toomuch);
|
||||
sl_sock_connhandler(connected);
|
||||
sl_sock_dischandler(disconnected);
|
||||
return sl_sock_run_server(type, node, 4096, handlers);
|
||||
}
|
||||
|
||||
sl_sock_t *RunClt(sl_socktype_e type, const char *node){
|
||||
serialsock = sl_sock_run_client(type, node, 4096);
|
||||
if(!serialsock){
|
||||
DBG("Can't run client");
|
||||
return NULL;
|
||||
}
|
||||
if(pthread_create(&clientthread, NULL, clientproc, NULL)) return NULL;
|
||||
return serialsock;
|
||||
}
|
||||
24
Network_CAN_clientserver/clientserver.h
Normal file
24
Network_CAN_clientserver/clientserver.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the canserver project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <usefull_macros.h>
|
||||
|
||||
sl_sock_t *RunSrv(sl_socktype_e type, const char *node);
|
||||
sl_sock_t *RunClt(sl_socktype_e type, const char *node);
|
||||
41
Network_CAN_clientserver/globopts.c
Normal file
41
Network_CAN_clientserver/globopts.c
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of the canserver project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "globopts.h"
|
||||
#include <usefull_macros.h>
|
||||
|
||||
globopts UserOpts = {
|
||||
.maxclients = 10,
|
||||
};
|
||||
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&UserOpts.help), "show this help"},
|
||||
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&UserOpts.verbose), "verbose level (each -v adds 1)"},
|
||||
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&UserOpts.logfile), "log file name"},
|
||||
{"srvnode", NEED_ARG, NULL, 'N', arg_string, APTR(&UserOpts.srvnode), "server node \"port\", \"name:port\" or path (could be \"\\0path\" for anonymous UNIX-socket)"},
|
||||
{"srvunix", NO_ARGS, NULL, 'U', arg_int, APTR(&UserOpts.srvunix), "server use UNIX socket instead of INET"},
|
||||
{"maxclients", NEED_ARG, NULL, 'm', arg_int, APTR(&UserOpts.maxclients), "max amount of clients connected to server (default: 2)"},
|
||||
{"cltnode", NEED_ARG, NULL, 'n', arg_string, APTR(&UserOpts.cltnode), "serial client node \"IP\", \"name:IP\" or path (could be \"\\0path\" for anonymous UNIX-socket)"},
|
||||
{"cltunix", NO_ARGS, NULL, 'u', arg_int, APTR(&UserOpts.cltunix), "serial client use UNIX socket instead of INET"},
|
||||
end_option
|
||||
};
|
||||
|
||||
void parseargs(int argc, char **argv){
|
||||
sl_parseargs(&argc, &argv, cmdlnopts);
|
||||
if(UserOpts.help) sl_showhelp(-1, cmdlnopts);
|
||||
}
|
||||
34
Network_CAN_clientserver/globopts.h
Normal file
34
Network_CAN_clientserver/globopts.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of the canserver project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct{
|
||||
int help;
|
||||
int verbose;
|
||||
int srvunix;
|
||||
int cltunix;
|
||||
int maxclients;
|
||||
char *logfile;
|
||||
char *srvnode;
|
||||
char *cltnode;
|
||||
} globopts;
|
||||
|
||||
extern globopts UserOpts;
|
||||
|
||||
void parseargs(int argc, char **argv);
|
||||
81
Network_CAN_clientserver/main.c
Normal file
81
Network_CAN_clientserver/main.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of the canserver project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "clientserver.h"
|
||||
#include "globopts.h"
|
||||
|
||||
static sl_sock_t *srv = NULL, *clt = NULL;
|
||||
|
||||
void signals(int sig){
|
||||
if(sig){
|
||||
signal(sig, SIG_IGN);
|
||||
DBG("Get signal %d, quit.\n", sig);
|
||||
LOGERR("Exit with status %d", sig);
|
||||
}else LOGERR("Exit");
|
||||
if(srv){
|
||||
DBG("Del server");
|
||||
sl_sock_delete(&srv);
|
||||
}
|
||||
if(clt){
|
||||
DBG("Del client");
|
||||
sl_sock_delete(&clt);
|
||||
}
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv){
|
||||
sl_init();
|
||||
parseargs(argc, argv);
|
||||
if(!UserOpts.srvnode) ERRX("Point server node");
|
||||
if(!UserOpts.cltnode) ERRX("Point serial client node");
|
||||
sl_socktype_e type = (UserOpts.srvunix) ? SOCKT_UNIX : SOCKT_NET;
|
||||
sl_sock_changemaxclients(UserOpts.maxclients);
|
||||
srv = RunSrv(type, UserOpts.srvnode);
|
||||
if(!srv) ERRX("Server: can't create socket and/or run threads");
|
||||
DBG("Server done");
|
||||
type = (UserOpts.cltunix) ? SOCKT_UNIX : SOCKT_NET;
|
||||
clt = RunClt(type, UserOpts.cltnode);
|
||||
if(!clt) ERRX("Serial client: can't connect to socket and/or run threads");
|
||||
DBG("Client done");
|
||||
sl_loglevel_e lvl = UserOpts.verbose + LOGLEVEL_ERR;
|
||||
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
||||
if(UserOpts.logfile) OPENLOG(UserOpts.logfile, lvl, 1);
|
||||
LOGMSG("Started");
|
||||
signal(SIGTERM, signals);
|
||||
signal(SIGINT, signals);
|
||||
signal(SIGQUIT, signals);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGHUP, signals);
|
||||
while(srv && srv->connected && clt && clt->connected){
|
||||
if(!srv->rthread){
|
||||
WARNX("Server handlers thread is dead");
|
||||
LOGERR("Server handlers thread is dead");
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOGMSG("End");
|
||||
DBG("Close");
|
||||
if(srv) sl_sock_delete(&srv);
|
||||
if(clt) sl_sock_delete(&clt);
|
||||
return 0;
|
||||
}
|
||||
91
Network_CAN_clientserver/parsecanmsgs.c
Normal file
91
Network_CAN_clientserver/parsecanmsgs.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* This file is part of the canserver project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "parsecanmsgs.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief parseCANstr - parser of string like "timestamp #ID [data0 data1 ...]"
|
||||
* @param str - input string
|
||||
* @param msg - message to fill
|
||||
* @return FALSE if given string isn't CAN message
|
||||
*/
|
||||
int parseCANstr(const char *str, CAN_message *msg){
|
||||
if(!str || !msg) return FALSE;
|
||||
const char *IDstart = strchr(str, '#');
|
||||
if(!IDstart) return FALSE;
|
||||
++IDstart;
|
||||
msg->timestamp = atoi(str);
|
||||
DBG("Timestamp: %u", msg->timestamp);
|
||||
unsigned bytes[9];
|
||||
int N = sscanf(IDstart, "%x %x %x %x %x %x %x %x %x",
|
||||
&bytes[0], &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5], &bytes[6], &bytes[7], &bytes[8]);
|
||||
if(N < 1) return FALSE; // where's ID?
|
||||
DBG("ID=%u", bytes[0]);
|
||||
msg->length = N - 1;
|
||||
msg->ID = bytes[0];
|
||||
for(int i = 0; i < msg->length; ++i) msg->data[i] = bytes[i + 1];
|
||||
DBG("length: %u", msg->length);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// return result of snprintf
|
||||
int formCANmsg(CAN_message *msg, char *buf, int buflen){
|
||||
if(!msg || !buf || buflen < 8) return 0;
|
||||
DBG("msg ID=%u, len=%u", msg->ID, msg->length);
|
||||
int L = snprintf(buf, buflen, "s %u ", msg->ID);
|
||||
for(int i = 0; i < msg->length && L < buflen; ++i)
|
||||
L += snprintf(buf + L, buflen - L, "%u ", msg->data[i]);
|
||||
if(L >= buflen){
|
||||
WARNX("buffer overflow");
|
||||
return 0;
|
||||
}
|
||||
L += snprintf(buf + L, buflen - L, "\n");
|
||||
DBG("convert CAN message to %d symbols: _%s_", L, buf);
|
||||
return L;
|
||||
}
|
||||
|
||||
// fill command and parameter into message
|
||||
// !!!!! msg->ID should be set by user !!!!!
|
||||
static int fillcmd(int cancmd, uint8_t par, CAN_message *msg){
|
||||
if(!msg || cancmd > 0xffff) return FALSE;
|
||||
MSGP_SET_CMD(msg, cancmd);
|
||||
MSGP_SET_PARNO(msg, par);
|
||||
MSGP_SET_ERR(msg, 0);
|
||||
msg->length = 4;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int CANu32setter(int cancmd, uint8_t par, uint32_t data, CAN_message *msg){
|
||||
if(!msg) return FALSE;
|
||||
par |= SETTER_FLAG;
|
||||
if(!fillcmd(cancmd, par, msg)) return FALSE;
|
||||
MSGP_SET_U32(msg, data);
|
||||
msg->length = 8;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int CANu32getter(int cancmd, uint8_t par, CAN_message *msg){
|
||||
if(!msg) return FALSE;
|
||||
if(!fillcmd(cancmd, par, msg)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
39
Network_CAN_clientserver/parsecanmsgs.h
Normal file
39
Network_CAN_clientserver/parsecanmsgs.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the canserver project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "canproto.h"
|
||||
|
||||
#define CANIDIN 1
|
||||
#define CANIDOUT 2
|
||||
|
||||
typedef struct{
|
||||
uint16_t ID; // message ID
|
||||
uint32_t timestamp; // milliseconds timestamp
|
||||
uint8_t length; // data length
|
||||
uint8_t data[8]; // up to 8 bytes of data
|
||||
} CAN_message;
|
||||
|
||||
int parseCANstr(const char *str, CAN_message *msg);
|
||||
int formCANmsg(CAN_message *msg, char *buf, int buflen);
|
||||
|
||||
int CANu32setter(int cancmd, uint8_t par, uint32_t data, CAN_message *msg);
|
||||
int CANu32getter(int cancmd, uint8_t par, CAN_message *msg);
|
||||
1
Network_CAN_clientserver/run
Normal file
1
Network_CAN_clientserver/run
Normal file
@ -0,0 +1 @@
|
||||
socat tcp-l:22222,reuseaddr,fork file:/dev/multistepper0,rawer
|
||||
Loading…
x
Reference in New Issue
Block a user