mirror of
https://github.com/eddyem/eddys_snippets.git
synced 2025-12-06 10:45:12 +03:00
added TeAmanagement
This commit is contained in:
parent
c54bdea9ed
commit
2b42f5a2a4
56
TeAmanagement/Makefile
Normal file
56
TeAmanagement/Makefile
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# run `make DEF=...` to add extra defines
|
||||||
|
PROGRAM := teacmd
|
||||||
|
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
|
||||||
|
LDFLAGS += -lusefull_macros
|
||||||
|
SRCS := $(wildcard *.c)
|
||||||
|
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
|
||||||
|
OBJDIR := mk
|
||||||
|
CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -std=gnu99
|
||||||
|
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
|
||||||
|
DEPS := $(OBJS:.o=.d)
|
||||||
|
TARGFILE := $(OBJDIR)/TARGET
|
||||||
|
CC = gcc
|
||||||
|
|
||||||
|
ifeq ($(shell test -e $(TARGFILE) && echo -n yes),yes)
|
||||||
|
TARGET := $(file < $(TARGFILE))
|
||||||
|
else
|
||||||
|
TARGET := RELEASE
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(TARGET), DEBUG)
|
||||||
|
.DEFAULT_GOAL := debug
|
||||||
|
endif
|
||||||
|
|
||||||
|
release: $(PROGRAM)
|
||||||
|
|
||||||
|
debug: CFLAGS += -DEBUG -Werror
|
||||||
|
debug: TARGET := DEBUG
|
||||||
|
debug: $(PROGRAM)
|
||||||
|
|
||||||
|
$(TARGFILE): $(OBJDIR)
|
||||||
|
@echo -e "\t\tTARGET: $(TARGET)"
|
||||||
|
@echo "$(TARGET)" > $(TARGFILE)
|
||||||
|
|
||||||
|
$(PROGRAM) : $(TARGFILE) $(OBJS)
|
||||||
|
@echo -e "\t\tLD $(PROGRAM)"
|
||||||
|
$(CC) $(LDFLAGS) $(OBJS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
$(OBJDIR):
|
||||||
|
@mkdir $(OBJDIR)
|
||||||
|
|
||||||
|
ifneq ($(MAKECMDGOALS),clean)
|
||||||
|
-include $(DEPS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: %.c
|
||||||
|
@echo -e "\t\tCC $<"
|
||||||
|
$(CC) -MD -c $(LDFLAGS) $(CFLAGS) $(DEFINES) -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo -e "\t\tCLEAN"
|
||||||
|
@rm -rf $(OBJDIR) 2>/dev/null || true
|
||||||
|
|
||||||
|
xclean: clean
|
||||||
|
@rm -f $(PROGRAM)
|
||||||
|
|
||||||
|
.PHONY: clean xclean
|
||||||
3
TeAmanagement/Readme
Normal file
3
TeAmanagement/Readme
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Client/server
|
||||||
|
|
||||||
|
|
||||||
1
TeAmanagement/TeAman.cflags
Normal file
1
TeAmanagement/TeAman.cflags
Normal file
@ -0,0 +1 @@
|
|||||||
|
-std=c17
|
||||||
6
TeAmanagement/TeAman.config
Normal file
6
TeAmanagement/TeAman.config
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Add predefined macros for your project here. For example:
|
||||||
|
// #define THE_ANSWER 42
|
||||||
|
#define EBUG
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define _XOPEN_SOURCE=1111
|
||||||
|
|
||||||
1
TeAmanagement/TeAman.creator
Normal file
1
TeAmanagement/TeAman.creator
Normal file
@ -0,0 +1 @@
|
|||||||
|
[General]
|
||||||
163
TeAmanagement/TeAman.creator.user
Normal file
163
TeAmanagement/TeAman.creator.user
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE QtCreatorProject>
|
||||||
|
<!-- Written by QtCreator 6.0.0, 2022-02-02T17:26:10. -->
|
||||||
|
<qtcreator>
|
||||||
|
<data>
|
||||||
|
<variable>EnvironmentId</variable>
|
||||||
|
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||||
|
<value type="int">0</value>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||||
|
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</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="int" 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.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="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">2</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>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||||
|
<valuemap type="QVariantMap">
|
||||||
|
<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>
|
||||||
|
<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 type="QVariantMap" key="CppEditor.QuickFix">
|
||||||
|
<value type="bool" key="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="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||||
|
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||||
|
<value type="int" 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/ELECTRONICS/CAN_controller/Socket_CANserver</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="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</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="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</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">Default</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||||
|
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</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="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||||
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||||
|
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||||
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||||
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||||
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||||
|
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||||
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||||
|
</valuemap>
|
||||||
|
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||||
|
</valuemap>
|
||||||
|
</data>
|
||||||
|
<data>
|
||||||
|
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||||
|
<value type="int">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
TeAmanagement/TeAman.cxxflags
Normal file
1
TeAmanagement/TeAman.cxxflags
Normal file
@ -0,0 +1 @@
|
|||||||
|
-std=c++17
|
||||||
7
TeAmanagement/TeAman.files
Normal file
7
TeAmanagement/TeAman.files
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
cmdlnopts.c
|
||||||
|
cmdlnopts.h
|
||||||
|
main.c
|
||||||
|
sersock.c
|
||||||
|
sersock.h
|
||||||
|
teacmd.c
|
||||||
|
teacmd.h
|
||||||
1
TeAmanagement/TeAman.includes
Normal file
1
TeAmanagement/TeAman.includes
Normal file
@ -0,0 +1 @@
|
|||||||
|
.
|
||||||
106
TeAmanagement/cmdlnopts.c
Normal file
106
TeAmanagement/cmdlnopts.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TeAman project.
|
||||||
|
* Copyright 2022 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 <assert.h>
|
||||||
|
#include <math.h> // NAN
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
|
||||||
|
// default PID filename:
|
||||||
|
#define DEFAULT_PIDFILE "/tmp/usbsock.pid"
|
||||||
|
#define DEFAULT_PORT "1234"
|
||||||
|
#define DEFAULT_SOCKPATH "\0TeA"
|
||||||
|
|
||||||
|
static int help;
|
||||||
|
static glob_pars G = {
|
||||||
|
.pidfile = DEFAULT_PIDFILE,
|
||||||
|
.speed = 9600,
|
||||||
|
.path = DEFAULT_SOCKPATH,
|
||||||
|
.minsteps = {-6150, -4100, -3700},
|
||||||
|
.maxsteps = {25000, 16000, 32500},
|
||||||
|
.x = NAN, .y = NAN, .z = NAN
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define command line options by filling structure:
|
||||||
|
* name has_arg flag val type argptr help
|
||||||
|
*/
|
||||||
|
static myoption cmdlnopts[] = {
|
||||||
|
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||||
|
{"devpath", NEED_ARG, NULL, 'd', arg_string, APTR(&G.devpath), _("serial device path")},
|
||||||
|
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("serial device speed (default: 9600)")},
|
||||||
|
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")},
|
||||||
|
{"pidfile", NEED_ARG, NULL, 'p', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")},
|
||||||
|
{"client", NO_ARGS, NULL, 'c', arg_int, APTR(&G.client), _("run as client")},
|
||||||
|
{"sockpath",NEED_ARG, NULL, 'f', arg_string, APTR(&G.path), _("socket path (start from \\0 for no files, default: \0TeA)")},
|
||||||
|
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose), _("increase log verbose level (default: LOG_WARN) and messages (default: none)")},
|
||||||
|
{"fitsheader",NEED_ARG, NULL, 'o', arg_string, APTR(&G.fitshdr), _("FITS header output file")},
|
||||||
|
{"terminal",NO_ARGS, NULL, 't', arg_int, APTR(&G.terminal), _("run as terminal client")},
|
||||||
|
{"xmin", NEED_ARG, NULL, 0, arg_int, APTR(&G.minsteps[0]),_("min position (steps) by X")},
|
||||||
|
{"xmax", NEED_ARG, NULL, 0, arg_int, APTR(&G.maxsteps[0]),_("max position (steps) by X")},
|
||||||
|
{"ymin", NEED_ARG, NULL, 0, arg_int, APTR(&G.minsteps[1]),_("min position (steps) by Y")},
|
||||||
|
{"ymax", NEED_ARG, NULL, 0, arg_int, APTR(&G.maxsteps[1]),_("max position (steps) by Y")},
|
||||||
|
{"zmin", NEED_ARG, NULL, 0, arg_int, APTR(&G.minsteps[2]),_("min position (steps) by Z")},
|
||||||
|
{"zmax", NEED_ARG, NULL, 0, arg_int, APTR(&G.maxsteps[2]),_("max position (steps) by Z")},
|
||||||
|
{"setx", NEED_ARG, NULL, 'x', arg_double, APTR(&G.x), _("move X-stage to this coordinate (mm)")},
|
||||||
|
{"sety", NEED_ARG, NULL, 'y', arg_double, APTR(&G.y), _("move Y-stage to this coordinate (mm)")},
|
||||||
|
{"setz", NEED_ARG, NULL, 'z', arg_double, APTR(&G.z), _("move Z-stage to this coordinate (mm)")},
|
||||||
|
{"wait", NO_ARGS, NULL, 'w', arg_int, APTR(&G.wait), _("wait end of operations")},
|
||||||
|
{"gotozero",NO_ARGS, NULL, '0', arg_int, APTR(&G.gotozero), _("init zero-position of all axis")},
|
||||||
|
end_option
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse command line options and return dynamically allocated structure
|
||||||
|
* to global parameters
|
||||||
|
* @param argc - copy of argc from main
|
||||||
|
* @param argv - copy of argv from main
|
||||||
|
* @return allocated structure with global parameters
|
||||||
|
*/
|
||||||
|
glob_pars *parse_args(int argc, char **argv){
|
||||||
|
int i;
|
||||||
|
size_t hlen = 1024;
|
||||||
|
char helpstring[1024], *hptr = helpstring;
|
||||||
|
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||||
|
// format of help: "Usage: progname [args]\n"
|
||||||
|
change_helpstring(helpstring);
|
||||||
|
// parse arguments
|
||||||
|
parseargs(&argc, &argv, cmdlnopts);
|
||||||
|
for(i = 0; i < argc; i++)
|
||||||
|
printf("Ignore parameter\t%s\n", argv[i]);
|
||||||
|
if(help) showhelp(-1, cmdlnopts);
|
||||||
|
return &G;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief verbose - print additional messages depending of G.verbose (add '\n' at end)
|
||||||
|
* @param levl - message level
|
||||||
|
* @param fmt - message
|
||||||
|
*/
|
||||||
|
void verbose(int levl, const char *fmt, ...){
|
||||||
|
va_list ar;
|
||||||
|
if(levl > G.verbose) return;
|
||||||
|
//printf("%s: ", __progname);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
48
TeAmanagement/cmdlnopts.h
Normal file
48
TeAmanagement/cmdlnopts.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TeAman project.
|
||||||
|
* Copyright 2022 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
|
||||||
|
#ifndef CMDLNOPTS_H__
|
||||||
|
#define CMDLNOPTS_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are some typedef's for global data
|
||||||
|
*/
|
||||||
|
typedef struct{
|
||||||
|
char *devpath; // path to serial device
|
||||||
|
char *pidfile; // name of PID file
|
||||||
|
char *logfile; // logging to this file
|
||||||
|
char *path; // path to socket file
|
||||||
|
char *fitshdr; // path to file with FITS header output
|
||||||
|
int minsteps[3]; // min steps for each axe
|
||||||
|
int maxsteps[3];
|
||||||
|
int terminal; // run as terminal
|
||||||
|
int speed; // connection speed
|
||||||
|
int verbose; // verbose level: for messages & logging
|
||||||
|
int client; // ==1 if application runs in client mode
|
||||||
|
int wait; // wait for stop
|
||||||
|
int gotozero; // run 'gotoz' for all three axis
|
||||||
|
double x; // coordinates to set (when run as client)
|
||||||
|
double y;
|
||||||
|
double z;
|
||||||
|
} glob_pars;
|
||||||
|
|
||||||
|
glob_pars *parse_args(int argc, char **argv);
|
||||||
|
void verbose(int levl, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif // CMDLNOPTS_H__
|
||||||
2506
TeAmanagement/log.log
Normal file
2506
TeAmanagement/log.log
Normal file
File diff suppressed because it is too large
Load Diff
108
TeAmanagement/main.c
Normal file
108
TeAmanagement/main.c
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TeAman project.
|
||||||
|
* Copyright 2022 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 <string.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
#include "sersock.h"
|
||||||
|
#include "teacmd.h"
|
||||||
|
|
||||||
|
static glob_pars *GP = NULL;
|
||||||
|
static TTY_descr *dev = NULL;
|
||||||
|
static pid_t childpid = 0;
|
||||||
|
int server = 1;
|
||||||
|
|
||||||
|
void signals(int sig){
|
||||||
|
if(childpid){ // slave process
|
||||||
|
DBG("Child killed with sig=%d", sig);
|
||||||
|
LOGWARN("Child killed with sig=%d", sig);
|
||||||
|
exit(sig);
|
||||||
|
}
|
||||||
|
// master process
|
||||||
|
DBG("Master process");
|
||||||
|
if(GP && GP->terminal) restore_console();
|
||||||
|
else if(dev) close_tty(&dev);
|
||||||
|
if(sig){
|
||||||
|
DBG("Exit with signal %d", sig);
|
||||||
|
signal(sig, SIG_IGN);
|
||||||
|
LOGERR("Exit with signal %d", sig);
|
||||||
|
}else LOGERR("Exit");
|
||||||
|
if(GP && GP->pidfile && server){
|
||||||
|
DBG("Unlink pid file");
|
||||||
|
unlink(GP->pidfile);
|
||||||
|
}
|
||||||
|
exit(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
char *self = strdup(argv[0]);
|
||||||
|
initial_setup();
|
||||||
|
GP = parse_args(argc, argv);
|
||||||
|
if(!GP->client && GP->terminal) ERRX("Can't work as server and terminal client simultaneously");
|
||||||
|
if(GP->logfile){
|
||||||
|
int lvl = LOGLEVEL_WARN + GP->verbose;
|
||||||
|
DBG("level = %d", lvl);
|
||||||
|
if(lvl > LOGLEVEL_ANY) lvl = LOGLEVEL_ANY;
|
||||||
|
verbose(1, "Log file %s @ level %d\n", GP->logfile, lvl);
|
||||||
|
OPENLOG(GP->logfile, lvl, 1);
|
||||||
|
if(!globlog) WARNX("Can't create log file");
|
||||||
|
}
|
||||||
|
if(GP->client) server = 0;
|
||||||
|
else{
|
||||||
|
if(!GP->devpath){
|
||||||
|
LOGERR("You should point serial device path");
|
||||||
|
ERRX("You should point serial device path");
|
||||||
|
}
|
||||||
|
set_validator(GP);
|
||||||
|
}
|
||||||
|
if(server) check4running(self, GP->pidfile);
|
||||||
|
// signal reactions:
|
||||||
|
signal(SIGTERM, signals); // kill (-15) - quit
|
||||||
|
signal(SIGHUP, SIG_IGN); // hup - ignore
|
||||||
|
signal(SIGINT, signals); // ctrl+C - quit
|
||||||
|
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||||
|
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||||
|
LOGMSG("Started");
|
||||||
|
#ifndef EBUG
|
||||||
|
if(server){
|
||||||
|
unsigned int pause = 5;
|
||||||
|
while(1){
|
||||||
|
childpid = fork();
|
||||||
|
if(childpid){ // master
|
||||||
|
double t0 = dtime();
|
||||||
|
LOGMSG("Created child with pid %d", childpid);
|
||||||
|
wait(NULL);
|
||||||
|
LOGWARN("Child %d died", childpid);
|
||||||
|
if(dtime() - t0 < 1.) pause += 5;
|
||||||
|
else pause = 1;
|
||||||
|
if(pause > 900) pause = 900;
|
||||||
|
sleep(pause); // wait a little before respawn
|
||||||
|
}else{ // slave
|
||||||
|
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return start_socket(server, GP, &dev);
|
||||||
|
}
|
||||||
7
TeAmanagement/out.fits
Normal file
7
TeAmanagement/out.fits
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
INSTRUME= 'TeA - Telescope Analyzer' / Acquisition hardware
|
||||||
|
XMM = 0 / Coordinate, mm
|
||||||
|
YMM = 0 / Coordinate, mm
|
||||||
|
ZMM = 0 / Coordinate, mm
|
||||||
|
XSTAT = 'stop' / Status of given coordinate motor
|
||||||
|
YSTAT = 'stop' / Status of given coordinate motor
|
||||||
|
ZSTAT = 'stop' / Status of given coordinate motor
|
||||||
473
TeAmanagement/sersock.c
Normal file
473
TeAmanagement/sersock.c
Normal file
@ -0,0 +1,473 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TeAman project.
|
||||||
|
* Copyright 2022 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 <netdb.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/un.h> // unix socket
|
||||||
|
|
||||||
|
#include "sersock.h"
|
||||||
|
#include "teacmd.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum{
|
||||||
|
SENDSTAT_OK,
|
||||||
|
SENDSTAT_OUTOFRANGE,
|
||||||
|
SENDSTAT_NOECHO,
|
||||||
|
SENDSTAT_ERR,
|
||||||
|
SENDSTAT_SETTER // not an error
|
||||||
|
|
||||||
|
};
|
||||||
|
static const char *sendstatuses[] = {
|
||||||
|
[SENDSTAT_OK] = "OK\n",
|
||||||
|
[SENDSTAT_OUTOFRANGE] = "Steps out of range\n",
|
||||||
|
[SENDSTAT_NOECHO] = "No echo received\n",
|
||||||
|
[SENDSTAT_ERR] = "Error in write()\n",
|
||||||
|
[SENDSTAT_SETTER] = "OK\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *getserdata(TTY_descr *D, int *len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief sendcmd - validate & send command to device
|
||||||
|
* @param cmd - command (string with or without "\n" on the end)
|
||||||
|
* @param d - serial device
|
||||||
|
* @return SENDSTAT_OK if OK or error
|
||||||
|
*/
|
||||||
|
static int sendcmd(char *cmd, ssize_t len, TTY_descr *d){
|
||||||
|
int v = validate_cmd(cmd, len);
|
||||||
|
int ret = SENDSTAT_OK;
|
||||||
|
if(v < 0) return SENDSTAT_OUTOFRANGE;
|
||||||
|
if(v > 0) ret = SENDSTAT_SETTER; // is setter
|
||||||
|
if(len != write(d->comfd, cmd, len)){
|
||||||
|
WARN("write()");
|
||||||
|
LOGWARN("write()");
|
||||||
|
return SENDSTAT_ERR;
|
||||||
|
}
|
||||||
|
int l;
|
||||||
|
usleep(500);
|
||||||
|
char *ans = getserdata(d, &l);
|
||||||
|
if(ans) DBG("ans: '%s'", ans); else DBG("no ans");
|
||||||
|
if(ans && 0 == strcmp(ans, cmd)) return ret;
|
||||||
|
return SENDSTAT_NOECHO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// work with single client, return -1 if disconnected or status of `sendcmd`
|
||||||
|
static int handle_socket(int sock, TTY_descr *d){
|
||||||
|
char buff[BUFLEN];
|
||||||
|
ssize_t rd = read(sock, buff, BUFLEN-1);;
|
||||||
|
DBG("Got %zd bytes", rd);
|
||||||
|
if(rd <= 0){ // error or disconnect
|
||||||
|
DBG("Nothing to read from fd %d (ret: %zd)", sock, rd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// add trailing zero to be on the safe side
|
||||||
|
buff[rd] = 0;
|
||||||
|
DBG("GOT: %s", buff);
|
||||||
|
ssize_t blen = strlen(buff);
|
||||||
|
int s = sendcmd(buff, blen, d);
|
||||||
|
if(SENDSTAT_OK != s && SENDSTAT_SETTER != s){
|
||||||
|
int l = strlen(sendstatuses[s]);
|
||||||
|
if(l != send(sock, sendstatuses[s], l, MSG_NOSIGNAL)){
|
||||||
|
WARN("send()");
|
||||||
|
LOGWARN("send() to fd=%d failed", sock);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(buff[blen-1] == '\n') buff[blen-1] = 0;
|
||||||
|
LOGMSG("CLIENT_%d: %s", sock, buff);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check data from fd
|
||||||
|
* @param fd - file descriptor
|
||||||
|
* @return 0 in case of timeout, 1 in case of fd have data, -1 if error
|
||||||
|
*/
|
||||||
|
int canberead(int fd){
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 10000; // wait for 10ms max
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(fd, &fds);
|
||||||
|
do{
|
||||||
|
int rc = select(fd+1, &fds, NULL, NULL, &timeout);
|
||||||
|
if(rc < 0){
|
||||||
|
if(errno != EINTR){
|
||||||
|
LOGWARN("select()");
|
||||||
|
WARN("select()");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}while(1);
|
||||||
|
if(FD_ISSET(fd, &fds)){
|
||||||
|
//DBG("FD_ISSET");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief getserdata - read data (ending by '\n') from serial device
|
||||||
|
* @param D (i) - device
|
||||||
|
* @param len (o) - amount of butes read (-1 if disconnected)
|
||||||
|
* @return pointer to data buffer or NULL if none
|
||||||
|
*/
|
||||||
|
static char *getserdata(TTY_descr *D, int *len){
|
||||||
|
static char serbuf[BUFLEN], *ptr = serbuf;
|
||||||
|
static size_t blen = BUFLEN - 1;
|
||||||
|
if(!D || D->comfd < 0) return NULL;
|
||||||
|
char *nl = NULL;
|
||||||
|
do{
|
||||||
|
int s = canberead(D->comfd);
|
||||||
|
if(s == 0) break;
|
||||||
|
if(s < 0){ // interrupted?
|
||||||
|
if(len) *len = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ssize_t l = read(D->comfd, ptr, blen);
|
||||||
|
if(l < 1){ // disconnected
|
||||||
|
DBG("device disconnected");
|
||||||
|
if(len) *len = -1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ptr[l] = 0;
|
||||||
|
//DBG("GOT %zd bytes: '%s'", l, ptr);
|
||||||
|
nl = strchr(ptr, '\n');
|
||||||
|
ptr += l;
|
||||||
|
blen -= l;
|
||||||
|
if(nl){
|
||||||
|
//DBG("Got newline");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}while(blen);
|
||||||
|
// recalculate newline from the beginning (what if old data stays there?)
|
||||||
|
nl = strchr(serbuf, '\n');
|
||||||
|
if(blen && !nl){
|
||||||
|
if(len) *len = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// in case of overflow send buffer without trailing '\n'
|
||||||
|
int L;
|
||||||
|
if(nl) L = nl - serbuf + 1; // get line to '\n'
|
||||||
|
else L = strlen(serbuf); // get all buffer
|
||||||
|
if(len) *len = L;
|
||||||
|
memcpy(D->buf, serbuf, L); // copy all + trailing zero
|
||||||
|
D->buflen = L;
|
||||||
|
D->buf[L] = 0;
|
||||||
|
//DBG("Put to buf %d bytes: '%s'", L, D->buf);
|
||||||
|
if(nl){
|
||||||
|
L = ptr - nl - 1; // symbols after newline
|
||||||
|
if(L > 0){ // there's some data after '\n' -> put it into the beginning
|
||||||
|
memmove(serbuf, nl+1, L);
|
||||||
|
blen = BUFLEN - 1 - L;
|
||||||
|
ptr = serbuf + L;
|
||||||
|
*ptr = 0;
|
||||||
|
//DBG("now serbuf is '%s'", serbuf);
|
||||||
|
}else{
|
||||||
|
blen = BUFLEN - 1;
|
||||||
|
ptr = serbuf;
|
||||||
|
*ptr = 0;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
blen = BUFLEN - 1;
|
||||||
|
ptr = serbuf;
|
||||||
|
*ptr = 0;
|
||||||
|
}
|
||||||
|
return D->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_server(int sock, TTY_descr *d, char *hdrfile){
|
||||||
|
if(listen(sock, MAXCLIENTS) == -1){
|
||||||
|
WARN("listen");
|
||||||
|
LOGWARN("listen");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int enable = 1;
|
||||||
|
if(ioctl(sock, FIONBIO, (void *)&enable) < 0){ // make socket nonblocking
|
||||||
|
LOGERR("Can't make socket nonblocking");
|
||||||
|
ERRX("ioctl()");
|
||||||
|
}
|
||||||
|
int nfd = 1; // only one socket @start
|
||||||
|
struct pollfd poll_set[MAXCLIENTS+1];
|
||||||
|
bzero(poll_set, sizeof(poll_set));
|
||||||
|
// ZERO - listening server socket
|
||||||
|
poll_set[0].fd = sock;
|
||||||
|
poll_set[0].events = POLLIN;
|
||||||
|
int need2poll = 1; // one of motors is moving or in error state - check all
|
||||||
|
double tpoll = dtime(); // time of last device polling
|
||||||
|
while(1){
|
||||||
|
poll(poll_set, nfd, 1); // max timeout - 1ms
|
||||||
|
if(poll_set[0].revents & POLLIN){ // check main for accept()
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
socklen_t len = sizeof(addr);
|
||||||
|
int client = accept(sock, (struct sockaddr*)&addr, &len);
|
||||||
|
DBG("New connection");
|
||||||
|
LOGMSG("Connection, fd=%d", client);
|
||||||
|
if(nfd == MAXCLIENTS + 1){
|
||||||
|
LOGWARN("Max amount of connections, disconnect fd=%d", client);
|
||||||
|
WARNX("Limit of connections reached");
|
||||||
|
close(client);
|
||||||
|
}else{
|
||||||
|
memset(&poll_set[nfd], 0, sizeof(struct pollfd));
|
||||||
|
poll_set[nfd].fd = client;
|
||||||
|
poll_set[nfd].events = POLLIN;
|
||||||
|
++nfd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scan connections
|
||||||
|
for(int fdidx = 1; fdidx < nfd; ++fdidx){
|
||||||
|
if((poll_set[fdidx].revents & POLLIN) == 0) continue;
|
||||||
|
int fd = poll_set[fdidx].fd;
|
||||||
|
int h = handle_socket(fd, d);
|
||||||
|
if(h < 0){ // socket closed
|
||||||
|
DBG("Client fd=%d disconnected", fd);
|
||||||
|
LOGMSG("Client fd=%d disconnected", fd);
|
||||||
|
close(fd);
|
||||||
|
// move last FD to current position
|
||||||
|
poll_set[fdidx] = poll_set[nfd - 1];
|
||||||
|
--nfd;
|
||||||
|
}else if(h == SENDSTAT_SETTER){
|
||||||
|
DBG("NEED TO POLL!");
|
||||||
|
need2poll = 1; // should check state
|
||||||
|
usleep(110000); // wait for buffers put to USB
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int l = -1;
|
||||||
|
char *serdata = NULL;
|
||||||
|
while((serdata = getserdata(d, &l))){
|
||||||
|
if(l < 0){
|
||||||
|
LOGERR("Serial device disconnected");
|
||||||
|
ERRX("Serial device disconnected");
|
||||||
|
}
|
||||||
|
for(int i = 1; i < nfd; ++i){
|
||||||
|
if(l != send(poll_set[i].fd, serdata, l, MSG_NOSIGNAL)){
|
||||||
|
LOGWARN("send() to fd=%d failed", poll_set[i].fd);
|
||||||
|
WARN("send()");
|
||||||
|
}
|
||||||
|
DBG("send 2 client %d", poll_set[i].fd);
|
||||||
|
}
|
||||||
|
// this call should be AFTER data sending as it changes value of `serdata`
|
||||||
|
if(parse_incoming_string(serdata, l, hdrfile)) need2poll = 1; // check later if something is moving
|
||||||
|
}
|
||||||
|
if(need2poll && dtime() - tpoll > 1.){ // check status once per second
|
||||||
|
char buff[BUFLEN];
|
||||||
|
for(int i = 0; i < 3; ++i){ // don't need to validate data here
|
||||||
|
snprintf(buff, BUFLEN, "%s%d\n", TEACMD_POSITION, i);
|
||||||
|
ssize_t blen = strlen(buff);
|
||||||
|
if(blen != write(d->comfd, buff, strlen(buff))) WARN("write()");
|
||||||
|
DBG("send '%s'", buff);
|
||||||
|
serdata = getserdata(d, &l); // clear echo
|
||||||
|
if(serdata){
|
||||||
|
DBG("ans: %s", serdata);
|
||||||
|
//parse_incoming_string(serdata, l, hdrfile);
|
||||||
|
}
|
||||||
|
snprintf(buff, BUFLEN, "%s%d\n", TEACMD_STATUS, i);
|
||||||
|
blen = strlen(buff);
|
||||||
|
if(blen != write(d->comfd, buff, strlen(buff))) WARN("write()");
|
||||||
|
DBG("send %s", buff);
|
||||||
|
serdata = getserdata(d, &l); // clear echo
|
||||||
|
if(serdata){
|
||||||
|
DBG("ans: %s", serdata);
|
||||||
|
//parse_incoming_string(serdata, l, hdrfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now clear incoming buffer & process answers (don't spam client)
|
||||||
|
/*
|
||||||
|
DBG("clear buff, time=0");
|
||||||
|
tpoll = dtime();
|
||||||
|
while(dtime() - tpoll < 1. && !(serdata = getserdata(d, &l)));
|
||||||
|
do{
|
||||||
|
need2poll = parse_incoming_string(serdata, l, hdrfile);
|
||||||
|
usleep(20000);
|
||||||
|
}while((serdata = getserdata(d, &l)));
|
||||||
|
DBG("done, time=%g, need2poll=%d", dtime()-tpoll, need2poll);
|
||||||
|
*/
|
||||||
|
need2poll = 0;
|
||||||
|
tpoll = dtime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read console char - for client
|
||||||
|
static int rc(){
|
||||||
|
int rb;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(STDIN_FILENO, &rfds);
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 100;
|
||||||
|
retval = select(1, &rfds, NULL, NULL, &tv);
|
||||||
|
if(!retval) rb = 0;
|
||||||
|
else {
|
||||||
|
if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar();
|
||||||
|
else rb = 0;
|
||||||
|
}
|
||||||
|
return rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief mygetline - silently and non-blocking getline
|
||||||
|
* @return zero-terminated string with '\n' at end (or without in case of overflow)
|
||||||
|
*/
|
||||||
|
static char *mygetline(){
|
||||||
|
static char buf[BUFLEN+1];
|
||||||
|
static int i = 0;
|
||||||
|
while(i < BUFLEN){
|
||||||
|
char rd = rc();
|
||||||
|
if(!rd) return NULL;
|
||||||
|
if(rd == 0x7f && i){ // backspace
|
||||||
|
buf[--i] = 0;
|
||||||
|
printf("\b \b");
|
||||||
|
}else{
|
||||||
|
buf[i++] = rd;
|
||||||
|
printf("%c", rd);
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
if(rd == '\n') break;
|
||||||
|
}
|
||||||
|
buf[i] = 0;
|
||||||
|
i = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void terminal_client(int sock){
|
||||||
|
setup_con(); // convert console mode into non-canon
|
||||||
|
int Bufsiz = BUFLEN;
|
||||||
|
char *recvBuff = MALLOC(char, Bufsiz);
|
||||||
|
while(1){
|
||||||
|
char *msg = mygetline();
|
||||||
|
if(msg){
|
||||||
|
ssize_t L = strlen(msg);
|
||||||
|
if(send(sock, msg, L, 0) != L){
|
||||||
|
WARN("send");
|
||||||
|
}else{
|
||||||
|
if(msg[L-1] == '\n') msg[L-1] = 0;
|
||||||
|
LOGMSG("TERMINAL: %s", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(1 != canberead(sock)) continue;
|
||||||
|
int n = read(sock, recvBuff, Bufsiz-1);
|
||||||
|
if(n == 0){
|
||||||
|
WARNX("Server disconnected");
|
||||||
|
signals(0);
|
||||||
|
}
|
||||||
|
recvBuff[n] = 0;
|
||||||
|
printf("%s", recvBuff);
|
||||||
|
if(recvBuff[n-1] == '\n') recvBuff[n-1] = 0;
|
||||||
|
LOGMSG("SERIAL: %s", recvBuff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief openserialdev - open connection to serial device
|
||||||
|
* @param path (i) - path to device
|
||||||
|
* @param speed - connection speed
|
||||||
|
* @return pointer to device structure if all OK
|
||||||
|
*/
|
||||||
|
static TTY_descr *openserialdev(char *path, int speed){
|
||||||
|
TTY_descr *d = new_tty(path, speed, BUFLEN);
|
||||||
|
DBG("Device created");
|
||||||
|
if(!d || !(tty_open(d, 1))){
|
||||||
|
WARN("Can't open device %s", path);
|
||||||
|
LOGWARN("Can't open device %s", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
DBG("device opened");
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_socket(int server, glob_pars *P, TTY_descr **dev){
|
||||||
|
if(!P || !dev){
|
||||||
|
WARNX("Need parameters & device");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char *hdrfile = NULL;
|
||||||
|
int realfile = 0;
|
||||||
|
char apath[128], *sockpath = P->path;
|
||||||
|
DBG("path: %s", sockpath);
|
||||||
|
if(*sockpath == 0){
|
||||||
|
DBG("convert name");
|
||||||
|
apath[0] = 0;
|
||||||
|
strncpy(apath+1, sockpath+1, 126);
|
||||||
|
}else if(strncmp("\\0", sockpath, 2) == 0){
|
||||||
|
DBG("convert name");
|
||||||
|
apath[0] = 0;
|
||||||
|
strncpy(apath+1, sockpath+2, 126);
|
||||||
|
}else{
|
||||||
|
strcpy(apath, sockpath);
|
||||||
|
realfile = 1;
|
||||||
|
}
|
||||||
|
if(server){
|
||||||
|
if(!dev) return 1;
|
||||||
|
if(!(*dev = openserialdev(P->devpath, P->speed))){
|
||||||
|
LOGERR("Can't open serial device %s", P->devpath);
|
||||||
|
ERR("Can't open serial device %s", P->devpath);
|
||||||
|
}
|
||||||
|
if(realfile) unlink(apath); // remove old socket
|
||||||
|
if(P->fitshdr){ // open file with header
|
||||||
|
FILE *hdrf = fopen(P->fitshdr, "w");
|
||||||
|
if(!hdrf){
|
||||||
|
WARNX("Can't create file %s", P->fitshdr);
|
||||||
|
LOGERR("Can't create file %s", P->fitshdr);
|
||||||
|
}else{
|
||||||
|
fclose(hdrf);
|
||||||
|
hdrfile = strdup(P->fitshdr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int sock = -1;
|
||||||
|
int reuseaddr = 1;
|
||||||
|
struct sockaddr_un saddr = {0};
|
||||||
|
saddr.sun_family = AF_UNIX;
|
||||||
|
memcpy(saddr.sun_path, apath, 106); // if sun_path[0] == 0 we don't create a file
|
||||||
|
if((sock = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0){ // or SOCK_STREAM?
|
||||||
|
LOGERR("socket()");
|
||||||
|
ERR("socket()");
|
||||||
|
}
|
||||||
|
if(server){
|
||||||
|
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
|
||||||
|
LOGWARN("setsockopt");
|
||||||
|
WARN("setsockopt");
|
||||||
|
}
|
||||||
|
if(bind(sock, &saddr, sizeof(saddr)) == -1){
|
||||||
|
close(sock);
|
||||||
|
LOGERR("bind");
|
||||||
|
ERR("bind");
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(connect(sock, &saddr, sizeof(saddr)) == -1){
|
||||||
|
LOGERR("connect()");
|
||||||
|
ERR("connect()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int ret = 0;
|
||||||
|
if(server) run_server(sock, *dev, hdrfile);
|
||||||
|
else if(P->terminal) terminal_client(sock);
|
||||||
|
else ret = client_proc(sock, P);
|
||||||
|
close(sock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
34
TeAmanagement/sersock.h
Normal file
34
TeAmanagement/sersock.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TeAman project.
|
||||||
|
* Copyright 2022 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
|
||||||
|
#ifndef SERSOCK_H__
|
||||||
|
#define SERSOCK_H__
|
||||||
|
|
||||||
|
#define BUFLEN (1024)
|
||||||
|
// Max amount of connections
|
||||||
|
#define MAXCLIENTS (30)
|
||||||
|
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
|
||||||
|
int start_socket(int server, glob_pars *P, TTY_descr **dev);
|
||||||
|
int canberead(int fd);
|
||||||
|
|
||||||
|
#endif // SERSOCK_H__
|
||||||
435
TeAmanagement/teacmd.c
Normal file
435
TeAmanagement/teacmd.c
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TeAman project.
|
||||||
|
* Copyright 2022 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 <ctype.h>
|
||||||
|
#include <math.h> // NAN
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "sersock.h"
|
||||||
|
#include "teacmd.h"
|
||||||
|
|
||||||
|
#define CMP(cmd, str) (0 == strncmp(cmd, str, sizeof(cmd)-1))
|
||||||
|
|
||||||
|
static int min[3] = {0}, max[3] = {0}; // min/max positions (steps) for each axe
|
||||||
|
static int pos[3] = {0}; // current position
|
||||||
|
|
||||||
|
static char *get_keyval(char *keyval);
|
||||||
|
|
||||||
|
void set_validator(glob_pars *P){
|
||||||
|
memcpy(min, P->minsteps, sizeof(min));
|
||||||
|
memcpy(max, P->maxsteps, sizeof(max));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearbuf(int sock){
|
||||||
|
FNAME();
|
||||||
|
char buf[256];
|
||||||
|
int r = 0;
|
||||||
|
do{
|
||||||
|
r = canberead(sock);
|
||||||
|
DBG("r = %d", r);
|
||||||
|
if(r != 1) break;
|
||||||
|
r = read(sock, buf, 255);
|
||||||
|
}while(r > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief movecmd - send command to move motor
|
||||||
|
* @param sock - socket fd
|
||||||
|
* @param n - motor number
|
||||||
|
* @param val - coordinate value
|
||||||
|
* @return FALSE if failed
|
||||||
|
*/
|
||||||
|
static int movecmd(int sock, int n, double val){
|
||||||
|
if(sock < 0 || n < 0 || n > 2) return FALSE;
|
||||||
|
char buf1[256], buf2[256];
|
||||||
|
if(isnan(val)) snprintf(buf1, 256, "%s%d\n", TEACMD_POSITION, n);
|
||||||
|
else snprintf(buf1, 256, "%s%d=%d\n", TEACMD_POSITION, n, TEAMM_STEPS(val));
|
||||||
|
ssize_t L = strlen(buf1);
|
||||||
|
clearbuf(sock);
|
||||||
|
if(send(sock, buf1, L, 0) != L){
|
||||||
|
buf1[L-1] = 0;
|
||||||
|
WARN("cant send \"%s\"", buf1);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
DBG("send %s", buf1);
|
||||||
|
double t0 = dtime();
|
||||||
|
// wait for a quater of second
|
||||||
|
do{
|
||||||
|
int r = canberead(sock);
|
||||||
|
if(r < 0){
|
||||||
|
WARNX("read error");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if(!r) continue;
|
||||||
|
int N = read(sock, buf2, 255);
|
||||||
|
if(N == 0){
|
||||||
|
WARNX("Server disconnected");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
buf2[N] = 0;
|
||||||
|
buf1[L-1] = 0;
|
||||||
|
if(buf2[N-1] == '\n') buf2[N-1] = 0;
|
||||||
|
if(isnan(val)){
|
||||||
|
char *val = get_keyval(buf2);
|
||||||
|
if(val){
|
||||||
|
if(CMP(TEACMD_POSITION, buf2)){
|
||||||
|
int Nmot = buf2[strlen(buf2)-1] - '0'; // commandN
|
||||||
|
DBG("got Nmot=%d, need %d", Nmot, n);
|
||||||
|
if(Nmot == n){
|
||||||
|
const char *coords = "XYZ";
|
||||||
|
verbose(1, "%c=%g", coords[Nmot], TEASTEPS_MM(atoi(val)));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(strcmp(buf1, buf2)) WARNX("Cmd '%s' got answer '%s'", buf1, buf2);
|
||||||
|
else{
|
||||||
|
verbose(1, "Cmd '%s' OK", buf1);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}while(dtime() - t0 < 0.25);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// poll status; return -1 if can't get answer or status code
|
||||||
|
static int pollcmd(int sock, int n){
|
||||||
|
if(sock < 0 || n < 0 || n > 2) return -1;
|
||||||
|
char buf[256];
|
||||||
|
snprintf(buf, 256, "%s%d\n", TEACMD_STATUS, n);
|
||||||
|
ssize_t L = strlen(buf);
|
||||||
|
clearbuf(sock);
|
||||||
|
if(send(sock, buf, L, 0) != L){
|
||||||
|
buf[L-1] = 0;
|
||||||
|
WARN("Can't send \"%s\"", buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
double t0 = dtime();
|
||||||
|
do{
|
||||||
|
int r = canberead(sock);
|
||||||
|
if(r < 0){
|
||||||
|
WARNX("read error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(!r) continue;
|
||||||
|
int N = read(sock, buf, 255);
|
||||||
|
if(N == 0){
|
||||||
|
WARNX("Server disconnected");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
buf[N] = 0;
|
||||||
|
DBG("got: %s", buf);
|
||||||
|
char *val = get_keyval(buf);
|
||||||
|
DBG("val='%s', buf='%s'", val, buf);
|
||||||
|
if(val){
|
||||||
|
if(CMP(TEACMD_STATUS, buf)){
|
||||||
|
int Nmot = buf[strlen(buf)-1] - '0'; // commandN
|
||||||
|
DBG("Nmot=%d", Nmot);
|
||||||
|
if(Nmot == n) return atoi(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}while(dtime() - t0 < 1.);
|
||||||
|
WARNX("Timeout");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int waitmoving(int sock){
|
||||||
|
int stall = 0, error = 0, ret = 0;
|
||||||
|
do{
|
||||||
|
int stop = 1;
|
||||||
|
for(int i = 0; i < 3; ++i){
|
||||||
|
int st = pollcmd(sock, i);
|
||||||
|
if(st < 0) ERRX("Server disconnected");
|
||||||
|
DBG("pollcmd returns %d", st);
|
||||||
|
switch(st){
|
||||||
|
case STP_RELAX:
|
||||||
|
break;
|
||||||
|
case STP_ACCEL:
|
||||||
|
case STP_MOVE:
|
||||||
|
case STP_MVSLOW:
|
||||||
|
case STP_DECEL:
|
||||||
|
stop = 0;
|
||||||
|
break;
|
||||||
|
case STP_STALL:
|
||||||
|
stall = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(stop) break;
|
||||||
|
usleep(250000);
|
||||||
|
}while(1);
|
||||||
|
if(stall){ WARNX("At least one of motors stalled"); ret = 2;}
|
||||||
|
if(error){ WARNX("At least on of motors in error state"); ret = 3;}
|
||||||
|
verbose(1, "Stop");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gotozcmd(int sock, int n){
|
||||||
|
char buf[256];
|
||||||
|
snprintf(buf, 256, "%s%d\n", TEACMD_GOTOZERO, n);
|
||||||
|
ssize_t L = strlen(buf);
|
||||||
|
clearbuf(sock);
|
||||||
|
if(send(sock, buf, L, 0) != L){
|
||||||
|
buf[L-1] = 0;
|
||||||
|
WARN("cant send \"%s\"", buf);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
DBG("%d -> 0", n);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief client_proc - process client parameters and send data to server
|
||||||
|
* @param sock - socket fd
|
||||||
|
* @param P - parameters
|
||||||
|
* @return error code
|
||||||
|
*/
|
||||||
|
int client_proc(int sock, glob_pars *P){
|
||||||
|
if(sock < 0 || !P) return 1;
|
||||||
|
int wouldmove = 0, ret = 0;
|
||||||
|
if(!isnan(P->x)){if(movecmd(sock, 0, P->x)){ wouldmove = 1;} else ret = 1;}
|
||||||
|
if(!isnan(P->y)){if(movecmd(sock, 1, P->y)){ wouldmove = 1;} else ret = 1;}
|
||||||
|
if(!isnan(P->z)){if(movecmd(sock, 2, P->z)){ wouldmove = 1;} else ret = 1;}
|
||||||
|
DBG("Send gotoz\n\n");
|
||||||
|
if(P->gotozero){
|
||||||
|
if(wouldmove){
|
||||||
|
usleep(250000);
|
||||||
|
ret += waitmoving(sock);
|
||||||
|
}
|
||||||
|
for(int i = 0; i < 3; ++i){
|
||||||
|
int ntries = 0;
|
||||||
|
for(; ntries < 5; ++i){
|
||||||
|
if(gotozcmd(sock, i)){
|
||||||
|
DBG("OK, gotoz%d", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ntries == 5) return 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(P->wait){
|
||||||
|
DBG("wait a little\n\n");
|
||||||
|
sleep(1);
|
||||||
|
ret += waitmoving(sock);
|
||||||
|
}
|
||||||
|
// get current coordinates
|
||||||
|
for(int i = 0; i < 3; ++i) movecmd(sock, i, NAN);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
static void terminal_client(int sock){
|
||||||
|
setup_con(); // convert console mode into non-canon
|
||||||
|
int Bufsiz = BUFLEN;
|
||||||
|
char *recvBuff = MALLOC(char, Bufsiz);
|
||||||
|
while(1){
|
||||||
|
char *msg = mygetline();
|
||||||
|
if(msg){
|
||||||
|
ssize_t L = strlen(msg);
|
||||||
|
if(send(sock, msg, L, 0) != L){
|
||||||
|
WARN("send");
|
||||||
|
}else{
|
||||||
|
if(msg[L-1] == '\n') msg[L-1] = 0;
|
||||||
|
LOGMSG("TERMINAL: %s", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(1 != canberead(sock)) continue;
|
||||||
|
int n = read(sock, recvBuff, Bufsiz-1);
|
||||||
|
if(n == 0){
|
||||||
|
WARNX("Server disconnected");
|
||||||
|
signals(0);
|
||||||
|
}
|
||||||
|
recvBuff[n] = 0;
|
||||||
|
printf("%s", recvBuff);
|
||||||
|
if(recvBuff[n-1] == '\n') recvBuff[n-1] = 0;
|
||||||
|
LOGMSG("SERIAL: %s", recvBuff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief printhdr - write FITS record into output file
|
||||||
|
* @param fd - fd to write
|
||||||
|
* @param key - key
|
||||||
|
* @param val - value
|
||||||
|
* @param cmnt - comment
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
static int printhdr(int fd, const char *key, const char *val, const char *cmnt){
|
||||||
|
char tmp[81];
|
||||||
|
char tk[9];
|
||||||
|
if(strlen(key) > 8){
|
||||||
|
strncpy(tk, key, 8);
|
||||||
|
key = tk;
|
||||||
|
}
|
||||||
|
if(cmnt){
|
||||||
|
snprintf(tmp, 81, "%-8s= %-21s / %s", key, val, cmnt);
|
||||||
|
}else{
|
||||||
|
snprintf(tmp, 81, "%-8s= %s", key, val);
|
||||||
|
}
|
||||||
|
size_t l = strlen(tmp);
|
||||||
|
tmp[l] = '\n';
|
||||||
|
++l;
|
||||||
|
if(write(fd, tmp, l) != (ssize_t)l){
|
||||||
|
WARN("write()");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get_keyval - get value of `key = val`
|
||||||
|
* @param keyval (io) - pair `key = val`, return `key`
|
||||||
|
* @return `val`
|
||||||
|
*/
|
||||||
|
static char *get_keyval(char *keyval){
|
||||||
|
// remove starting spaces in key
|
||||||
|
while(isspace(*keyval)) ++keyval;
|
||||||
|
char *val = strchr(keyval, '=');
|
||||||
|
if(val){ // got value: remove starting spaces in val
|
||||||
|
*val++ = 0;
|
||||||
|
while(isspace(*val)) ++val;
|
||||||
|
}
|
||||||
|
// remove trailing spaces in key
|
||||||
|
char *e = keyval + strlen(keyval) - 1; // last key symbol
|
||||||
|
while(isspace(*e) && e > keyval) --e;
|
||||||
|
e[1] = 0;
|
||||||
|
// now we have key (`str`) and val (or NULL)
|
||||||
|
//DBG("key=%s, val=%s", keyval, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief parse_incoming_string - parse server string (answer from device)
|
||||||
|
* @param str - string with data
|
||||||
|
* @param l - strlen(str)
|
||||||
|
* @param hdrname - filename with FITS header
|
||||||
|
* @return 1 if one of motors not in STP_RELAX
|
||||||
|
*/
|
||||||
|
int parse_incoming_string(char *str, int l, char *hdrname){
|
||||||
|
if(!str || l < 1 || !hdrname) goto chkstate;
|
||||||
|
DBG("Got string %s", str);
|
||||||
|
static int status[3] = {0}; // statuses
|
||||||
|
if(str[l-1] == '\n') str[l-1] = 0;
|
||||||
|
LOGMSG("SERIAL: %s", str);
|
||||||
|
char *value = get_keyval(str);
|
||||||
|
if(!value) goto chkstate;
|
||||||
|
#define POS(cmd) (sizeof(cmd)-1)
|
||||||
|
int Nmot = str[strlen(str)-1] - '0'; // commandN
|
||||||
|
DBG("Nmot=%d", Nmot);
|
||||||
|
if(Nmot < 0 || Nmot > 3) goto chkstate;
|
||||||
|
int valI = atoi(value); // integer value of parameter
|
||||||
|
DBG("val=%d", valI);
|
||||||
|
if(CMP(TEACMD_POSITION, str)){ // got position
|
||||||
|
DBG("Got position[%d]=%d", Nmot, valI);
|
||||||
|
pos[Nmot] = valI;
|
||||||
|
}else if(CMP(TEACMD_STATUS, str)){ // got state
|
||||||
|
DBG("Got status[%d]=%d", Nmot, valI);
|
||||||
|
status[Nmot] = valI;
|
||||||
|
}else goto chkstate; // nothing interesting
|
||||||
|
// now all OK: we got something new - refresh FITS header
|
||||||
|
char val[23];//, comment[71];
|
||||||
|
//double dtmp;
|
||||||
|
#define WRHDR(k, v, c) do{if(printhdr(fd, k, v, c)){goto returning;}}while(0)
|
||||||
|
#define COMMENT(...) do{snprintf(comment, 70, __VA_ARGS__);}while(0)
|
||||||
|
#define VAL(fmt, x) do{snprintf(val, 22, fmt, x);}while(0)
|
||||||
|
#define VALD(x) VAL("%.10g", x)
|
||||||
|
#define VALS(x) VAL("'%s'", x)
|
||||||
|
l = strlen(hdrname) + 7;
|
||||||
|
char *aname = MALLOC(char, l);
|
||||||
|
snprintf(aname, l, "%sXXXXXX", hdrname);
|
||||||
|
int fd = mkstemp(aname);
|
||||||
|
if(fd < 0){
|
||||||
|
WARN("Can't write header file, mkstemp()");
|
||||||
|
LOGWARN("Can't write header file, mkstemp()");
|
||||||
|
FREE(aname);
|
||||||
|
goto chkstate;
|
||||||
|
}
|
||||||
|
fchmod(fd, 0644);
|
||||||
|
WRHDR("INSTRUME", "'TeA - Telescope Analyzer'", "Acquisition hardware");
|
||||||
|
const char *coords[3] = {"XMM", "YMM", "ZMM"};
|
||||||
|
for(int i = 0; i < 3; ++i){
|
||||||
|
VALD(TEASTEPS_MM(pos[i]));
|
||||||
|
WRHDR(coords[i], val, "Coordinate, mm");
|
||||||
|
}
|
||||||
|
const char *states[STP_AMOUNT] = {
|
||||||
|
[STP_RELAX] = "stop",
|
||||||
|
[STP_ACCEL] = "acceleration",
|
||||||
|
[STP_MOVE] = "fast moving",
|
||||||
|
[STP_MVSLOW] = "slow moving",
|
||||||
|
[STP_DECEL] = "deceleration",
|
||||||
|
[STP_STALL] = "stall",
|
||||||
|
[STP_ERR] = "error"
|
||||||
|
};
|
||||||
|
const char *coordsst[3] = {"XSTAT", "YSTAT", "ZSTAT"};
|
||||||
|
for(int i = 0; i < 3; ++i){
|
||||||
|
VALS(states[status[i]]);
|
||||||
|
WRHDR(coordsst[i], val, "Status of given coordinate motor");
|
||||||
|
}
|
||||||
|
returning:
|
||||||
|
close(fd);
|
||||||
|
rename(aname, hdrname);
|
||||||
|
DBG("file %s ready", hdrname);
|
||||||
|
FREE(aname);
|
||||||
|
chkstate:
|
||||||
|
for(int i = 0; i < 3; ++i) if(status[i] != STP_RELAX) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief validate_cmd - test if command won't move motor out of range
|
||||||
|
* @param str - cmd
|
||||||
|
* @return -1 if out of range, 0 if all OK, 1 if str is position setter
|
||||||
|
*/
|
||||||
|
int validate_cmd(char *str, int l){
|
||||||
|
char buff[256];
|
||||||
|
if(l > 255) return -1; // too long - can't check
|
||||||
|
strcpy(buff, str);
|
||||||
|
int ret = 0;
|
||||||
|
char *value = get_keyval(buff);
|
||||||
|
if(!value) return 0; // getter?
|
||||||
|
int Nmot = buff[strlen(buff)-1] - '0'; // commandN
|
||||||
|
if(Nmot < 0 || Nmot > 2) return 0; // something like relay?
|
||||||
|
int valI = atoi(value); // integer value of parameter
|
||||||
|
if(CMP(TEACMD_POSITION, buff)){ // user asks for absolute position
|
||||||
|
if(valI < min[Nmot] || valI > max[Nmot]){
|
||||||
|
WARNX("%d out of range [%d, %d]", valI, min[Nmot], max[Nmot]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ret = 1;
|
||||||
|
}else if(CMP(TEACMD_RELPOSITION, buff) || CMP(TEACMD_SLOWMOVE, buff)){
|
||||||
|
int tagpos = valI + pos[Nmot];
|
||||||
|
if(tagpos < min[Nmot] || tagpos > max[Nmot]){
|
||||||
|
WARNX("%d out of range [%d, %d]", tagpos, min[Nmot], max[Nmot]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ret = 1;
|
||||||
|
}else if(CMP(TEACMD_SETPOS, buff)){
|
||||||
|
WARNX("Forbidden to set position");
|
||||||
|
return -1; // forbidden command
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
50
TeAmanagement/teacmd.h
Normal file
50
TeAmanagement/teacmd.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TeAman project.
|
||||||
|
* Copyright 2022 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 "cmdlnopts.h"
|
||||||
|
|
||||||
|
int client_proc(int sock, glob_pars *P);
|
||||||
|
void set_validator(glob_pars *P);
|
||||||
|
int parse_incoming_string(char *str, int l, char *hdrname);
|
||||||
|
int validate_cmd(char *str, int l);
|
||||||
|
|
||||||
|
|
||||||
|
// steps into MM and vice versa
|
||||||
|
#define TEASTEPS_MM(steps) (steps/200.)
|
||||||
|
#define TEAMM_STEPS(mm) ((int)(mm * 200.))
|
||||||
|
|
||||||
|
// tea commands
|
||||||
|
#define TEACMD_POSITION "abspos"
|
||||||
|
#define TEACMD_RELPOSITION "relpos"
|
||||||
|
#define TEACMD_SLOWMOVE "relslow"
|
||||||
|
#define TEACMD_SETPOS "setpos"
|
||||||
|
#define TEACMD_GOTOZERO "gotoz"
|
||||||
|
#define TEACMD_STATUS "state"
|
||||||
|
|
||||||
|
typedef enum{
|
||||||
|
STP_RELAX, // 0 - no moving
|
||||||
|
STP_ACCEL, // 1 - start moving with acceleration
|
||||||
|
STP_MOVE, // 2 - moving with constant speed
|
||||||
|
STP_MVSLOW, // 3 - moving with slowest constant speed (end of moving)
|
||||||
|
STP_DECEL, // 4 - moving with deceleration
|
||||||
|
STP_STALL, // 5 - stalled
|
||||||
|
STP_ERR, // 6 - wrong/error state
|
||||||
|
STP_AMOUNT
|
||||||
|
} teastate;
|
||||||
Loading…
x
Reference in New Issue
Block a user