mirror of
https://github.com/eddyem/zeiss_utils.git
synced 2025-12-06 02:35:15 +03:00
initial commit
This commit is contained in:
parent
82e167bb74
commit
cbec6c1f63
55
.gitignore
vendored
55
.gitignore
vendored
@ -1,52 +1,3 @@
|
|||||||
# Prerequisites
|
mk/*
|
||||||
*.d
|
Zphocus.*
|
||||||
|
can_phocus
|
||||||
# Object files
|
|
||||||
*.o
|
|
||||||
*.ko
|
|
||||||
*.obj
|
|
||||||
*.elf
|
|
||||||
|
|
||||||
# Linker output
|
|
||||||
*.ilk
|
|
||||||
*.map
|
|
||||||
*.exp
|
|
||||||
|
|
||||||
# Precompiled Headers
|
|
||||||
*.gch
|
|
||||||
*.pch
|
|
||||||
|
|
||||||
# Libraries
|
|
||||||
*.lib
|
|
||||||
*.a
|
|
||||||
*.la
|
|
||||||
*.lo
|
|
||||||
|
|
||||||
# Shared objects (inc. Windows DLLs)
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.so.*
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Executables
|
|
||||||
*.exe
|
|
||||||
*.out
|
|
||||||
*.app
|
|
||||||
*.i*86
|
|
||||||
*.x86_64
|
|
||||||
*.hex
|
|
||||||
|
|
||||||
# Debug files
|
|
||||||
*.dSYM/
|
|
||||||
*.su
|
|
||||||
*.idb
|
|
||||||
*.pdb
|
|
||||||
|
|
||||||
# Kernel Module Compile Results
|
|
||||||
*.mod*
|
|
||||||
*.cmd
|
|
||||||
.tmp_versions/
|
|
||||||
modules.order
|
|
||||||
Module.symvers
|
|
||||||
Mkfile.old
|
|
||||||
dkms.conf
|
|
||||||
|
|||||||
68
Z1000_focus/DS406_canopen.h
Normal file
68
Z1000_focus/DS406_canopen.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
Commands & other data for absolute rotary encoder DS406
|
||||||
|
**/
|
||||||
|
#pragma once
|
||||||
|
#ifndef DS406_CANOPEN_H__
|
||||||
|
#define DS406_CANOPEN_H__
|
||||||
|
|
||||||
|
#define DS406_DEVTYPE 0x1000
|
||||||
|
#define DS406_ERRORREG 0x1001
|
||||||
|
#define DS406_MANUFSTATUS 0x1002
|
||||||
|
#define DS406_COBID_SYNC 0x1005
|
||||||
|
#define DS406_MANDEVNAME 0x1008
|
||||||
|
#define DS406_MANHW_VERS 0x1009
|
||||||
|
#define DS406_MANSW_VERS 0x100A
|
||||||
|
#define DS406_STORE_PARAMS 0x1010
|
||||||
|
#define DS406_RESTORE_DEF 0x1011
|
||||||
|
#define DS406_COBID_EMERG 0x1014
|
||||||
|
#define DS406_INHIB_TM_EMERG 0x1015
|
||||||
|
#define DS406_PROD_HEARTB_TM 0x1017
|
||||||
|
#define DS406_IDENT_OBJ 0x1018
|
||||||
|
#define DS406_ERROR_BEHAVIOUR 0x1029
|
||||||
|
|
||||||
|
#define DS406_PDO1 0x1800
|
||||||
|
#define DS406_PDO2 0x1801
|
||||||
|
#define DS406_PDO1_MAPPED 0x1A00
|
||||||
|
#define DS406_PDO2_MAPPED 0x1A01
|
||||||
|
#define DS406_PDO3_MAPPED 0x1A02
|
||||||
|
#define DS406_PDO4_MAPPED 0x1A03
|
||||||
|
|
||||||
|
#define DS406_BAUDRATE 0x2100
|
||||||
|
#define DS406_NODE_NUMBER 0x2101
|
||||||
|
#define DS406_TERMINATOR 0x2102
|
||||||
|
#define DS406_NMT_AUTOSTART 0x2104
|
||||||
|
#define DS406_PDO_TRIG_THRES 0x2105
|
||||||
|
#define DS406_FILTER_CONFIG 0x2106
|
||||||
|
#define DS406_CUSTOMER_MEMRY 0x2110
|
||||||
|
#define DS406_SENSOR_AMPLITUDE 0x2200
|
||||||
|
#define DS406_TARG_FREQ_DEVIAT 0x2201
|
||||||
|
|
||||||
|
#define DS406_OPER_PARAMS 0x6000
|
||||||
|
#define DS406_MEAS_UNITS_PERREV 0x6001
|
||||||
|
#define DS406_TOT_MEAS_RANGE 0x6002
|
||||||
|
#define DS406_PRESET_VAL 0x6003
|
||||||
|
#define DS406_POSITION_VAL 0x6004
|
||||||
|
#define DS406_PDO_IS_POSITION 0x60040020
|
||||||
|
#define DS406_POS_RAW_VAL 0x600C
|
||||||
|
#define DS406_SPEED_VAL 0x6030
|
||||||
|
#define DS406_PDO_IS_SPEED 0x60300110
|
||||||
|
#define DS406_CYCLE_TIMER 0x6200
|
||||||
|
#define DS406_WORK_AREA_STATE 0x6400
|
||||||
|
#define DS406_WORK_AREA_LIM_LO 0x6401
|
||||||
|
#define DS406_WORK_AREA_LIM_HI 0x6401
|
||||||
|
#define DS406_OPER_STATUS 0x6500
|
||||||
|
#define DS406_TURN_RESOLUT 0x6501
|
||||||
|
#define DS406_REVOL_NUMBER 0x6502
|
||||||
|
#define DS406_ALARMS 0x6503
|
||||||
|
#define DS406_SUPP_ALARMS 0x6504
|
||||||
|
#define DS406_WARNINGS 0x6505
|
||||||
|
#define DS406_SUPP_WARNINGS 0x6506
|
||||||
|
#define DS406_PROF_SW_VERS 0x6507
|
||||||
|
#define DS406_OFFSET_VAL 0x6509
|
||||||
|
#define DS406_MODULE_ID 0x650A
|
||||||
|
#define DS406_SERIAL_NUMBER 0x650B
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // DS406_CANOPEN_H__
|
||||||
70
Z1000_focus/HW_dependent.h
Normal file
70
Z1000_focus/HW_dependent.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Zphocus project.
|
||||||
|
* Copyright 2019 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 HW_DEPENDENT__
|
||||||
|
#define HW_DEPENDENT__
|
||||||
|
|
||||||
|
#include "motor_cancodes.h"
|
||||||
|
|
||||||
|
// amount of encoder's counts when stop = rawspeed^2/STOPPING_COEFF
|
||||||
|
#define STOPPING_COEFF (117500L)
|
||||||
|
|
||||||
|
// direction of motor rotation positive to encoder (1 - negative)
|
||||||
|
#define MOTOR_REVERSE (0)
|
||||||
|
|
||||||
|
// End switches (lowest bit - DI00)
|
||||||
|
// clockwise: DI4
|
||||||
|
#define ESW_CW (1<<4)
|
||||||
|
// counterclockwise: DI5
|
||||||
|
#define ESW_CCW (1<<5)
|
||||||
|
// parameter indexes of esw:
|
||||||
|
#define PAR_CW_IDX PAR_DI04_IDX
|
||||||
|
#define PAR_CCW_IDX PAR_DI05_IDX
|
||||||
|
|
||||||
|
// constants
|
||||||
|
// translate raw values to revolutions per minute
|
||||||
|
#define REVMIN(x) (x/5)
|
||||||
|
// rev/min to raw speed value
|
||||||
|
#define RAWSPEED(x) (x*5)
|
||||||
|
// max/min speed (rev/min)
|
||||||
|
#define MAXSPEED 1200
|
||||||
|
#define MINSPEED 130
|
||||||
|
// moving timeout (*0.01s)
|
||||||
|
#define MOVING_TIMEOUT 5000
|
||||||
|
|
||||||
|
// raw position precision
|
||||||
|
#define RAWPOS_TOLERANCE 20
|
||||||
|
// raw dF value for accurate focussing
|
||||||
|
#define dF0 100
|
||||||
|
// minimal & maximal focus positions (should be >min+dF0 & <max-dF0)
|
||||||
|
#define FOCMIN 200
|
||||||
|
#define FOCMAX 320000
|
||||||
|
// the same in mm
|
||||||
|
#define FOCMIN_MM 0.1
|
||||||
|
#define FOCMAX_MM 76.0
|
||||||
|
// permitted distance to end-switch
|
||||||
|
#define ESW_DIST_ALLOW 1.0
|
||||||
|
|
||||||
|
// constants for focus conversion: foc_mm = (foc_raw - FOCRAW_MM0) / FOCSCALE_MM
|
||||||
|
#define FOCSCALE_MM 4096.
|
||||||
|
#define FOCRAW_MM0 0
|
||||||
|
#define FOC_RAW2MM(x) ((x-FOCRAW_MM0)/FOCSCALE_MM)
|
||||||
|
#define FOC_MM2RAW(x) (FOCRAW_MM0+x*FOCSCALE_MM)
|
||||||
|
|
||||||
|
#endif // HW_DEPENDENT__
|
||||||
42
Z1000_focus/Makefile
Normal file
42
Z1000_focus/Makefile
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# run `make DEF="-D... -D..."` to add extra defines
|
||||||
|
PROGRAM := can_phocus
|
||||||
|
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all -pthread
|
||||||
|
SRCS := $(wildcard *.c)
|
||||||
|
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
|
||||||
|
OBJDIR := mk
|
||||||
|
CFLAGS += -O2 -Wall -Werror -Wextra -Wno-trampolines -std=gnu99
|
||||||
|
OBJS := $(addprefix $(OBJDIR)/, $(SRCS:%.c=%.o))
|
||||||
|
DEPS := $(OBJS:.o=.d)
|
||||||
|
CC ?= gcc
|
||||||
|
$(info GCC is: ${CC})
|
||||||
|
#CXX = g++
|
||||||
|
|
||||||
|
all : $(OBJDIR) $(PROGRAM)
|
||||||
|
|
||||||
|
$(PROGRAM) : $(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 -f $(OBJS) $(DEPS)
|
||||||
|
@rmdir $(OBJDIR) 2>/dev/null || true
|
||||||
|
|
||||||
|
xclean: clean
|
||||||
|
@rm -f $(PROGRAM)
|
||||||
|
|
||||||
|
gentags:
|
||||||
|
CFLAGS="$(CFLAGS) $(DEFINES)" geany -g $(PROGRAM).c.tags *[hc] 2>/dev/null
|
||||||
|
|
||||||
|
.PHONY: gentags clean xclean
|
||||||
86
Z1000_focus/Readme_SEW
Normal file
86
Z1000_focus/Readme_SEW
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
======= Идентификатор устройства: =======
|
||||||
|
10 - reserved
|
||||||
|
9 - 0 - данные процесса, 1 - данные параметров
|
||||||
|
8 - \
|
||||||
|
7 - |
|
||||||
|
6 - | address
|
||||||
|
5 - |
|
||||||
|
4 - |
|
||||||
|
3 - /
|
||||||
|
2 - \
|
||||||
|
1 - | function
|
||||||
|
0 - /
|
||||||
|
|
||||||
|
арес задается в настройках привода
|
||||||
|
|
||||||
|
функция:
|
||||||
|
3 - выходные данные процесса (PO)
|
||||||
|
4 - входные данные процесса (PI)
|
||||||
|
5 - синхронизация
|
||||||
|
6 - (для группового адреса) - групповое PO
|
||||||
|
|
||||||
|
если данные параметров (9-й бит == 1), то функция:
|
||||||
|
3 - запрос данных параметров
|
||||||
|
4 - ответ
|
||||||
|
|
||||||
|
|
||||||
|
======= Контрольные слова =======
|
||||||
|
|
||||||
|
Первые 8 бит CW1&CW2 (со звездочкой - то, что используется):
|
||||||
|
7 - резерв
|
||||||
|
6 - *сброс ошибки
|
||||||
|
5 - набор параметров
|
||||||
|
4 - генератор темпа
|
||||||
|
3 - резерв
|
||||||
|
2 - *разрешение/стоп
|
||||||
|
1 - *разрешение/быстрый стоп
|
||||||
|
0 - *блокировка/разрешение
|
||||||
|
|
||||||
|
Старшие 8 бит у CW1 не использую, у CW2 старшие 8 бит - виртуальные клеммы (цифровые входы)
|
||||||
|
|
||||||
|
|
||||||
|
======= Слова состояния =======
|
||||||
|
|
||||||
|
Младшие 8 бит обоих:
|
||||||
|
7 - резерв
|
||||||
|
6 - резерв
|
||||||
|
5 - *неисправность/предупреждение
|
||||||
|
4 - набор параметров 2/1
|
||||||
|
3 - генератор темпа 2/1
|
||||||
|
2 - *PO разблокированы/заблокированы
|
||||||
|
1 - *готов/не готов
|
||||||
|
0 - *выход разблокирован/заблокирован
|
||||||
|
|
||||||
|
Биты 1+7:
|
||||||
|
0+0 - не готов
|
||||||
|
0+1 - ошибка
|
||||||
|
1+0 - готов
|
||||||
|
1+1 - предупреждение
|
||||||
|
|
||||||
|
Старшие 8 бит CW1 зависит от бита 5 младшего слова:
|
||||||
|
1 - код ошибки
|
||||||
|
0 - состояние
|
||||||
|
|
||||||
|
Коды состояний:
|
||||||
|
0 - не готов
|
||||||
|
1 - блокировка
|
||||||
|
2 - нет разрешения
|
||||||
|
3 - ток удержания
|
||||||
|
4 - разрешение
|
||||||
|
5 - регулирование
|
||||||
|
8 - заводские настройки
|
||||||
|
13 - захват
|
||||||
|
16 - ожидание данных
|
||||||
|
17 - безопасный останов
|
||||||
|
|
||||||
|
|
||||||
|
======= Чтение/запись парметров =======
|
||||||
|
Индекс и субиндекс параметров можно посмотреть в сплывающей подсказке конфигуратора.
|
||||||
|
Идентификатор - с 1 в девятом бите.
|
||||||
|
Посылка и ответ состоят из восьми байт. Первые четыре байта:
|
||||||
|
0 - команда (0x31 для считывания параметра и 0x32 для записи)
|
||||||
|
1 - субиндекс
|
||||||
|
2 - индекс, старший байт
|
||||||
|
3 - индекс, младший байт
|
||||||
|
|
||||||
|
Четыре младших байта - данные.
|
||||||
716
Z1000_focus/can_encoder.c
Normal file
716
Z1000_focus/can_encoder.c
Normal file
@ -0,0 +1,716 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Zphocus project.
|
||||||
|
* Copyright 2019 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 <string.h> // memcpy
|
||||||
|
#include <math.h> // fabs
|
||||||
|
#include "canopen.h"
|
||||||
|
#include "can_encoder.h"
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
#include "DS406_canopen.h"
|
||||||
|
#include "motor_cancodes.h"
|
||||||
|
#include "HW_dependent.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
// printf when -v
|
||||||
|
extern int verbose(const char *fmt, ...);
|
||||||
|
|
||||||
|
// CAN bus IDs: for motor's functions (PI ID [F=4] == PO ID[F=3] + 1) and parameters
|
||||||
|
static unsigned long motor_id = 0, motor_p_id = 0;//, bcast_id = 1;
|
||||||
|
// current motor position
|
||||||
|
static unsigned long curposition = 0;
|
||||||
|
// encoder's node number
|
||||||
|
static int encnodenum = 0;
|
||||||
|
|
||||||
|
static canstatus can_write_par(uint8_t subidx, uint16_t idx, uint32_t *parval);
|
||||||
|
static canstatus can_read_par(uint8_t subidx, uint16_t idx, uint32_t *parval);
|
||||||
|
static int move(unsigned long targposition, int16_t rawspeed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief init_encoder - encoder's interface initialisation
|
||||||
|
* @param encnode - encoder's node number
|
||||||
|
* @param reset - reset node before operating
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int init_encoder(int encnode, int reset){
|
||||||
|
unsigned long lval;
|
||||||
|
encnodenum = encnode;
|
||||||
|
verbose("cur node: %d\n", encnodenum);
|
||||||
|
if(!initNode(encnodenum)){
|
||||||
|
ERRX("Can't find device with Node address %d!", encnodenum);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(reset){
|
||||||
|
verbose("Reset node %d... ", encnodenum);
|
||||||
|
if(resetNode(encnodenum))
|
||||||
|
verbose("Ok!\n");
|
||||||
|
else
|
||||||
|
verbose("Failed.\n");
|
||||||
|
}
|
||||||
|
if(getLong(encnodenum, DS406_DEVTYPE, 0, &lval)){
|
||||||
|
int prof = lval&0xffff;
|
||||||
|
int type = lval>>16;
|
||||||
|
verbose("Found absolute %s-turn rotary encoder DS%d\n", (type==2)?"multi":"single", prof);
|
||||||
|
if(prof != 406 || type != 2){
|
||||||
|
WARNX("The device on node %d isn't a multi-turn encoder!", encnodenum);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
WARNX("Can't get encoder device type");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
//setByte(encnodenum, 0x3010, 1, 1); // Posital's encoders specific! (Could not be saved!)
|
||||||
|
if(G->verbose){
|
||||||
|
if(getLong(encnodenum, DS406_TURN_RESOLUT, 0, &lval)) printf("TURN_RESOLUT: %08lx (%ld)\n",lval, lval);
|
||||||
|
if(getShort(encnodenum, DS406_REVOL_NUMBER, 0, &sval)) printf("REVOL_NUMBER: %04x (%d)\n",sval, sval);
|
||||||
|
if(getLong(encnodenum, DS406_MODULE_ID, 1, &lval)) printf("OFFSET VALUE: %08lx (%ld)\n",lval, lval);
|
||||||
|
if(getLong(encnodenum, DS406_MODULE_ID, 2, &lval)) printf("MIN POS: %08lx (%ld)\n",lval, lval);
|
||||||
|
if(getLong(encnodenum, DS406_MODULE_ID, 3, &lval)) printf("MAX POS: %08lx (%ld)\n",lval, lval);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
verbose("Set operational... ");
|
||||||
|
startNode(encnodenum);
|
||||||
|
int n, i = recvNextPDO(0.1, &n, &lval);
|
||||||
|
int state = getNodeState(encnodenum);
|
||||||
|
verbose("State=%02x\n", state);
|
||||||
|
if(state == NodeOperational) verbose("Ok!\n");
|
||||||
|
else{
|
||||||
|
WARNX("Failed to start node!");
|
||||||
|
returnPreOper();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(i > 0) verbose("Node%d PDO%d %08lx\n",n,i,lval);
|
||||||
|
do{
|
||||||
|
int node[4], pdo_n[4];
|
||||||
|
unsigned long pdo_v[4];
|
||||||
|
verbose("Send SYNC...\n");
|
||||||
|
clean_recv();
|
||||||
|
sendSync();
|
||||||
|
can_dsleep(0.01);
|
||||||
|
if((n = recvPDOs(0.5, 4, node, pdo_n, pdo_v))==0)
|
||||||
|
WARNX("No PDO received on SYNC");
|
||||||
|
else for(i=0;i<n;i++)
|
||||||
|
verbose("Node%d PDO%d %08lx (%ld)\n",node[i],pdo_n[i],pdo_v[i],pdo_v[n]);
|
||||||
|
}while(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief go_out_from_ESW - check ESW roles, check ESW states and try to go out if stay on ESW
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int go_out_from_ESW(){
|
||||||
|
uint32_t cw, ccw, parval;
|
||||||
|
// check esw roles
|
||||||
|
if(CAN_NOERR != can_read_par(PAR_DI_SUBIDX, PAR_CW_IDX, &cw)) goto bad;
|
||||||
|
if(CAN_NOERR != can_read_par(PAR_DI_SUBIDX, PAR_CCW_IDX, &ccw)) goto bad;
|
||||||
|
parval = DI_ENSTOP;
|
||||||
|
if(cw != DI_ENSTOP || ccw != DI_ENSTOP){ // activate enable/stop
|
||||||
|
if(CAN_NOERR != can_write_par(PAR_DI_SUBIDX, PAR_CW_IDX, &parval)) goto bad;
|
||||||
|
if(CAN_NOERR != can_write_par(PAR_DI_SUBIDX, PAR_CCW_IDX, &parval)) goto bad;
|
||||||
|
}
|
||||||
|
// check esw
|
||||||
|
eswstate e;
|
||||||
|
if(CAN_NOERR != get_endswitches(&e)){
|
||||||
|
WARNX("Can't read end-switches state");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(e == ESW_BOTH_ACTIVE){ // error situation!
|
||||||
|
WARNX("Error: both end-switches are active!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(e == ESW_INACTIVE){
|
||||||
|
DBG("Esw inactive");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// try to move from esw
|
||||||
|
parval = DI_NOFUNC;
|
||||||
|
uint16_t idx = (e == ESW_CW_ACTIVE) ? PAR_CW_IDX : PAR_CCW_IDX;
|
||||||
|
if(CAN_NOERR != can_write_par(PAR_DI_SUBIDX, idx, &parval)) goto bad; // turn off motor stopping @esw
|
||||||
|
int16_t speed = (e == ESW_CW_ACTIVE) ? -RAWSPEED(MINSPEED) : RAWSPEED(MINSPEED);
|
||||||
|
for(int i = 0; i < 5; ++i){ // 5 tries to move out
|
||||||
|
getPos(NULL);
|
||||||
|
DBG("try %d, pos: %lu, E=%d", i, curposition, e);
|
||||||
|
unsigned long targ = (e == ESW_CW_ACTIVE) ? curposition - (double)FOCSCALE_MM*0.2 : curposition + (double)FOCSCALE_MM*0.2;
|
||||||
|
|
||||||
|
if(move(targ, speed)) continue;
|
||||||
|
get_endswitches(&e);
|
||||||
|
if(e == ESW_INACTIVE) break;
|
||||||
|
}
|
||||||
|
// return esw state
|
||||||
|
parval = DI_ENSTOP;
|
||||||
|
if(CAN_NOERR != can_write_par(PAR_DI_SUBIDX, PAR_CW_IDX, &parval)) goto bad;
|
||||||
|
if(CAN_NOERR != can_write_par(PAR_DI_SUBIDX, PAR_CCW_IDX, &parval)) goto bad;
|
||||||
|
if(e != ESW_INACTIVE){
|
||||||
|
WARNX("Can't move out of end-switch");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
bad:
|
||||||
|
WARNX("Can't get/set esw parameters");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief init_motor_ids - convert motor address into CAN IDs (PO & data)
|
||||||
|
* run this function before any working with motor
|
||||||
|
* @param addr - motor driver address
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int init_motor_ids(int addr){
|
||||||
|
if(addr < 0 || addr > 0x3f){
|
||||||
|
WARNX("Wrong motor address, should be from 0 to 63");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
motor_id = MOTOR_PO_ID(addr);
|
||||||
|
motor_p_id = MOTOR_PAR_ID(addr);
|
||||||
|
DBG("motor POid=%lu, motor_PROCid=%lu", motor_id, motor_p_id);
|
||||||
|
// check esw roles & end-switches state
|
||||||
|
if(go_out_from_ESW()) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief getPos - get current encoder's position
|
||||||
|
* @param pos (o) - position value (in mm)
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int getPos(double *pos){
|
||||||
|
int r = !(getLong(encnodenum, DS406_POSITION_VAL, 0, &curposition));
|
||||||
|
if(pos) *pos = FOC_RAW2MM(curposition);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just return value of curposition in mm
|
||||||
|
double curPos(){
|
||||||
|
return FOC_RAW2MM(curposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief returnPreOper - return encoder into pre-operational state
|
||||||
|
*/
|
||||||
|
void returnPreOper(){
|
||||||
|
verbose("Return to Pre-operational... ");
|
||||||
|
setPreOper(encnodenum);
|
||||||
|
int state=getNodeState(encnodenum);
|
||||||
|
verbose("State=0x%02x - ", state);
|
||||||
|
if(state == NodePreOperational){
|
||||||
|
verbose("Ok!\n");
|
||||||
|
}else{
|
||||||
|
verbose("Failed!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief fix_targspeed - fix raw speed value if it is greater MAXSPEED or less than MINSPEED
|
||||||
|
* @param targspd (io) - raw target speed
|
||||||
|
*/
|
||||||
|
static void fix_targspeed(int16_t *targspd){
|
||||||
|
if(!targspd) return;
|
||||||
|
DBG("cur spd: %d", *targspd);
|
||||||
|
int16_t sign = (*targspd < 0) ? -1 : 1;
|
||||||
|
int16_t absspd = abs(*targspd);
|
||||||
|
if(absspd < MINSPEED) *targspd = sign * MINSPEED;
|
||||||
|
else if(absspd > MAXSPEED) *targspd = sign * MAXSPEED;
|
||||||
|
DBG("become spd: %d", *targspd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief can_send_chk - send CAN frame to motor & check answer
|
||||||
|
* @param buf (i) - PO data frame (6 bytes)
|
||||||
|
* @param obuf (o) - received PI data frame
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
static canstatus can_send_chk(unsigned char *buf, unsigned char *obuf){
|
||||||
|
const int l = 6; // frame length
|
||||||
|
/*if(G->verbose){
|
||||||
|
printf("Send frame to ID=%lu: ", motor_id);
|
||||||
|
for(int i = 0; i < l; ++i)
|
||||||
|
printf(" %02x", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
}*/
|
||||||
|
if(can_send_frame(motor_id, l, buf) <= 0){
|
||||||
|
WARNX("Error sending CAN frame (len %d)", l);
|
||||||
|
return CAN_CANTSEND;
|
||||||
|
}
|
||||||
|
int I, rxpnt, idr, dlen;
|
||||||
|
double rxtime;
|
||||||
|
unsigned char rdata[8];
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
for(I = 0; I < 50; ++I){
|
||||||
|
if(can_recv_frame(&rxpnt, &rxtime, &idr, &dlen, rdata) && (idr&0x1fffffff) == motor_id+1) break;
|
||||||
|
can_dsleep(0.01);
|
||||||
|
}
|
||||||
|
if(I == 50){
|
||||||
|
WARNX("Error getting answer");
|
||||||
|
return CAN_NOANSWER;
|
||||||
|
}
|
||||||
|
if(obuf) memcpy(obuf, rdata, l);
|
||||||
|
/*if(G->verbose){
|
||||||
|
printf("Got answer with ID=%d: ", idr&0x1fffffff);
|
||||||
|
for(int i = 0; i < dlen; ++i)
|
||||||
|
printf(" %02x", rdata[i]);
|
||||||
|
printf("\n");
|
||||||
|
}*/
|
||||||
|
if((rdata[0] & (SW_B_MAILFUN|SW_B_READY)) == SW_B_MAILFUN){ // error
|
||||||
|
WARNX("Mailfunction, error code: %d", rdata[1]);
|
||||||
|
return CAN_ERROR; // error
|
||||||
|
}else if((rdata[0] & (SW_B_MAILFUN|SW_B_READY)) == (SW_B_MAILFUN|SW_B_READY)){ // warning
|
||||||
|
WARNX("Warning, code: %d", rdata[1]);
|
||||||
|
buf[1] |= CW_B_CLERR; // try to clear warnings
|
||||||
|
can_send_frame(motor_id, l, buf);
|
||||||
|
return CAN_WARNING; // warning
|
||||||
|
}
|
||||||
|
//verbose("Got motor state: %d\n", rdata[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief can_send_parag - send/get motor parameters
|
||||||
|
* @param buf (i) - parameter out data frame (8 bytes)
|
||||||
|
* @param obuf (o) - parameter in data frame (8 bytes)
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
static canstatus can_send_param(unsigned char *buf, unsigned char *obuf){
|
||||||
|
const int l = 8; // frame length
|
||||||
|
int I, rxpnt, idr, dlen;
|
||||||
|
double rxtime;
|
||||||
|
unsigned char rdata[8];
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
if(can_send_frame(motor_p_id, l, buf) <= 0){
|
||||||
|
WARNX("Error sending CAN frame (len %d)", l);
|
||||||
|
return CAN_CANTSEND;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
green("Sent param: ");
|
||||||
|
for(int i=0; i<l; ++i) printf("0x%02x ", buf[i]);
|
||||||
|
printf("\n");
|
||||||
|
*/
|
||||||
|
for(I = 0; I < 50; ++I){
|
||||||
|
if(can_recv_frame(&rxpnt, &rxtime, &idr, &dlen, rdata) && (idr&0x1fffffff) == motor_p_id+1) break;
|
||||||
|
can_dsleep(0.01);
|
||||||
|
}
|
||||||
|
if(I == 50){
|
||||||
|
WARNX("Error getting answer");
|
||||||
|
return CAN_NOANSWER;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
green("Received param: ");
|
||||||
|
for(int i=0; i<dlen; ++i) printf("0x%02x ", rdata[i]);
|
||||||
|
printf("\n");
|
||||||
|
*/
|
||||||
|
if(obuf) memcpy(obuf, rdata, dlen);
|
||||||
|
if(obuf[0] & CAN_PAR_ERRFLAG){
|
||||||
|
WARNX("Wrong parameter idx/subidx or other error");
|
||||||
|
return CAN_WARNING;
|
||||||
|
}
|
||||||
|
if(*((uint32_t*)buf) != *((uint32_t*)obuf)){
|
||||||
|
WARNX("Got wrong answer for parameter request");
|
||||||
|
return CAN_WARNING;
|
||||||
|
}
|
||||||
|
return CAN_NOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief can_read_par - read motor parameter
|
||||||
|
* @param subidx - parameter subindex
|
||||||
|
* @param idx - parameter index
|
||||||
|
* @param parval (o) - parameter value
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
static canstatus can_read_par(uint8_t subidx, uint16_t idx, uint32_t *parval){
|
||||||
|
if(!parval) return CAN_WARNING;
|
||||||
|
uint8_t buf[8] = {CAN_READPAR_CMD, subidx}, obuf[8];
|
||||||
|
buf[2] = idx >> 8;
|
||||||
|
buf[3] = idx & 0xff;
|
||||||
|
canstatus s = can_send_param(buf, obuf);
|
||||||
|
if(s != CAN_NOERR) return s;
|
||||||
|
*parval = obuf[4]<<24 | obuf[5]<<16 | obuf[6]<<8 | obuf[7];
|
||||||
|
return CAN_NOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief can_write_par - write motor parameter
|
||||||
|
* @param subidx - parameter subindex
|
||||||
|
* @param idx - parameter index
|
||||||
|
* @param parval (i) - new parameter value (NULL for 0)
|
||||||
|
* @return status
|
||||||
|
*/
|
||||||
|
static canstatus can_write_par(uint8_t subidx, uint16_t idx, uint32_t *parval){
|
||||||
|
uint8_t buf[8] = {CAN_WRITEPAR_CMD, subidx,0}, obuf[8];
|
||||||
|
buf[2] = idx >> 8;
|
||||||
|
buf[3] = idx & 0xff;
|
||||||
|
if(parval){
|
||||||
|
uint32_t par = *parval;
|
||||||
|
obuf[4] = (par >> 24) & 0xff;
|
||||||
|
obuf[5] = (par >> 16) & 0xff;
|
||||||
|
obuf[6] = (par >> 8) & 0xff;
|
||||||
|
obuf[7] = par & 0xff;
|
||||||
|
}
|
||||||
|
return can_send_param(buf, obuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get_motor_speed
|
||||||
|
* @param motstatus (o) - status (if !NULL)
|
||||||
|
* @return speed in rev/min
|
||||||
|
*/
|
||||||
|
canstatus get_motor_speed(double *spd){
|
||||||
|
if(!spd) return CAN_WARNING;
|
||||||
|
union{
|
||||||
|
uint32_t u;
|
||||||
|
int32_t i;
|
||||||
|
} speed;
|
||||||
|
canstatus s = can_read_par(PAR_SPD_SUBIDX, PAR_SPD_IDX, &speed.u);
|
||||||
|
if(s != CAN_NOERR){
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
*spd = (double)speed.i / 1000.;
|
||||||
|
/*
|
||||||
|
union{
|
||||||
|
int16_t i16;
|
||||||
|
uint8_t c8[2];
|
||||||
|
} mspd;
|
||||||
|
mspd.c8[0] = rdata[3];
|
||||||
|
mspd.c8[1] = rdata[2];
|
||||||
|
*/
|
||||||
|
return CAN_NOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get_endswitches - get state of end-switches
|
||||||
|
* @param Esw (o) - end-switches state
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
canstatus get_endswitches(eswstate *Esw){
|
||||||
|
uint32_t val = 0;
|
||||||
|
canstatus s = can_read_par(PAR_DI_SUBIDX, PAR_DIST_IDX, &val);
|
||||||
|
if(Esw){
|
||||||
|
int v = 0;
|
||||||
|
if(!(val & ESW_CW)){ // + pressed
|
||||||
|
v |= ESW_CW_ACTIVE;
|
||||||
|
}
|
||||||
|
if(!(val & ESW_CCW)){ // - pressed
|
||||||
|
v |= ESW_CCW_ACTIVE;
|
||||||
|
}
|
||||||
|
*Esw = v;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop - stop motor
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int stop(){
|
||||||
|
unsigned char buf[6] = {0, CW_STOP,0,};
|
||||||
|
if(can_send_chk(buf, NULL)){
|
||||||
|
WARNX("Can't stop motor!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief movewconstspeed - move with constant speed
|
||||||
|
* @param rawspd - given speed
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int movewconstspeed(int spd){
|
||||||
|
int16_t targspd = RAWSPEED(spd);
|
||||||
|
unsigned char buf[8] = {0,};
|
||||||
|
buf[1] = CW_ENABLE;
|
||||||
|
if(MOTOR_REVERSE) spd = -spd;
|
||||||
|
buf[2] = (targspd >> 8) & 0xff;
|
||||||
|
buf[3] = targspd & 0xff;
|
||||||
|
if(can_send_chk(buf, NULL)){
|
||||||
|
WARNX("Can't move motor!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief move - move focuser from current position to approximately `targposition` with speed `rawspeed`
|
||||||
|
* @param targposition - target position in raw value
|
||||||
|
* @param rawspeed - speed in raw value
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
static int move(unsigned long targposition, int16_t rawspeed){
|
||||||
|
if(abs(targposition - curposition) < RAWPOS_TOLERANCE){
|
||||||
|
verbose("Already at position\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsigned char buf[6] = {0,};
|
||||||
|
DBG("Start moving with speed %d", rawspeed);
|
||||||
|
buf[1] = CW_ENABLE;
|
||||||
|
if(MOTOR_REVERSE) rawspeed = -rawspeed;
|
||||||
|
buf[2] = (rawspeed >> 8) & 0xff;
|
||||||
|
buf[3] = rawspeed & 0xff;
|
||||||
|
DBG("\tBUF: %d, %d, %d, %d", buf[0], buf[1], buf[2], buf[3]);
|
||||||
|
if(can_send_chk(buf, NULL)){
|
||||||
|
WARNX("Can't move motor!");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#ifdef EBUG
|
||||||
|
double t0 = can_dtime();
|
||||||
|
#endif
|
||||||
|
long olddiffr = targposition - curposition;
|
||||||
|
long corrvalue = (long)rawspeed*(long)rawspeed / STOPPING_COEFF; // correction due to stopping ramp
|
||||||
|
DBG("start-> curpos: %ld, difference: %ld, corrval: %ld, tm=%g",
|
||||||
|
curposition, olddiffr, corrvalue, can_dtime()-t0);
|
||||||
|
int i, zerctr = 0;
|
||||||
|
for(i = 0; i < MOVING_TIMEOUT; ++i){
|
||||||
|
can_dsleep(0.01);
|
||||||
|
//uint16_t motstat;
|
||||||
|
double speed;
|
||||||
|
eswstate esw;
|
||||||
|
if(emerg_stop){ // emergency stop activated
|
||||||
|
WARNX("Activated stop while moving");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(get_motor_speed(&speed) != CAN_NOERR){ // WTF?
|
||||||
|
WARNX("Unknown situation: can't get speed of moving motor");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(get_endswitches(&esw) != CAN_NOERR){
|
||||||
|
WARNX("Can't get endswitches state, stopping");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(esw != ESW_INACTIVE){ // OOps, something wrong
|
||||||
|
if(esw == ESW_BOTH_ACTIVE){ // error!
|
||||||
|
WARNX("Check end-switches, both active!");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//TODO: check wrong end-switches!
|
||||||
|
if(rawspeed > 0){ // moving CW, don't care for CCW esw state
|
||||||
|
if(esw == ESW_CW_ACTIVE){
|
||||||
|
WARNX("CW mowing: end-switch!");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}else{ // movig CCW
|
||||||
|
if(esw == ESW_CCW_ACTIVE){
|
||||||
|
WARNX("CCW mowing: end-switch!");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fabs(speed) < 0.1){ // || (motstat & (SW_B_UNBLOCK|SW_B_READY|SW_B_POUNBLOCK)) != (SW_B_UNBLOCK|SW_B_READY|SW_B_POUNBLOCK)){
|
||||||
|
if(++zerctr == 10){
|
||||||
|
WARNX("Motor stopped while moving!");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}else zerctr = 0;
|
||||||
|
if(!getLong(encnodenum, DS406_POSITION_VAL, 0, &curposition)) continue;
|
||||||
|
long diffr = targposition - curposition;
|
||||||
|
//DBG("Speed: %g, curpos: %ld, diff: %ld", speed, curposition, diffr);
|
||||||
|
if(abs(diffr) < corrvalue || abs(diffr) - RAWPOS_TOLERANCE > abs(olddiffr)){
|
||||||
|
DBG("OK! almost reach: olddif=%ld, diff=%ld, corrval=%ld, tm=%g", olddiffr, diffr, corrvalue, can_dtime()-t0);
|
||||||
|
olddiffr = diffr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
olddiffr = diffr;
|
||||||
|
}
|
||||||
|
if(i == MOVING_TIMEOUT){
|
||||||
|
WARNX("Error: timeout, but motor still not @ position! STOP!");
|
||||||
|
stop();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
DBG("end-> curpos: %ld, difference: %ld, tm=%g\n", curposition, targposition - curposition, can_dtime()-t0);
|
||||||
|
long oldposition;
|
||||||
|
int r = stop();
|
||||||
|
if(r) r = stop();
|
||||||
|
if(!r) do{ // wait till stop
|
||||||
|
oldposition = curposition;
|
||||||
|
can_dsleep(0.1);
|
||||||
|
getLong(encnodenum, DS406_POSITION_VAL, 0, &curposition);
|
||||||
|
//DBG("wait-> curpos: %ld, difference: %ld, tm=%g\n", curposition, targposition - curposition, can_dtime()-t0);
|
||||||
|
}while((long)curposition != oldposition);
|
||||||
|
if(abs(targposition - curposition) > RAWPOS_TOLERANCE)
|
||||||
|
verbose("Current (%ld) position is too far from target (%ld)\n", curposition, targposition);
|
||||||
|
DBG("stop-> curpos: %ld, difference: %ld, tm=%g\n", curposition, targposition - curposition, can_dtime()-t0);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief move2pos - accurate focus moving to target position (in encoder's units)
|
||||||
|
* @param target - position 2 move (in mm)
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int move2pos(double target){
|
||||||
|
double cur;
|
||||||
|
if(getPos(&cur)){
|
||||||
|
WARNX("Can't get current position!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
unsigned long targposition = FOC_MM2RAW(target);
|
||||||
|
DBG("Raw target position: %lu", targposition);
|
||||||
|
if(target > FOCMAX_MM || target < FOCMIN_MM || targposition < FOCMIN || targposition > FOCMAX){
|
||||||
|
WARNX("Target focus position over the available range!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(abs(targposition - curposition) < RAWPOS_TOLERANCE){
|
||||||
|
verbose("Already at position\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
long targ0pos = (long)targposition + (long)dF0;
|
||||||
|
long spd = (targ0pos - (long)curposition) / 2L;
|
||||||
|
int16_t targspd = (int16_t) spd;
|
||||||
|
if(spd > INT16_MAX) targspd = INT16_MAX;
|
||||||
|
else if(spd < INT16_MIN) targspd = INT16_MIN;
|
||||||
|
fix_targspeed(&targspd);
|
||||||
|
// check moving direction: thin focus correction always should run to negative!
|
||||||
|
if(targposition < curposition){ // we are from the right
|
||||||
|
if(targspd < -MINSPEED*3/2){ // omit rough moving to focus value if there's too little distance towards target
|
||||||
|
// rough moving
|
||||||
|
DBG("1) ROUGH move to the LEFT: curpos=%ld, difference=%ld\n", curposition, targ0pos - (long)curposition);
|
||||||
|
if(move(targ0pos, RAWSPEED(targspd))){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{ // we are from the left - move to the point @ right side of target
|
||||||
|
DBG("1) ROUGH move to the RIGHT: curpos=%ld, difference=%ld\n", curposition, targ0pos - (long)curposition);
|
||||||
|
if(move(targ0pos, RAWSPEED(targspd))){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now move precisely
|
||||||
|
if(!getLong(encnodenum, DS406_POSITION_VAL, 0, &curposition)){
|
||||||
|
WARNX("Can't get current position");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(abs(targposition - curposition) < RAWPOS_TOLERANCE){
|
||||||
|
verbose("Catch the position @ rough movint\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(curposition < targposition){
|
||||||
|
WARNX("Error in current position!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
DBG("2) curpos: %ld, difference: %ld\n", curposition, (long)targposition - (long)curposition);
|
||||||
|
// now make an accurate moving
|
||||||
|
if(move(targposition, -(RAWSPEED(MINSPEED)))){
|
||||||
|
WARNX("Can't catch focus precisely!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(!getLong(encnodenum, DS406_POSITION_VAL, 0, &curposition)){
|
||||||
|
WARNX("Can't get current position");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(abs(targposition - curposition) > RAWPOS_TOLERANCE){
|
||||||
|
verbose("Stopped over the accuracy range\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// return 0 if all OK
|
||||||
|
static int get_pos_speed(unsigned long *pos, double *speed){
|
||||||
|
int ret = 0;
|
||||||
|
if(pos){
|
||||||
|
if(!getLong(encnodenum, DS406_POSITION_VAL, 0, pos)) ret = 1;
|
||||||
|
}
|
||||||
|
if(speed){
|
||||||
|
if(get_motor_speed(speed) != CAN_NOERR){
|
||||||
|
*speed = 0.;
|
||||||
|
ret = 1;
|
||||||
|
}else if(MOTOR_REVERSE) *speed = -*speed;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void movewithmon(double spd){
|
||||||
|
unsigned char buf[6] = {0,};
|
||||||
|
if(fabs(spd) < MINSPEED || fabs(spd) > MAXSPEED){
|
||||||
|
WARNX("Target speed should be be from %d to %d (rev/min)", MINSPEED, MAXSPEED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned long pos, oldpos = 0, startpos;
|
||||||
|
double speed;
|
||||||
|
get_pos_speed(&startpos, NULL); // starting position
|
||||||
|
int16_t targspd = RAWSPEED(spd);
|
||||||
|
buf[1] = CW_ENABLE;
|
||||||
|
if(MOTOR_REVERSE) targspd = -targspd;
|
||||||
|
buf[2] = (targspd >> 8) & 0xff;
|
||||||
|
buf[3] = targspd & 0xff;
|
||||||
|
if(can_send_chk(buf, NULL)){
|
||||||
|
WARNX("Can't move motor!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double t0 = can_dtime(), tlast = t0;
|
||||||
|
green("\nAcceleration with monitoring not longer than for 4 seconds\n\n");
|
||||||
|
#define PRINT() do{printf("t=%g, pos=%lu (%.3fmm), spd=%g, Dpos=%.0f\n", tcur - t0, pos, \
|
||||||
|
FOC_RAW2MM(pos), speed, oldpos ? ((double)pos - oldpos)/(tcur - tlast) : 0);}while(0)
|
||||||
|
while(can_dtime() - t0 < 4.){
|
||||||
|
if(get_pos_speed(&pos, &speed)){ // can't get speed? WTF?
|
||||||
|
WARNX("Strange things are going here...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
double tcur = can_dtime();
|
||||||
|
PRINT();
|
||||||
|
oldpos = pos;
|
||||||
|
if(fabs(speed - spd) < 1.){
|
||||||
|
green("\tTarget speed reached for %.2fs, DPOS=%ld\n", tlast - t0, pos - startpos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tlast = tcur;
|
||||||
|
can_dsleep(0.03);
|
||||||
|
}
|
||||||
|
green("\nMove for 3 seconds with constant speed\n\n");
|
||||||
|
get_pos_speed(&startpos, NULL);
|
||||||
|
t0 = can_dtime();
|
||||||
|
do{
|
||||||
|
can_dsleep(0.5);
|
||||||
|
if(get_pos_speed(&pos, &speed)){ // can't get speed? WTF?
|
||||||
|
WARNX("Strange things are going there...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
double tcur = can_dtime();
|
||||||
|
PRINT();
|
||||||
|
oldpos = pos;
|
||||||
|
tlast = tcur;
|
||||||
|
}while(can_dtime() - t0 < 3.);
|
||||||
|
double meanspd = ((double)pos - startpos) / (tlast - t0);
|
||||||
|
green("\tMean pos speed: %.0f (%g mm/s)\n", meanspd, FOC_RAW2MM(meanspd));
|
||||||
|
green("\nStop with monitoring not longer than for 4 seconds\n\n");
|
||||||
|
get_pos_speed(&startpos, NULL);
|
||||||
|
t0 = can_dtime();
|
||||||
|
for(int i = 0; i < 100 && stop(); ++i);
|
||||||
|
while(can_dtime() - t0 < 4.){
|
||||||
|
get_pos_speed(&pos, NULL);
|
||||||
|
double tcur = can_dtime();
|
||||||
|
PRINT();
|
||||||
|
if(oldpos == pos){
|
||||||
|
green("\tStopped for %.2fs, DPOS=%ld\n", tlast - t0, pos - startpos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
oldpos = pos;
|
||||||
|
tlast = tcur;
|
||||||
|
can_dsleep(0.03);
|
||||||
|
}
|
||||||
|
#undef PRINT
|
||||||
|
}
|
||||||
53
Z1000_focus/can_encoder.h
Normal file
53
Z1000_focus/can_encoder.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Zphocus project.
|
||||||
|
* Copyright 2019 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 CAN_ENCODER_H__
|
||||||
|
#define CAN_ENCODER_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum{
|
||||||
|
CAN_NOERR = 0,
|
||||||
|
CAN_CANTSEND,
|
||||||
|
CAN_NOANSWER,
|
||||||
|
CAN_WARNING,
|
||||||
|
CAN_ERROR
|
||||||
|
} canstatus;
|
||||||
|
|
||||||
|
typedef enum{
|
||||||
|
ESW_INACTIVE = 0,
|
||||||
|
ESW_CW_ACTIVE = 1,
|
||||||
|
ESW_CCW_ACTIVE = 2,
|
||||||
|
ESW_BOTH_ACTIVE = 3
|
||||||
|
} eswstate;
|
||||||
|
|
||||||
|
int init_encoder(int encnode, int reset);
|
||||||
|
void returnPreOper();
|
||||||
|
int getPos(double *pos);
|
||||||
|
double curPos();
|
||||||
|
int init_motor_ids(int addr);
|
||||||
|
void movewithmon(double spd);
|
||||||
|
canstatus get_motor_speed(double *spd);
|
||||||
|
canstatus get_endswitches(eswstate *Esw);
|
||||||
|
int move2pos(double target);
|
||||||
|
int stop();
|
||||||
|
int movewconstspeed(int spd);
|
||||||
|
int go_out_from_ESW();
|
||||||
|
|
||||||
|
#endif // CAN_ENCODER_H__
|
||||||
566
Z1000_focus/can_io.c
Normal file
566
Z1000_focus/can_io.c
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
/* CAN I/O library (to use as a process)
|
||||||
|
* usage:
|
||||||
|
* first: fork() + start_can_io(NULL) - start CAN Rx-buffering process
|
||||||
|
* then: fork() + Control_1(....) - start process that uses recv/send functions
|
||||||
|
* ...........................
|
||||||
|
* then: fork() + Control_N(....)
|
||||||
|
*
|
||||||
|
* note: use init_can_io() at the begining of every Control process
|
||||||
|
* BUT DON't USE it in main() before Control process start
|
||||||
|
* ^^^^^^^^^^^^^
|
||||||
|
* (c) vsher@sao.ru
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/msg.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "canmsg.h"
|
||||||
|
#include "can_io.h"
|
||||||
|
|
||||||
|
char can_dev[40] = "/dev/can0";
|
||||||
|
int can_fd=-1;
|
||||||
|
char can_lck[40] = "/tmp/dev_can0.lock";
|
||||||
|
int can_lk=-1;
|
||||||
|
static int server_mode=0;
|
||||||
|
static int my_uid;
|
||||||
|
|
||||||
|
#define CAN_SHM_SIZE ((sizeof(int)*4)+CAN_CTLR_SIZE+(CAN_RX_SIZE*sizeof(canmsg_t)))
|
||||||
|
|
||||||
|
union ShMkey {
|
||||||
|
char name[5];
|
||||||
|
key_t code;
|
||||||
|
} can_shm_key;
|
||||||
|
|
||||||
|
int can_shm_id=-1;
|
||||||
|
char *can_shm_addr = NULL;
|
||||||
|
|
||||||
|
#define can_pid (*(((int *)can_shm_addr)+0)) /* PID of CAN I/O process */
|
||||||
|
#define can_open (*(((int *)can_shm_addr)+1)) /* file descr.of CAN-driver */
|
||||||
|
#define rx_buff_pntr (*(((int *)can_shm_addr)+2)) /* from 0 till CAN_RX_SIZE-1 */
|
||||||
|
#define can_mode (*(((int *)can_shm_addr)+3)) /* CAN server/client mode flags */
|
||||||
|
#define CAN_SEND_SERVER 1 /* clients should try to send frames through CAN-server or directly to CAN-driver otherwise */
|
||||||
|
|
||||||
|
void *can_ctrl_addr = NULL; /* shm area reserved for control process purpose*/
|
||||||
|
canmsg_t *rx_buff; /* rx ring buffer: CAN_RX_SIZE*sizeof(canmsg_t)*/
|
||||||
|
|
||||||
|
struct CMD_Queue { /* ÏÐÉÓÁÎÉÅ ÏÞÅÒÅÄÉ (ËÁÎÁÌÁ) ËÏÍÁÎÄ */
|
||||||
|
union{
|
||||||
|
char name[5]; /* ËÌÀÞ ÉÄÅÎÔÅÆÉËÁÃÉÉ ÏÞÅÒÅÄÉ */
|
||||||
|
key_t code;
|
||||||
|
} key;
|
||||||
|
int mode; /* ÒÅÖÉÍ ÄÏÓÔÕÐÁ (rwxrwxrwx) */
|
||||||
|
int side; /* ÔÉÐ ÐÏÄÓÏÅÄÉÎÅÎÉÑ: ëÌÉÅÎÔ/óÅÒ×ÅÒ (Sender/Receiver)*/
|
||||||
|
int id; /* ÄÅÓËÒÉÐÔÏÒ ÐÏÄÓÏÅÄÉÎÅÎÉÑ */
|
||||||
|
unsigned int acckey; /* ËÌÀÞ ÄÏÓÔÕÐÁ (ÄÌÑ ÐÅÒÅÄÁÞÉ ëÌÉÅÎÔ->óÅÒ×ÅÒ) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ËÁÎÁÌ ËÏÍÁÎÄ ÉÓÐÏÌØÚÕÅÍ ÄÌÑ ÐÅÒÅÄÁÞÉ CAN-ÆÒÅÊÍÏ× */
|
||||||
|
static struct CMD_Queue canout = {.key.name = {'C','A','N',0,0},0200,0,-1,0};
|
||||||
|
|
||||||
|
/* ÓÔÒÕËÔÕÒÁ ÓÏÏÂÝÅÎÉÑ */
|
||||||
|
struct my_msgbuf {
|
||||||
|
unsigned long mtype; /* type of message */
|
||||||
|
unsigned long acckey; /* ËÌÀÞ ÄÏÓÔÕÐÁ ËÌÉÅÎÔÁ */
|
||||||
|
unsigned long src_pid; /* ÎÏÍÅÒ ÐÒÏÃÅÓÓÁ ÉÓÔÏÞÎÉËÁ */
|
||||||
|
unsigned long src_ip; /* IP-ÁÄÒ. ÉÓÔÏÞÎÉËÁ, =0 - ÌÏËÁÌØÎÁÑ ËÏÍÁÎÄÁ */
|
||||||
|
char mtext[100]; /* message text */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void can_abort(int sig);
|
||||||
|
|
||||||
|
void set_server_mode(int mode) {server_mode=mode;}
|
||||||
|
int can_server() {return(server_mode);}
|
||||||
|
void set_sending_mode(int to_server) {
|
||||||
|
if(to_server) can_mode |= CAN_SEND_SERVER;
|
||||||
|
else can_mode &= ~CAN_SEND_SERVER;
|
||||||
|
}
|
||||||
|
int can_sending_mode() {return(can_mode&CAN_SEND_SERVER);}
|
||||||
|
int can_card() {return(can_fd>0);}
|
||||||
|
int can_gate() {return(0);}
|
||||||
|
double can_gate_time_offset() {return(0.0);}
|
||||||
|
|
||||||
|
void setup_can_net(_U_ unsigned long ipaddr, _U_ int port, _U_ unsigned long acckey) {return;}
|
||||||
|
|
||||||
|
unsigned long get_acckey() {return(0);}
|
||||||
|
|
||||||
|
static int shm_created=0;
|
||||||
|
|
||||||
|
/* to use _AFTER_ process forking */
|
||||||
|
void *init_can_io() { /* returns shared area addr. for client control process*/
|
||||||
|
int new_shm=0;
|
||||||
|
char *p, msg[256];
|
||||||
|
|
||||||
|
my_uid=geteuid();
|
||||||
|
if(!can_shm_addr){
|
||||||
|
if((p = strrchr(can_dev,'/'))){
|
||||||
|
memcpy(&can_lck[9], p+1, 4);
|
||||||
|
memcpy(can_shm_key.name, p+1, 4);
|
||||||
|
can_shm_key.name[4]='\0';
|
||||||
|
}else{
|
||||||
|
fprintf(stderr,"Wrong CAN device name: %s\n", can_dev);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, 0644);
|
||||||
|
if(can_shm_id<0 && errno==EACCES)
|
||||||
|
can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, 0444);
|
||||||
|
if(can_shm_id<0 && errno==ENOENT && server_mode) {
|
||||||
|
can_shm_id = shmget(can_shm_key.code, CAN_SHM_SIZE, IPC_CREAT|IPC_EXCL|0644);
|
||||||
|
new_shm = shm_created = 1;
|
||||||
|
}
|
||||||
|
if(can_shm_id<0){
|
||||||
|
can_prtime(stderr);
|
||||||
|
if(new_shm)
|
||||||
|
sprintf(msg,"Can't create shm CAN buffer '%s'",can_shm_key.name);
|
||||||
|
else if(server_mode)
|
||||||
|
sprintf(msg,"CAN-I/O: Can't find shm segment for CAN buffer '%s'",can_shm_key.name);
|
||||||
|
else
|
||||||
|
snprintf(msg, 256, "Can't find shm segment for CAN buffer '%s' (maybe no CAN-I/O process?)",can_shm_key.name);
|
||||||
|
perror(msg);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
can_shm_addr = shmat(can_shm_id, NULL, 0);
|
||||||
|
if(can_shm_addr == (void*)-1 && errno == EACCES)
|
||||||
|
can_shm_addr = shmat(can_shm_id, NULL, SHM_RDONLY);
|
||||||
|
if(can_shm_addr == (void*)-1){
|
||||||
|
sprintf(msg,"Can't attach shm CAN buffer '%s'",can_shm_key.name);
|
||||||
|
perror(msg);
|
||||||
|
shmctl(can_shm_id, IPC_RMID, NULL);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
can_ctrl_addr = (canmsg_t *)(can_shm_addr+sizeof(int)*4);
|
||||||
|
rx_buff = (canmsg_t *)(can_ctrl_addr+CAN_CTLR_SIZE);
|
||||||
|
|
||||||
|
if(can_fd < 0 && canout.id < 0){
|
||||||
|
int flags = (server_mode)? O_RDWR : O_WRONLY;
|
||||||
|
if(server_mode){
|
||||||
|
if(( can_fd = open(can_dev, flags)) < 0 ){
|
||||||
|
sprintf(msg,"CAN-I/O: Error opening CAN device %s", can_dev);
|
||||||
|
can_prtime(stderr);
|
||||||
|
perror(msg);
|
||||||
|
shmctl(can_shm_id, IPC_RMID, NULL);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
can_fd = open(can_dev, flags);
|
||||||
|
canout.id = msgget(canout.key.code, canout.mode);
|
||||||
|
if(can_fd < 0 && canout.id < 0) {
|
||||||
|
snprintf(msg, 256, "Error opening CAN device(%s) or CAN output queue '%s' (maybe no CANqueue server process?)",can_dev,canout.key.name);
|
||||||
|
perror(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(can_lk > 0) close(can_lk);
|
||||||
|
if(( can_lk = open(can_lck, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH )) < 0 ){
|
||||||
|
sprintf(msg,"Error opening CAN device lock-file %s", can_lck);
|
||||||
|
perror(msg);
|
||||||
|
shmctl(can_shm_id, IPC_RMID, NULL);
|
||||||
|
close(can_fd);
|
||||||
|
exit(errno);
|
||||||
|
}
|
||||||
|
fchmod(can_lk, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
|
||||||
|
if(new_shm){
|
||||||
|
struct timeval tmv;
|
||||||
|
struct timezone tz;
|
||||||
|
gettimeofday(&tmv,&tz);
|
||||||
|
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
|
||||||
|
can_pid = 0;
|
||||||
|
can_open = -1;
|
||||||
|
rx_buff_pntr = 0;
|
||||||
|
for(int i = 0; i < CAN_RX_SIZE; ++i){
|
||||||
|
rx_buff[i].id = 0;
|
||||||
|
rx_buff[i].timestamp = tmv;
|
||||||
|
}
|
||||||
|
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
|
||||||
|
}
|
||||||
|
signal(SIGHUP, can_exit);
|
||||||
|
signal(SIGINT, can_exit);
|
||||||
|
signal(SIGQUIT,can_exit);
|
||||||
|
signal(SIGTERM,can_exit);
|
||||||
|
return(can_ctrl_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CAN "Rx to buff" process */
|
||||||
|
void *start_can_io(_U_ void *arg){
|
||||||
|
set_server_mode(1);
|
||||||
|
init_can_io();
|
||||||
|
if(can_io_ok()){
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"CAN I/O process(%d) already running!\n",can_pid);
|
||||||
|
sleep(1);
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"New CAN I/O process(%d) exiting...!\n",getpid());
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if(can_fd < 0){
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"Error opening CAN device %s\n", can_dev);
|
||||||
|
shmctl(can_shm_id, IPC_RMID, NULL);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
can_pid = getpid();
|
||||||
|
can_open = can_fd;
|
||||||
|
can_mode = 0;
|
||||||
|
|
||||||
|
signal(SIGHUP, can_abort);
|
||||||
|
signal(SIGINT, can_abort);
|
||||||
|
signal(SIGQUIT,can_abort);
|
||||||
|
signal(SIGFPE, can_abort);
|
||||||
|
signal(SIGPIPE,can_abort);
|
||||||
|
signal(SIGSEGV,can_abort);
|
||||||
|
signal(SIGALRM, SIG_IGN);
|
||||||
|
signal(SIGTERM,can_abort);
|
||||||
|
|
||||||
|
if(shmctl(can_shm_id, SHM_LOCK, NULL) < 0)
|
||||||
|
perror("CAN I/O: can't prevents swapping of Rx-buffer area");
|
||||||
|
|
||||||
|
while(1){
|
||||||
|
int n;
|
||||||
|
canmsg_t rx;
|
||||||
|
|
||||||
|
if(!can_io_shm_ok()){can_delay(0.3); continue;}
|
||||||
|
|
||||||
|
n = can_wait(can_fd, 0.3);
|
||||||
|
if(n < 0) sleep(1);
|
||||||
|
if(n <= 0) continue;
|
||||||
|
|
||||||
|
do{
|
||||||
|
//static struct timeval tm = {0,0};
|
||||||
|
n = read(can_fd, &rx, sizeof(struct canmsg_t));
|
||||||
|
if(n < 0){
|
||||||
|
perror("CAN Rx error");
|
||||||
|
} else if(n > 0) {
|
||||||
|
/* work around the timestamp bug in old driver version
|
||||||
|
while((double)rx.timestamp.tv_sec+(double)rx.timestamp.tv_usec/1e6 < (double)tm.tv_sec+(double)tm.tv_usec/1e6) {
|
||||||
|
rx.timestamp.tv_usec += 10000;
|
||||||
|
if(rx.timestamp.tv_usec > 1000000) {
|
||||||
|
rx.timestamp.tv_sec++;
|
||||||
|
rx.timestamp.tv_usec -= 1000000;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
|
||||||
|
rx_buff[rx_buff_pntr] = rx;
|
||||||
|
rx_buff_pntr = (rx_buff_pntr + 1) % CAN_RX_SIZE;
|
||||||
|
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
|
||||||
|
//fprintf(stderr,"%d read(id=%02x,len=%d)\n",rx_buff_pntr,rx.id,rx.length);fflush(stderr);
|
||||||
|
/*fprintf(stderr,"reading CAN: 1 frame\n");*/
|
||||||
|
}/*else {
|
||||||
|
* fprintf(stderr,"reading CAN: nothing\n");fflush(stderr);
|
||||||
|
} */
|
||||||
|
} while(n>0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* put CAN-frame to recv-buffer */
|
||||||
|
void can_put_buff_frame(double rtime, int id, int length, unsigned char data[]) {
|
||||||
|
int i;
|
||||||
|
canmsg_t rx;
|
||||||
|
int sec = (int)rtime;
|
||||||
|
if(!server_mode) return;
|
||||||
|
if(length<0) length=0;
|
||||||
|
if(length>8) length=8;
|
||||||
|
rx.id = id&((id&CAN_EXT_FLAG)? 0x1fffffff : 0x7ff);
|
||||||
|
rx.cob=0;
|
||||||
|
rx.flags = ((id&CAN_RTR_FLAG)?MSG_RTR:0)|((id&CAN_EXT_FLAG)?MSG_EXT:0);
|
||||||
|
rx.length=length;
|
||||||
|
for(i=0; i<length; i++) rx.data[i]=data[i];
|
||||||
|
rx.timestamp.tv_sec = sec;
|
||||||
|
rx.timestamp.tv_usec = (int)((rtime-sec)*1000000.);
|
||||||
|
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
|
||||||
|
rx_buff[rx_buff_pntr] = rx;
|
||||||
|
rx_buff_pntr = (rx_buff_pntr + 1) % CAN_RX_SIZE;
|
||||||
|
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
|
||||||
|
//fprintf(stderr,"put_buf(id=%02x,flag=%1x,len=%d)\n",rx.id,rx.flags,rx.length);fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ÷ÓÅ ÎÏÒÍÁÌØÎÏ Ó SHM-ÂÕÆÅÒÏÍ CAN-I/O ÐÒÏÃÅÓÓÁ */
|
||||||
|
int can_io_shm_ok(){
|
||||||
|
return(can_pid>0 && can_open>0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ÷ÓÅ ÎÏÒÍÁÌØÎÏ Ó CAN-I/O ÐÒÏÃÅÓÓÏÍ */
|
||||||
|
/* (ÎÏ ÎÁÄÏ ÂÙÔØ ÓÕÐÅÒ-ÀÚÅÒÏÍ!) */
|
||||||
|
int can_io_ok(){
|
||||||
|
return(can_io_shm_ok() && (my_uid!=0||kill(can_pid, 0)==0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ÷ÏÚÍÏÖÎÁ ÒÁÂÏÔÁ c CAN ÄÌÑ ËÌÉÅÎÔÁ */
|
||||||
|
int can_ok() {
|
||||||
|
return(can_io_shm_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* wait for CAN-frame */
|
||||||
|
int can_wait(int fd, double tout){
|
||||||
|
int nfd,width;
|
||||||
|
struct timeval tv;
|
||||||
|
fd_set readfds;
|
||||||
|
|
||||||
|
if(fd==0 && tout>=0.01){
|
||||||
|
double dt = can_dsleep(tout);
|
||||||
|
if(dt>0.) can_dsleep(dt);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
if(fd<0) fd=can_fd;
|
||||||
|
if(fd>0){
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(fd, &readfds);
|
||||||
|
width = fd+1;
|
||||||
|
} else width = 0;
|
||||||
|
tv.tv_sec = (int)tout;
|
||||||
|
tv.tv_usec = (int)((tout - tv.tv_sec)*1000000.+0.9);
|
||||||
|
slipping:
|
||||||
|
if(fd>0 && can_fd>0)
|
||||||
|
nfd = select(width, &readfds, (fd_set *)NULL, (fd_set *)NULL, &tv);
|
||||||
|
else
|
||||||
|
nfd = select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, &tv);
|
||||||
|
if(nfd < 0){
|
||||||
|
if(errno == EINTR)
|
||||||
|
goto slipping;
|
||||||
|
perror("Error in can_wait(){ select() }");
|
||||||
|
return(-1);
|
||||||
|
} else if(nfd == 0) /* timeout! */
|
||||||
|
return(0);
|
||||||
|
if(fd>0 && FD_ISSET(fd, &readfds)) /* Rx frame! */
|
||||||
|
return(1);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cleanup recv-buffer in client process */
|
||||||
|
void can_clean_recv(int *pbuf, double *rtime) {
|
||||||
|
struct timeval tmv;
|
||||||
|
struct timezone tz;
|
||||||
|
gettimeofday(&tmv,&tz);
|
||||||
|
*pbuf = rx_buff_pntr;
|
||||||
|
*rtime = tmv.tv_sec + (double)tmv.tv_usec/1000000.;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find next rx-frame in recv-buffer for client process */
|
||||||
|
int can_recv_frame(int *pbuf, double *rtime,
|
||||||
|
int *id, int *length, unsigned char data[]){
|
||||||
|
return(can_get_buff_frame(pbuf, rtime, id, length, data));
|
||||||
|
}
|
||||||
|
int can_get_buff_frame(int *pbuf, double *rtime,
|
||||||
|
int *id, int *length, unsigned char data[]) {
|
||||||
|
while(*pbuf != rx_buff_pntr){
|
||||||
|
canmsg_t *rx = &rx_buff[*pbuf];
|
||||||
|
struct timeval *tv = &rx->timestamp;
|
||||||
|
double t_rx;
|
||||||
|
|
||||||
|
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
|
||||||
|
|
||||||
|
t_rx = tv->tv_sec + (double)tv->tv_usec/1000000.;
|
||||||
|
if(t_rx+1. >= *rtime){
|
||||||
|
int i;
|
||||||
|
*id = rx->id | ((rx->flags&MSG_RTR)? CAN_RTR_FLAG:0);
|
||||||
|
*length = rx->length;
|
||||||
|
for(i = 0; i < *length; i++)
|
||||||
|
data[i] = rx->data[i];
|
||||||
|
*rtime = t_rx;
|
||||||
|
*pbuf = (*pbuf + 1) % CAN_RX_SIZE;
|
||||||
|
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
*pbuf = (*pbuf + 1) % CAN_RX_SIZE;
|
||||||
|
|
||||||
|
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send tx-frame from client process */
|
||||||
|
/* to CAN-driver or to output queue */
|
||||||
|
int can_send_frame(unsigned long id, int length, unsigned char data[]) {
|
||||||
|
int i, ret=1;
|
||||||
|
if(can_fd<0 && canout.id<0)
|
||||||
|
return(0);
|
||||||
|
if(length>8) length=8;
|
||||||
|
if(length<0) length=0;
|
||||||
|
if(!server_mode && (can_mode&CAN_SEND_SERVER) && canout.id>=0 )
|
||||||
|
goto send2server;
|
||||||
|
if(can_fd >= 0){
|
||||||
|
canmsg_t tx;
|
||||||
|
tx.id = id&((id&CAN_EXT_FLAG)? 0x1fffffff : 0x7ff);
|
||||||
|
tx.cob=0;
|
||||||
|
tx.flags = ((id&CAN_RTR_FLAG)?MSG_RTR:0)|((id&CAN_EXT_FLAG)?MSG_EXT:0);
|
||||||
|
tx.length=length;
|
||||||
|
for(i=0;i<length;i++) tx.data[i]=data[i];
|
||||||
|
if(flock(can_lk, LOCK_EX)<0) perror("locking CAN");
|
||||||
|
//fprintf(stderr,"write(id=%02x,flag=%1x,len=%d)\n",tx.id,tx.flags,tx.length);fflush(stderr);
|
||||||
|
ret = write(can_fd, &tx, sizeof(struct canmsg_t));
|
||||||
|
if(flock(can_lk, LOCK_UN)<0) perror("unlocking CAN");
|
||||||
|
if(server_mode)
|
||||||
|
/* copy tx CAN-frame back to recv-buffer */
|
||||||
|
can_put_buff_frame(can_dtime(), id, length, data);
|
||||||
|
} else if(canout.id >= 0){
|
||||||
|
struct my_msgbuf mbuf;
|
||||||
|
send2server:
|
||||||
|
mbuf.src_pid = getpid();
|
||||||
|
mbuf.src_ip = 0;
|
||||||
|
mbuf.acckey = canout.acckey;
|
||||||
|
mbuf.mtype = id+1;
|
||||||
|
for(i=0;i<length;i++) mbuf.mtext[i]=data[i];
|
||||||
|
msgsnd( canout.id, (struct msgbuf *)&mbuf, length+12, IPC_NOWAIT);
|
||||||
|
}
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void can_abort(int sig) {
|
||||||
|
char ss[10];
|
||||||
|
struct shmid_ds buf;
|
||||||
|
|
||||||
|
if(sig) signal(sig,SIG_IGN);
|
||||||
|
if(!server_mode) can_exit(sig);
|
||||||
|
switch(sig){
|
||||||
|
case 0 : strcpy(ss," "); break;
|
||||||
|
case SIGHUP : strcpy(ss,"SIGHUP -"); break;
|
||||||
|
case SIGINT : strcpy(ss,"SIGINT -"); break;
|
||||||
|
case SIGQUIT: strcpy(ss,"SIGQUIT -"); break;
|
||||||
|
case SIGFPE : strcpy(ss,"SIGFPE -"); break;
|
||||||
|
case SIGPIPE: strcpy(ss,"SIGPIPE -"); break;
|
||||||
|
case SIGSEGV: strcpy(ss,"SIGSEGV -"); break;
|
||||||
|
case SIGTERM: strcpy(ss,"SIGTERM -"); break;
|
||||||
|
default: sprintf(ss,"SIG_%d -",sig); break;
|
||||||
|
}
|
||||||
|
switch(sig){
|
||||||
|
default:
|
||||||
|
case SIGHUP :
|
||||||
|
case SIGINT :
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"CAN I/O: %s Ignore .....\n",ss);
|
||||||
|
fflush(stderr);
|
||||||
|
signal(sig, can_abort);
|
||||||
|
return;
|
||||||
|
case SIGPIPE:
|
||||||
|
case SIGQUIT:
|
||||||
|
case SIGFPE :
|
||||||
|
case SIGSEGV:
|
||||||
|
case SIGTERM:
|
||||||
|
signal(SIGALRM, can_abort);
|
||||||
|
alarm(2);
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"CAN I/O: %s process should stop after 2sec delay...\n",ss);
|
||||||
|
fflush(stderr);
|
||||||
|
close(can_fd);
|
||||||
|
can_fd = can_open = -1;
|
||||||
|
return;
|
||||||
|
case SIGALRM:
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"CAN I/O: process stop!\n");
|
||||||
|
fflush(stderr);
|
||||||
|
close(can_lk);
|
||||||
|
can_lk = -1;
|
||||||
|
can_pid = 0;
|
||||||
|
shmdt(can_shm_addr);
|
||||||
|
shmctl(can_shm_id, IPC_STAT, &buf);
|
||||||
|
if(buf.shm_nattch == 0){
|
||||||
|
shmctl(can_shm_id, SHM_UNLOCK, NULL);
|
||||||
|
shmctl(can_shm_id, IPC_RMID, NULL);
|
||||||
|
}
|
||||||
|
exit(sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void can_exit(int sig) {
|
||||||
|
char ss[16];
|
||||||
|
|
||||||
|
struct shmid_ds buf;
|
||||||
|
if(sig) signal(sig,SIG_IGN);
|
||||||
|
if(server_mode) can_abort(sig);
|
||||||
|
switch (sig) {
|
||||||
|
case 0 : strcpy(ss,"Exiting - "); break;
|
||||||
|
case SIGHUP : strcpy(ss,"SIGHUP -"); break;
|
||||||
|
case SIGINT : strcpy(ss,"SIGINT -"); break;
|
||||||
|
case SIGQUIT: strcpy(ss,"SIGQUIT -"); break;
|
||||||
|
case SIGFPE : strcpy(ss,"SIGFPE -"); break;
|
||||||
|
case SIGPIPE: strcpy(ss,"SIGPIPE -"); break;
|
||||||
|
case SIGSEGV: strcpy(ss,"SIGSEGV -"); break;
|
||||||
|
case SIGTERM: strcpy(ss,"SIGTERM -"); break;
|
||||||
|
default: sprintf(ss,"SIG_%d -",sig); break;
|
||||||
|
}
|
||||||
|
switch (sig) {
|
||||||
|
default:
|
||||||
|
case SIGHUP :
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"%s Ignore .....\n", ss);
|
||||||
|
fflush(stderr);
|
||||||
|
signal(sig, can_exit);
|
||||||
|
return;
|
||||||
|
case 0:
|
||||||
|
case SIGINT :
|
||||||
|
case SIGPIPE:
|
||||||
|
case SIGQUIT:
|
||||||
|
case SIGFPE :
|
||||||
|
case SIGSEGV:
|
||||||
|
case SIGTERM:
|
||||||
|
if(can_fd>=0) close(can_fd);
|
||||||
|
can_prtime(stderr);
|
||||||
|
fprintf(stderr,"%s process stop!\n", ss);
|
||||||
|
fflush(stderr);
|
||||||
|
close(can_lk);
|
||||||
|
shmdt(can_shm_addr);
|
||||||
|
shmctl(can_shm_id, IPC_STAT, &buf);
|
||||||
|
if(buf.shm_nattch == 0)
|
||||||
|
shmctl(can_shm_id, IPC_RMID, NULL);
|
||||||
|
exit(sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *time2asc(double t){
|
||||||
|
static char stmp[10][20];
|
||||||
|
static int itmp=0;
|
||||||
|
char *lin = stmp[itmp];
|
||||||
|
int h, min;
|
||||||
|
double sec;
|
||||||
|
h = (int)(t/3600.);
|
||||||
|
min = (int)((t - (double)h*3600.)/60.);
|
||||||
|
sec = t - (double)h*3600. - (double)min*60.;
|
||||||
|
h %= 24;
|
||||||
|
sprintf(lin, "%02d:%02d:%09.6f", h,min,sec);
|
||||||
|
itmp = (itmp+1)%10;
|
||||||
|
return lin;
|
||||||
|
}
|
||||||
|
|
||||||
|
double can_dsleep(double dt) {
|
||||||
|
struct timespec ts,tsr;
|
||||||
|
ts.tv_sec = (time_t)dt;
|
||||||
|
ts.tv_nsec = (long)((dt-ts.tv_sec)*1e9);
|
||||||
|
nanosleep(&ts,&tsr);
|
||||||
|
return((double)ts.tv_sec + (double)ts.tv_nsec/1e9);
|
||||||
|
}
|
||||||
|
|
||||||
|
double can_dtime() {
|
||||||
|
struct timeval ct;
|
||||||
|
struct timezone tz;
|
||||||
|
gettimeofday(&ct, &tz);
|
||||||
|
return ((double)ct.tv_sec + (double)ct.tv_usec/1e6);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *can_atime() {return(time2asc(can_dtime()));}
|
||||||
|
|
||||||
|
void can_prtime(FILE *fd) {
|
||||||
|
static double otime=0.0;
|
||||||
|
double ntime=can_dtime();
|
||||||
|
time_t itime = (int)ntime;
|
||||||
|
if(otime==0.0) tzset();
|
||||||
|
ntime -= (double)timezone;
|
||||||
|
if((((int)ntime)%(24*3600) < ((int)otime)%(24*3600)) || otime==0.0)
|
||||||
|
fprintf(fd,"========================\n%s",ctime(&itime));
|
||||||
|
fprintf(fd,"%s ",time2asc(ntime));
|
||||||
|
otime=ntime;
|
||||||
|
}
|
||||||
45
Z1000_focus/can_io.h
Normal file
45
Z1000_focus/can_io.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// (c) vsher@sao.ru
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef CAN_IO_H__
|
||||||
|
#define CAN_IO_H__
|
||||||
|
|
||||||
|
#define _U_ __attribute__((__unused__))
|
||||||
|
|
||||||
|
|
||||||
|
#define CAN_CTLR_SIZE 1024 /* size of client process shared area */
|
||||||
|
#define CAN_RX_SIZE 1000 /* max. # frames in Rx-buffer */
|
||||||
|
#define CAN_RTR_FLAG 0x20000000 /* send frame as Remote Transmission Request */
|
||||||
|
#define CAN_EXT_FLAG 0x40000000 /* send frame with extended 29-bit ID, 11-bit otherwise */
|
||||||
|
|
||||||
|
int can_wait(int fd, double tout);
|
||||||
|
#define can_delay(Tout) can_wait(0, Tout)
|
||||||
|
void set_server_mode(int mode);
|
||||||
|
int can_server();
|
||||||
|
void set_sending_mode(int to_server);
|
||||||
|
int can_sending_mode();
|
||||||
|
int can_card();
|
||||||
|
int can_gate();
|
||||||
|
double can_gate_time_offset();
|
||||||
|
void setup_can_net(unsigned long ipaddr, int port, unsigned long acckey);
|
||||||
|
unsigned long get_acckey();
|
||||||
|
void *init_can_io();
|
||||||
|
void *start_can_io(void *arg);
|
||||||
|
void can_put_buff_frame(double rtime, int id, int length, unsigned char data[]);
|
||||||
|
int can_io_ok();
|
||||||
|
int can_io_shm_ok();
|
||||||
|
int can_ok();
|
||||||
|
void can_clean_recv(int *pbuf, double *rtime);
|
||||||
|
int can_get_buff_frame(int *pbuf, double *rtime,
|
||||||
|
int *id, int *length, unsigned char data[]);
|
||||||
|
int can_recv_frame(int *pbuf, double *rtime,
|
||||||
|
int *id, int *length, unsigned char data[]);
|
||||||
|
int can_send_frame(unsigned long id, int length, unsigned char data[]);
|
||||||
|
void can_exit(int sig);
|
||||||
|
char *time2asc(double t);
|
||||||
|
double can_dsleep(double dt);
|
||||||
|
double can_dtime();
|
||||||
|
char *can_atime();
|
||||||
|
void can_prtime(FILE *fd);
|
||||||
|
#endif // CAN_IO_H__
|
||||||
136
Z1000_focus/canmsg.h
Normal file
136
Z1000_focus/canmsg.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/* canmsg.h - common kernel-space and user-space CAN message structure
|
||||||
|
* Linux CAN-bus device driver.
|
||||||
|
* Written by Pavel Pisa - OCERA team member
|
||||||
|
* email:pisa@cmp.felk.cvut.cz
|
||||||
|
* This software is released under the GPL-License.
|
||||||
|
* Version lincan-0.3 17 Jun 2004
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CANMSG_T_H
|
||||||
|
#define _CANMSG_T_H
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#else /* __KERNEL__ */
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CAN_MSG_VERSION_2 enables new canmsg_t layout compatible with
|
||||||
|
* can4linux project from http://www.port.de/
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define CAN_MSG_VERSION_2
|
||||||
|
|
||||||
|
/* Number of data bytes in one CAN message */
|
||||||
|
#define CAN_MSG_LENGTH 8
|
||||||
|
|
||||||
|
#ifdef CAN_MSG_VERSION_2
|
||||||
|
|
||||||
|
typedef struct timeval canmsg_tstamp_t ;
|
||||||
|
|
||||||
|
typedef unsigned long canmsg_id_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct canmsg_t - structure representing CAN message
|
||||||
|
* @flags: message flags
|
||||||
|
* %MSG_RTR .. message is Remote Transmission Request,
|
||||||
|
* %MSG_EXT .. message with extended ID,
|
||||||
|
* %MSG_OVR .. indication of queue overflow condition,
|
||||||
|
* %MSG_LOCAL .. message originates from this node.
|
||||||
|
* @cob: communication object number (not used)
|
||||||
|
* @id: ID of CAN message
|
||||||
|
* @timestamp: not used
|
||||||
|
* @length: length of used data
|
||||||
|
* @data: data bytes buffer
|
||||||
|
*
|
||||||
|
* Header: canmsg.h
|
||||||
|
*/
|
||||||
|
struct canmsg_t {
|
||||||
|
int flags;
|
||||||
|
int cob;
|
||||||
|
canmsg_id_t id;
|
||||||
|
canmsg_tstamp_t timestamp;
|
||||||
|
unsigned short length;
|
||||||
|
unsigned char data[CAN_MSG_LENGTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
#else /*CAN_MSG_VERSION_2*/
|
||||||
|
#ifndef PACKED
|
||||||
|
#define PACKED __attribute__((packed))
|
||||||
|
#endif
|
||||||
|
/* Old, deprecated version of canmsg_t structure */
|
||||||
|
struct canmsg_t {
|
||||||
|
short flags;
|
||||||
|
int cob;
|
||||||
|
canmsg_id_t id;
|
||||||
|
unsigned long timestamp;
|
||||||
|
unsigned int length;
|
||||||
|
unsigned char data[CAN_MSG_LENGTH];
|
||||||
|
} PACKED;
|
||||||
|
#endif /*CAN_MSG_VERSION_2*/
|
||||||
|
|
||||||
|
typedef struct canmsg_t canmsg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct canfilt_t - structure for acceptance filter setup
|
||||||
|
* @flags: message flags
|
||||||
|
* %MSG_RTR .. message is Remote Transmission Request,
|
||||||
|
* %MSG_EXT .. message with extended ID,
|
||||||
|
* %MSG_OVR .. indication of queue overflow condition,
|
||||||
|
* %MSG_LOCAL .. message originates from this node.
|
||||||
|
* there are corresponding mask bits
|
||||||
|
* %MSG_RTR_MASK, %MSG_EXT_MASK, %MSG_LOCAL_MASK.
|
||||||
|
* %MSG_PROCESSLOCAL enables local messages processing in the
|
||||||
|
* combination with global setting
|
||||||
|
* @queid: CAN queue identification in the case of the multiple
|
||||||
|
* queues per one user (open instance)
|
||||||
|
* @cob: communication object number (not used)
|
||||||
|
* @id: selected required value of cared ID id bits
|
||||||
|
* @mask: select bits significand for the comparation;
|
||||||
|
* 1 .. take care about corresponding ID bit, 0 .. don't care
|
||||||
|
*
|
||||||
|
* Header: canmsg.h
|
||||||
|
*/
|
||||||
|
struct canfilt_t {
|
||||||
|
int flags;
|
||||||
|
int queid;
|
||||||
|
int cob;
|
||||||
|
canmsg_id_t id;
|
||||||
|
canmsg_id_t mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct canfilt_t canfilt_t;
|
||||||
|
|
||||||
|
/* Definitions to use for canmsg_t and canfilt_t flags */
|
||||||
|
#define MSG_RTR (1<<0)
|
||||||
|
#define MSG_OVR (1<<1)
|
||||||
|
#define MSG_EXT (1<<2)
|
||||||
|
#define MSG_LOCAL (1<<3)
|
||||||
|
/* If you change above lines, check canque_filtid2internal function */
|
||||||
|
|
||||||
|
/* Additional definitions used for canfilt_t only */
|
||||||
|
#define MSG_FILT_MASK_SHIFT 8
|
||||||
|
#define MSG_RTR_MASK (MSG_RTR<<MSG_FILT_MASK_SHIFT)
|
||||||
|
#define MSG_EXT_MASK (MSG_EXT<<MSG_FILT_MASK_SHIFT)
|
||||||
|
#define MSG_LOCAL_MASK (MSG_LOCAL<<MSG_FILT_MASK_SHIFT)
|
||||||
|
#define MSG_PROCESSLOCAL (MSG_OVR<<MSG_FILT_MASK_SHIFT)
|
||||||
|
|
||||||
|
/* Can message ID mask */
|
||||||
|
#define MSG_ID_MASK ((1l<<29)-1)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*_CANMSG_T_H*/
|
||||||
314
Z1000_focus/canopen.c
Normal file
314
Z1000_focus/canopen.c
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
// (c) vsher@sao.ru
|
||||||
|
#include "canopen.h"
|
||||||
|
#include "sdo_abort_codes.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int rxpnt=-1;
|
||||||
|
static double rxtime=0.;
|
||||||
|
|
||||||
|
int sendNMT(int node, int icode){
|
||||||
|
unsigned long idt=0;
|
||||||
|
int dlen=2;
|
||||||
|
unsigned char tdata[2] = {0};
|
||||||
|
tdata[0] = icode&0xff;
|
||||||
|
tdata[1] = node&0x7f;
|
||||||
|
return (can_send_frame(idt, dlen, tdata) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int resetNode2(int oldnode, int newnode){
|
||||||
|
int idr,dlen,ntout=50;
|
||||||
|
unsigned char rdata[8];
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
if(!sendNMT(oldnode, 0x81)) return 0;
|
||||||
|
for(int i=0; i < ntout; ++i){
|
||||||
|
can_dsleep(0.01);
|
||||||
|
while(can_recv_frame(&rxpnt, &rxtime, &idr, &dlen, rdata)){
|
||||||
|
if(idr == (0x700|newnode) && dlen==1 && rdata[0] == 0) return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resetNode(int node){return resetNode2(node,node);}
|
||||||
|
|
||||||
|
int getNodeState(int node){
|
||||||
|
/* use Node Guarding Protocol */
|
||||||
|
unsigned long idt = (0x700 | (node&0x7f) | CAN_RTR_FLAG);
|
||||||
|
int idr = 0, dlen = 0, ntout = 15;
|
||||||
|
unsigned char rdata[8];
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
if(can_send_frame(idt, dlen, rdata)<=0) return 0;
|
||||||
|
for(int i=0; i < ntout; ++i){
|
||||||
|
can_dsleep(0.01);
|
||||||
|
while(can_recv_frame(&rxpnt, &rxtime, &idr, &dlen, rdata)) {
|
||||||
|
if(idr == (0x700|node) && dlen == 1) return rdata[0]&0x7f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int initNode(int node){
|
||||||
|
int state;
|
||||||
|
if(rxpnt<0){
|
||||||
|
init_can_io();
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
}
|
||||||
|
if(!can_ok()) return 0;
|
||||||
|
if((state = getNodeState(node)) == 0) return 0;
|
||||||
|
if(state != NodePreOperational) setPreOper(node);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sendSDOdata(int node, int func, int object, int subindex, unsigned char data[]){
|
||||||
|
unsigned long idt = 0x600 | (node&0x7f);
|
||||||
|
int dlen = 8;
|
||||||
|
unsigned char tdata[8] = {0};
|
||||||
|
func &= 0x7f;
|
||||||
|
tdata[0] = func;
|
||||||
|
tdata[1] = object&0xff;
|
||||||
|
tdata[2] = (object&0xff00)>>8;
|
||||||
|
tdata[3] = subindex&0xff;
|
||||||
|
switch(func){
|
||||||
|
case 0x22:
|
||||||
|
case 0x23:
|
||||||
|
case 0x43: tdata[7]=data[3];
|
||||||
|
// FALLTHRU
|
||||||
|
case 0x27:
|
||||||
|
case 0x47: tdata[6]=data[2];
|
||||||
|
// FALLTHRU
|
||||||
|
case 0x2b:
|
||||||
|
case 0x4b: tdata[5]=data[1];
|
||||||
|
// FALLTHRU
|
||||||
|
case 0x2f:
|
||||||
|
case 0x4f: tdata[4]=data[0];
|
||||||
|
// FALLTHRU
|
||||||
|
case 0x40: break;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
return (can_send_frame(idt, dlen, tdata) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sendSDOreq(int node, int object, int subindex){
|
||||||
|
unsigned char dummy[1];
|
||||||
|
return sendSDOdata(node, 0x40, object, subindex, dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
int recvSDOresp(int node, int t_func, int t_object, int t_subindex, unsigned char data[]){
|
||||||
|
int idt = 0x580|(node&0x7f);
|
||||||
|
int idr = 0, dlen = 0;
|
||||||
|
unsigned char rdata[8] = {0};
|
||||||
|
int ntout = (t_object == 0x1010||t_object == 0x1011)? 50 : 15;
|
||||||
|
for(int i = 0; i < ntout; ++i){
|
||||||
|
can_dsleep(0.01);
|
||||||
|
while(can_recv_frame(&rxpnt, &rxtime, &idr, &dlen, rdata)) if(idr==idt){
|
||||||
|
int r_func, r_object, r_subindex;
|
||||||
|
if(dlen < 4){
|
||||||
|
fprintf(stderr,"Too short SDO response from Node%d\n",node&0x7f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
r_func = rdata[0];
|
||||||
|
r_object = (rdata[2]<<8)|rdata[1];
|
||||||
|
r_subindex = rdata[3];
|
||||||
|
if(r_func == 0x80){ // got SDO error code
|
||||||
|
unsigned long ercode = (rdata[7]<<24)|(rdata[6]<<16)|(rdata[5]<<8)|rdata[4];
|
||||||
|
fprintf(stderr,"SDO error %08lx from Node%d (object %04x/%d) \n",ercode,node&0x7f,r_object,r_subindex);
|
||||||
|
fprintf(stderr,"(%s)\n",sdo_abort_text(ercode));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(r_object!=t_object || r_subindex != t_subindex){
|
||||||
|
fprintf(stderr,"Got SDO response with a stranger object (%04x/%d instead of %04x/%d) from Node%d\n",r_object,r_subindex,t_object,t_subindex,node&0x7f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if((t_func&0xf0) == 0x20 && r_func == 0x60) return 1;
|
||||||
|
if(t_func == 0x40 && (r_func&0xf0) == 0x40){
|
||||||
|
dlen = 0;
|
||||||
|
switch (r_func & 0x7f){
|
||||||
|
default:
|
||||||
|
case 0x43: data[3] = rdata[7]; dlen++;
|
||||||
|
// FALLTHRU
|
||||||
|
case 0x47: data[2] = rdata[6]; dlen++;
|
||||||
|
// FALLTHRU
|
||||||
|
case 0x4b: data[1] = rdata[5]; dlen++;
|
||||||
|
// FALLTHRU
|
||||||
|
case 0x4f: data[0] = rdata[4]; dlen++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return dlen;
|
||||||
|
}
|
||||||
|
fprintf(stderr,"Suspicious SDO response from Node%d (func %02x object %04x/%d)\n",node&0x7f,r_func,r_object,r_subindex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr,"Can't get SDO response from Node%d! Timeout?\n",node&0x7f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int doSDOdownload(int node, int object, int subindex, unsigned char data[], int dlen){
|
||||||
|
int func = 0x22;
|
||||||
|
switch(dlen){
|
||||||
|
default:func = 0x22; break;
|
||||||
|
case 4: func = 0x23; break;
|
||||||
|
case 3: func = 0x27; break;
|
||||||
|
case 2: func = 0x2b; break;
|
||||||
|
case 1: func = 0x2f; break;
|
||||||
|
}
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
if(!sendSDOdata(node, func, object, subindex, data)) return 0;
|
||||||
|
return recvSDOresp(node, func, object, subindex, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int doSDOupload(int node, int object, int subindex, unsigned char data[]){
|
||||||
|
int func = 0x40;
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
if(!sendSDOdata(node, func, object, subindex, data)) return 0;
|
||||||
|
return recvSDOresp(node, func, object, subindex, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int setLong(int node, int object, int subindex, unsigned long value){
|
||||||
|
unsigned char data[4] = {0};
|
||||||
|
data[0] = value&0xff;
|
||||||
|
data[1] = (value>>8)&0xff;
|
||||||
|
data[2] = (value>>16)&0xff;
|
||||||
|
data[3] = (value>>24)&0xff;
|
||||||
|
return doSDOdownload(node, object, subindex, data, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int setShort(int node, int object, int subindex, unsigned short value){
|
||||||
|
unsigned char data[4] = {0};
|
||||||
|
data[0] = value&0xff;
|
||||||
|
data[1] = (value>>8)&0xff;
|
||||||
|
return doSDOdownload(node, object, subindex, data, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int setByte(int node, int object, int subindex, unsigned char value){
|
||||||
|
unsigned char data[4] = {0};
|
||||||
|
data[0] = value;
|
||||||
|
return doSDOdownload(node, object, subindex, data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int saveObjects(int node){
|
||||||
|
unsigned char data[4] = {'s','a','v','e'};
|
||||||
|
return doSDOdownload(node, 0x1010, 1, data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char sdata[5] = {'\0'};
|
||||||
|
char *getString(int node, int object, int subindex){
|
||||||
|
int dlen = doSDOupload(node, object, subindex, sdata);
|
||||||
|
if(dlen == 0) return NULL;
|
||||||
|
sdata[4] = '\0';
|
||||||
|
return (char*)sdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLong(int node, int object, int subindex, unsigned long *value){
|
||||||
|
unsigned char data[4] = {0};
|
||||||
|
int dlen = doSDOupload(node, object, subindex, data);
|
||||||
|
if(dlen == 0) return 0;
|
||||||
|
if(dlen != 4)
|
||||||
|
fprintf(stderr,"Warning! Got only %d bytes for Long value from Node%d/%04x/%d\n",dlen,node,object,subindex);
|
||||||
|
*value = (data[3]<<24)|(data[2]<<16)|(data[1]<<8)|data[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getShort(int node, int object, int subindex, unsigned short *value){
|
||||||
|
unsigned char data[4] = {0};
|
||||||
|
int dlen = doSDOupload(node, object, subindex, data);
|
||||||
|
if(dlen == 0) return 0;
|
||||||
|
if(dlen != 2)
|
||||||
|
fprintf(stderr,"Warning! Got %d bytes for Short value from Node%d/%04x/%d\n",dlen,node,object,subindex);
|
||||||
|
*value = (data[1]<<8)|data[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getByte(int node, int object, int subindex, unsigned char *value){
|
||||||
|
unsigned char data[4] = {0};
|
||||||
|
int dlen = doSDOupload(node, object, subindex, data);
|
||||||
|
if(dlen == 0) return 0;
|
||||||
|
if(dlen != 1)
|
||||||
|
fprintf(stderr,"Warning! Got %d bytes for Byte value from Node%d/%04x/%d\n",dlen,node,object,subindex);
|
||||||
|
*value = data[0];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sendSync(){
|
||||||
|
// send broadcasting SYNC telegram
|
||||||
|
unsigned long idt=0x80;
|
||||||
|
int dlen=0;
|
||||||
|
unsigned char tdata[1] = {0};
|
||||||
|
return (can_send_frame(idt, dlen, tdata) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int recvNextPDO(double tout, int *node, unsigned long *value){
|
||||||
|
// wait up to 'tout' sec. for the one next PDO
|
||||||
|
// if ok - return 1 for PDO1 or 2 for PDO2; else 0 if timeout
|
||||||
|
double te = can_dtime()+tout;
|
||||||
|
int idr=0, dlen=0, pdon = 0;
|
||||||
|
unsigned char rdata[8] = {0};
|
||||||
|
do{
|
||||||
|
while(can_recv_frame(&rxpnt, &rxtime, &idr, &dlen, rdata)){
|
||||||
|
if(idr&CAN_RTR_FLAG) continue;
|
||||||
|
if((idr&0xf80) == 0x180) pdon=1;
|
||||||
|
else if((idr&0xf80) == 0x280) pdon=2;
|
||||||
|
if(pdon){
|
||||||
|
*node = idr&0x7f;
|
||||||
|
*value = (rdata[3]<<24)|(rdata[2]<<16)|(rdata[1]<<8)|rdata[0];
|
||||||
|
return pdon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
can_dsleep(0.02);
|
||||||
|
} while(can_dtime() < te);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int recvPDOs(double tout, int maxpdo, int node[], int pdo_n[], unsigned long value[]){
|
||||||
|
// wait up to 'tout' sec. for the array of 'maxpdo' PDOs
|
||||||
|
// if ok - return number of PDO really received; else 0 if timeout
|
||||||
|
double te = can_dtime()+tout;
|
||||||
|
int npdo=0, idr=0, dlen=0, pdon = 0;
|
||||||
|
unsigned char rdata[8] = {0};
|
||||||
|
do{
|
||||||
|
while(can_recv_frame(&rxpnt, &rxtime, &idr, &dlen, rdata)){
|
||||||
|
if(idr&CAN_RTR_FLAG) continue;
|
||||||
|
if((idr&0xf80) == 0x180) pdon=1;
|
||||||
|
else if((idr&0xf80) == 0x280) pdon=2;
|
||||||
|
else pdon = 0;
|
||||||
|
if(pdon){
|
||||||
|
node[npdo] = idr&0x7f;
|
||||||
|
pdo_n[npdo] = pdon;
|
||||||
|
switch(dlen){
|
||||||
|
case 1:
|
||||||
|
value[npdo] = rdata[0];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value[npdo] = (rdata[1]<<8)|rdata[0];
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
value[npdo] = (rdata[2]<<16)|(rdata[1]<<8)|rdata[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value[npdo] = (rdata[3]<<24)|(rdata[2]<<16)|(rdata[1]<<8)|rdata[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
npdo++;
|
||||||
|
if(npdo>=maxpdo) return npdo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
can_dsleep(0.02);
|
||||||
|
} while(can_dtime() < te && npdo < maxpdo);
|
||||||
|
return npdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
int requestPDO(double tout, int node, int pdon, unsigned long *value){
|
||||||
|
// send request for PDO and wait up to 'tout' sec. while the node respond to
|
||||||
|
// if ok - return 1; else 0
|
||||||
|
unsigned long idt = (((pdon==1)?0x180:0x280)|(node&0x7f)|CAN_RTR_FLAG);
|
||||||
|
int dlen=0, rnode;
|
||||||
|
unsigned char dummy[1];
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
if(can_send_frame(idt, dlen, dummy) <= 0) return 0;
|
||||||
|
can_dsleep(0.01);
|
||||||
|
if(recvNextPDO(tout, &rnode, value) == pdon) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clean_recv(){
|
||||||
|
can_clean_recv(&rxpnt, &rxtime);
|
||||||
|
}
|
||||||
47
Z1000_focus/canopen.h
Normal file
47
Z1000_focus/canopen.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// (c) vsher@sao.ru
|
||||||
|
#pragma once
|
||||||
|
#ifndef CANOPEN_H__
|
||||||
|
#define CANOPEN_H__
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "can_io.h"
|
||||||
|
|
||||||
|
#define startNode(Node) sendNMT(Node,1) // put node in "Operational" mode
|
||||||
|
#define stopNode(Node) sendNMT(Node,2) // put node in "Stop" mode
|
||||||
|
#define setPreOper(Node) sendNMT(Node,0x80) // return node in "Pre-Operational" mode
|
||||||
|
|
||||||
|
#define NodeStopped 4
|
||||||
|
#define NodeOperational 5
|
||||||
|
#define NodePreOperational 0x7f
|
||||||
|
|
||||||
|
void clean_recv();
|
||||||
|
int initNode(int node);
|
||||||
|
int sendNMT(int node, int icode);
|
||||||
|
int resetNode2(int oldnode, int newnode);
|
||||||
|
int resetNode(int node);
|
||||||
|
int getNodeState(int node);
|
||||||
|
int initNode(int node);
|
||||||
|
int sendSDOdata(int node, int func, int object, int subindex, unsigned char data[]);
|
||||||
|
int sendSDOreq(int node, int object, int subindex);
|
||||||
|
int recvSDOresp(int node, int t_func, int t_object, int t_subindex, unsigned char data[]);
|
||||||
|
int doSDOdownload(int node, int object, int subindex, unsigned char data[], int dlen);
|
||||||
|
int doSDOupload(int node, int object, int subindex, unsigned char data[]);
|
||||||
|
int setLong(int node, int object, int subindex, unsigned long value);
|
||||||
|
int setShort(int node, int object, int subindex, unsigned short value);
|
||||||
|
int setByte(int node, int object, int subindex, unsigned char value);
|
||||||
|
int saveObjects(int node);
|
||||||
|
char *getString(int node, int object, int subindex);
|
||||||
|
int getLong(int node, int object, int subindex, unsigned long *value);
|
||||||
|
int getShort(int node, int object, int subindex, unsigned short *value);
|
||||||
|
int getByte(int node, int object, int subindex, unsigned char *value);
|
||||||
|
int sendSync();
|
||||||
|
int recvNextPDO(double tout, int *node, unsigned long *value);
|
||||||
|
int recvPDOs(double tout, int maxpdo, int node[], int pdo_n[], unsigned long value[]);
|
||||||
|
int requestPDO(double tout, int node, int pdon, unsigned long *value);
|
||||||
|
|
||||||
|
#endif // CANOPEN_H__
|
||||||
86
Z1000_focus/cmdlnopts.c
Normal file
86
Z1000_focus/cmdlnopts.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* cmdlnopts.c - the only function that parse cmdln args and returns glob parameters
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here are global parameters initialisation
|
||||||
|
*/
|
||||||
|
int help;
|
||||||
|
glob_pars G;
|
||||||
|
|
||||||
|
// DEFAULTS
|
||||||
|
// default global parameters
|
||||||
|
glob_pars const Gdefault = {
|
||||||
|
.nodenum = 3,
|
||||||
|
.motorID = 12,
|
||||||
|
.gotopos = NAN,
|
||||||
|
.port = DEFPORT
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define command line options by filling structure:
|
||||||
|
* name has_arg flag val type argptr help
|
||||||
|
*/
|
||||||
|
myoption cmdlnopts[] = {
|
||||||
|
// set 1 to param despite of its repeating number:
|
||||||
|
{"help", NO_ARGS, NULL, 'h', arg_none, APTR(&help), "show this help"},
|
||||||
|
{"node", NEED_ARG, NULL, 'n', arg_int, APTR(&G.nodenum), "encoder node number"},
|
||||||
|
{"reset", NO_ARGS, NULL, 'r', arg_none, APTR(&G.reset), "reset encoder"},
|
||||||
|
{"verbose", NO_ARGS, NULL, 'v', arg_int, APTR(&G.verbose), "show more info"},
|
||||||
|
{"motorid", NEED_ARG, NULL, 'i', arg_int, APTR(&G.motorID), "motor controller address"},
|
||||||
|
//{"bcastid", NEED_ARG, NULL, 'b', arg_int, APTR(&G.motorID), "motor controller broadcast address"},
|
||||||
|
{"gotopos", NEED_ARG, NULL, 'g', arg_double, APTR(&G.gotopos), "target focus position"},
|
||||||
|
{"targspeed",NEED_ARG, NULL, 't', arg_double, APTR(&G.targspeed), "move motor with constant speed (rev/min)"},
|
||||||
|
{"stop", NO_ARGS, NULL, 's', arg_none, APTR(&G.stop), "stop motor"},
|
||||||
|
{"monitor", NEED_ARG, NULL, 'm', arg_double, APTR(&G.monitspd), "move a little with given speed with monitoring"},
|
||||||
|
{"eswstate",NO_ARGS, NULL, 'e', arg_none, APTR(&G.showesw), "show end-switches state"},
|
||||||
|
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logname), "logfile name and path"},
|
||||||
|
{"server", NO_ARGS, NULL, 'S', arg_none, APTR(&G.server), "work as server"},
|
||||||
|
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), "server port number"},
|
||||||
|
{"host", NEED_ARG, NULL, 'H', arg_string, APTR(&G.host), "host to connect (default: localhost)"},
|
||||||
|
{"standalone",NO_ARGS, NULL, 'A', arg_none, APTR(&G.standalone),"run as standalone application"},
|
||||||
|
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){
|
||||||
|
void *ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||||
|
// format of help: "Usage: progname [args]\n"
|
||||||
|
change_helpstring("Usage: %s [args]\n\n\tWhere args are:\n");
|
||||||
|
// parse arguments
|
||||||
|
parseargs(&argc, &argv, cmdlnopts);
|
||||||
|
if(help) showhelp(-1, cmdlnopts);
|
||||||
|
return &G;
|
||||||
|
}
|
||||||
|
|
||||||
50
Z1000_focus/cmdlnopts.h
Normal file
50
Z1000_focus/cmdlnopts.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* cmdlnopts.h - comand line options for parceargs
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __CMDLNOPTS_H__
|
||||||
|
#define __CMDLNOPTS_H__
|
||||||
|
|
||||||
|
#include "parseargs.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int nodenum; // encoder's node number
|
||||||
|
int reset; // reset encoder
|
||||||
|
int verbose; // more messages
|
||||||
|
int motorID; // motor address (from controller's settings)
|
||||||
|
int bcastID; // broadcast motor address
|
||||||
|
double gotopos; // move focus to given position
|
||||||
|
double targspeed; // just rotate motor with given speed
|
||||||
|
int stop; // stop motor
|
||||||
|
double monitspd; // start speed monitoring (for dynamics)
|
||||||
|
int showesw; // show end-switches state
|
||||||
|
char *logname; // logfile name & path
|
||||||
|
int server; // work as server
|
||||||
|
char *port; // port number for server or client
|
||||||
|
char *host; // host to connect (in client mode)
|
||||||
|
int standalone; // run standalone
|
||||||
|
} glob_pars;
|
||||||
|
|
||||||
|
|
||||||
|
glob_pars *parse_args(int argc, char **argv);
|
||||||
|
#endif // __CMDLNOPTS_H__
|
||||||
|
|
||||||
191
Z1000_focus/main.c
Normal file
191
Z1000_focus/main.c
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Zphocus project.
|
||||||
|
* Copyright 2019 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 <math.h> // fabs
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include "can_encoder.h"
|
||||||
|
#include "canopen.h"
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
#include "HW_dependent.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
|
||||||
|
static glob_pars *G;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief verbose - printf when parameter `verbose` set
|
||||||
|
* @param fmt - format & other attrs
|
||||||
|
* @return amount of printed characters
|
||||||
|
*/
|
||||||
|
int verbose(const char *fmt, ...){
|
||||||
|
if(!G || !G->verbose) return 0;
|
||||||
|
va_list ar; int i;
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief signals - signal handler (also called by functions ERR/ERRX)
|
||||||
|
* @param signo - signal number
|
||||||
|
*/
|
||||||
|
void signals(int signo){
|
||||||
|
WARNX("Received signal %d", signo);
|
||||||
|
exit(signo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmdparser(){
|
||||||
|
#define BUFL 128
|
||||||
|
char buf[BUFL] = {0,};
|
||||||
|
if(G->stop) sprintf(buf, S_CMD_STOP);
|
||||||
|
else if(fabs(G->targspeed) > DBL_EPSILON) snprintf(buf, BUFL, S_CMD_TARGSPEED "=%g", G->targspeed);
|
||||||
|
else if(!isnan(G->gotopos)) snprintf(buf, BUFL, S_CMD_GOTO "=%g", G->gotopos);
|
||||||
|
else sprintf(buf, S_CMD_FOCUS);
|
||||||
|
sock_send_data(G->host, G->port, buf);
|
||||||
|
#undef BUFL
|
||||||
|
}
|
||||||
|
|
||||||
|
//extern char can_dev[40];
|
||||||
|
|
||||||
|
int main (int argc, char *argv[]){
|
||||||
|
int ret = 0;
|
||||||
|
double curposition = 0;
|
||||||
|
initial_setup();
|
||||||
|
G = parse_args(argc, argv);
|
||||||
|
|
||||||
|
if(fabs(G->targspeed) > DBL_EPSILON && !isnan(G->gotopos))
|
||||||
|
ERRX("Arguments \"target speed\" and \"target position\" can't meet together!");
|
||||||
|
if(fabs(G->targspeed) > DBL_EPSILON){
|
||||||
|
if(fabs(G->targspeed) < MINSPEED || fabs(G->targspeed) > MAXSPEED){
|
||||||
|
WARNX("Target speed should be be from %d to %d (rev/min)", MINSPEED, MAXSPEED);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!isnan(G->gotopos)){
|
||||||
|
if(G->gotopos > FOCMAX_MM || G->gotopos < FOCMIN_MM){
|
||||||
|
WARNX("Focal distance may be from %g to %g mm", FOCMIN_MM, FOCMAX_MM);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGTERM, signals);
|
||||||
|
signal(SIGTSTP, SIG_IGN);
|
||||||
|
signal(SIGHUP, SIG_IGN);
|
||||||
|
//can_dev[8] = '1';
|
||||||
|
|
||||||
|
if(G->server || G->standalone){ // init hardware
|
||||||
|
if(G->logname){
|
||||||
|
openlogfile(G->logname);
|
||||||
|
}
|
||||||
|
if(init_encoder(G->nodenum, G->reset)) ERRX("Encoder not found");
|
||||||
|
|
||||||
|
if(getPos(&curposition)){
|
||||||
|
WARNX("Can't read current position");
|
||||||
|
ret = 1;
|
||||||
|
goto Oldcond;
|
||||||
|
}else verbose("Position @ start: %.2fmm\n", curposition);
|
||||||
|
|
||||||
|
if(init_motor_ids(G->motorID)){
|
||||||
|
ret = 1;
|
||||||
|
goto Oldcond;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(G->server){ // daemonize & run server
|
||||||
|
#if !defined EBUG
|
||||||
|
if(daemon(1, 0)){
|
||||||
|
ERR("daemon()");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while(1){ // guard for dead processes
|
||||||
|
pid_t childpid = fork();
|
||||||
|
if(childpid){
|
||||||
|
putlog("create child with PID %d\n", childpid);
|
||||||
|
DBG("Created child with PID %d\n", childpid);
|
||||||
|
wait(NULL);
|
||||||
|
putlog("child %d died\n", childpid);
|
||||||
|
WARNX("Child %d died\n", childpid);
|
||||||
|
sleep(1);
|
||||||
|
}else{
|
||||||
|
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
|
||||||
|
daemonize(G->port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if(!G->standalone){
|
||||||
|
cmdparser();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fabs(G->monitspd) > DBL_EPSILON){
|
||||||
|
movewithmon(G->monitspd);
|
||||||
|
goto Oldcond;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(G->stop){ // Stop motor
|
||||||
|
if(stop()) ret = 1;
|
||||||
|
goto Oldcond;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fabs(G->targspeed) > DBL_EPSILON){ // move with constant speed
|
||||||
|
verbose("Try to move with %g revolutions per minute\n", G->targspeed);
|
||||||
|
if(movewconstspeed(G->targspeed)){
|
||||||
|
ret = 1;
|
||||||
|
goto Oldcond;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isnan(G->gotopos)){ // move to given position
|
||||||
|
verbose("Try to move to position %g\n", G->gotopos);
|
||||||
|
ret = move2pos(G->gotopos);
|
||||||
|
goto Oldcond;
|
||||||
|
}
|
||||||
|
|
||||||
|
Oldcond:
|
||||||
|
if(getPos(&curposition)) WARNX("Can't read current position");
|
||||||
|
else{
|
||||||
|
if(G->verbose) printf("pos=%.2fmm, ", curposition);
|
||||||
|
else printf("%.2f\n", curposition);
|
||||||
|
}
|
||||||
|
if(G->showesw){
|
||||||
|
eswstate e;
|
||||||
|
if(CAN_NOERR != get_endswitches(&e)) WARNX("Can't read end-switches state");
|
||||||
|
else switch(e){
|
||||||
|
case ESW_INACTIVE:
|
||||||
|
green("End-switches inactive\n");
|
||||||
|
break;
|
||||||
|
case ESW_CW_ACTIVE:
|
||||||
|
red("Active CW end-switch\n");
|
||||||
|
break;
|
||||||
|
case ESW_CCW_ACTIVE:
|
||||||
|
red("Active CCW end-switch\n");
|
||||||
|
break;
|
||||||
|
case ESW_BOTH_ACTIVE:
|
||||||
|
default:
|
||||||
|
red("ERROR: both end-switches active\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double spd;
|
||||||
|
if(get_motor_speed(&spd) == CAN_NOERR) verbose("speed=%d\n", spd);
|
||||||
|
else WARNX("Can't read speed");
|
||||||
|
|
||||||
|
returnPreOper();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
98
Z1000_focus/motor_cancodes.h
Normal file
98
Z1000_focus/motor_cancodes.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Zphocus project.
|
||||||
|
* Copyright 2019 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 MOTOR_CANCODES_H__
|
||||||
|
#define MOTOR_CANCODES_H__
|
||||||
|
|
||||||
|
// functions PO/PI/etc
|
||||||
|
#define PO_FNO 3
|
||||||
|
#define PI_FNO 4
|
||||||
|
#define SYNC_FNO 5
|
||||||
|
#define GR_FNO 6
|
||||||
|
#define PARAMDATA 512
|
||||||
|
|
||||||
|
// calculation of ID from address
|
||||||
|
#define MOTOR_PO_ID(addr) ((addr<<3) + PO_FNO)
|
||||||
|
#define MOTOR_PAR_ID(addr) (PARAMDATA + (addr<<3) + PO_FNO)
|
||||||
|
//#define MOTOR_BCAST_PO_ID(addr) ((addr<<3) + GR_FNO)
|
||||||
|
|
||||||
|
// zero's (command) byte of parameter request
|
||||||
|
// read & write parameters
|
||||||
|
#define CAN_READPAR_CMD 0x31
|
||||||
|
#define CAN_WRITEPAR_CMD 0x32
|
||||||
|
// error flag in answer
|
||||||
|
#define CAN_PAR_ERRFLAG 0x80
|
||||||
|
|
||||||
|
// control word bits & bit combination
|
||||||
|
#define CW_RAPIDSTOP 0
|
||||||
|
#define CW_INHIBIT 1
|
||||||
|
#define CW_STOP 2
|
||||||
|
#define CW_ENABLE 6
|
||||||
|
#define CW_B_BLOCK (1<<0)
|
||||||
|
#define CW_B_ENRAPID (1<<1)
|
||||||
|
#define CW_B_ENSTOP (1<<2)
|
||||||
|
#define CW_B_TEMPO (1<<4)
|
||||||
|
#define CW_B_PARSET (1<<5)
|
||||||
|
#define CW_B_CLERR (1<<6)
|
||||||
|
|
||||||
|
// status word
|
||||||
|
#define SW_B_UNBLOCK (1<<0)
|
||||||
|
#define SW_B_READY (1<<1)
|
||||||
|
#define SW_B_POUNBLOCK (1<<2)
|
||||||
|
#define SW_B_TEMPO21 (1<<3)
|
||||||
|
#define SW_B_PARAM21 (1<<4)
|
||||||
|
#define SW_B_MAILFUN (1<<5)
|
||||||
|
// artifical status bit (instead of reserved) for error getting speed
|
||||||
|
#define SW_B_CANTGETSPD (1<<6)
|
||||||
|
#define SW_ENABLE 4
|
||||||
|
#define SW_NOTENABLE 2
|
||||||
|
#define SW_INHIBIT 1
|
||||||
|
|
||||||
|
// state codes (when SW_B_MAILFUN==0)
|
||||||
|
#define STATE_NOTREADY 0
|
||||||
|
#define STATE_BLOCK 1
|
||||||
|
#define STATE_NOPERMIT 2
|
||||||
|
#define STATE_DETENT 3
|
||||||
|
#define STATE_PERMIS 4
|
||||||
|
#define STATE_REGUL 5
|
||||||
|
#define STATE_FACTORYST 8
|
||||||
|
#define STATE_CAPTURE 13
|
||||||
|
#define STATE_W4DATA 16
|
||||||
|
#define STATE_SAFESTOP 17
|
||||||
|
|
||||||
|
// Some parameters: indexes & subindexes
|
||||||
|
// Digital inputs
|
||||||
|
#define PAR_DI_SUBIDX 0
|
||||||
|
// inputs state (lowest bit is DI00)
|
||||||
|
#define PAR_DIST_IDX 8334
|
||||||
|
// Speed & current
|
||||||
|
#define PAR_SPD_SUBIDX 0
|
||||||
|
#define PAR_CRNT_SUBIDX 0
|
||||||
|
#define PAR_SPD_IDX 8318
|
||||||
|
#define PAR_CRNT_IDX 8326
|
||||||
|
// inputs role
|
||||||
|
#define PAR_DI00_IDX 8844
|
||||||
|
#define PAR_DI02_IDX 8336
|
||||||
|
#define PAR_DI03_IDX 8337
|
||||||
|
#define PAR_DI04_IDX 8338
|
||||||
|
#define PAR_DI05_IDX 8339
|
||||||
|
// roles:
|
||||||
|
#define DI_NOFUNC 0
|
||||||
|
#define DI_ENSTOP 1
|
||||||
|
|
||||||
|
#endif // MOTOR_CANCODES_H__
|
||||||
497
Z1000_focus/parseargs.c
Normal file
497
Z1000_focus/parseargs.c
Normal file
@ -0,0 +1,497 @@
|
|||||||
|
/* geany_encoding=koi8-r
|
||||||
|
* parseargs.c - parsing command line arguments & print help
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h> // printf
|
||||||
|
#include <getopt.h> // getopt_long
|
||||||
|
#include <stdlib.h> // calloc, exit, strtoll
|
||||||
|
#include <assert.h> // assert
|
||||||
|
#include <string.h> // strdup, strchr, strlen
|
||||||
|
#include <strings.h>// strcasecmp
|
||||||
|
#include <limits.h> // INT_MAX & so on
|
||||||
|
#include <libintl.h>// gettext
|
||||||
|
#include <ctype.h> // isalpha
|
||||||
|
#include "parseargs.h"
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
|
||||||
|
char *helpstring = "%s\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change standard help header
|
||||||
|
* MAY consist ONE "%s" for progname
|
||||||
|
* @param str (i) - new format
|
||||||
|
*/
|
||||||
|
void change_helpstring(char *s){
|
||||||
|
int pcount = 0, scount = 0;
|
||||||
|
char *str = s;
|
||||||
|
// check `helpstring` and set it to default in case of error
|
||||||
|
for(; pcount < 2; str += 2){
|
||||||
|
if(!(str = strchr(str, '%'))) break;
|
||||||
|
if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%"
|
||||||
|
else{
|
||||||
|
str += 2; // pass next '%'
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(str[1] == 's') scount++; // increment "%s" counter
|
||||||
|
};
|
||||||
|
if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong
|
||||||
|
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
|
||||||
|
ERRX(_("Wrong helpstring!"));
|
||||||
|
}
|
||||||
|
helpstring = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carefull atoll/atoi
|
||||||
|
* @param num (o) - returning value (or NULL if you wish only check number) - allocated by user
|
||||||
|
* @param str (i) - string with number must not be NULL
|
||||||
|
* @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more)
|
||||||
|
* @return TRUE if conversion sone without errors, FALSE otherwise
|
||||||
|
*/
|
||||||
|
static bool myatoll(void *num, char *str, argtype t){
|
||||||
|
long long tmp, *llptr;
|
||||||
|
int *iptr;
|
||||||
|
char *endptr;
|
||||||
|
assert(str);
|
||||||
|
assert(num);
|
||||||
|
tmp = strtoll(str, &endptr, 0);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||||
|
return FALSE;
|
||||||
|
switch(t){
|
||||||
|
case arg_longlong:
|
||||||
|
llptr = (long long*) num;
|
||||||
|
*llptr = tmp;
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
default:
|
||||||
|
if(tmp < INT_MIN || tmp > INT_MAX){
|
||||||
|
/// "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
|
||||||
|
WARNX(_("Integer out of range"));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
iptr = (int*)num;
|
||||||
|
*iptr = (int)tmp;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same as myatoll but for double
|
||||||
|
// There's no NAN & INF checking here (what if they would be needed?)
|
||||||
|
static bool myatod(void *num, const char *str, argtype t){
|
||||||
|
double tmp, *dptr;
|
||||||
|
float *fptr;
|
||||||
|
char *endptr;
|
||||||
|
assert(str);
|
||||||
|
tmp = strtod(str, &endptr);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||||
|
return FALSE;
|
||||||
|
switch(t){
|
||||||
|
case arg_double:
|
||||||
|
dptr = (double *) num;
|
||||||
|
*dptr = tmp;
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
default:
|
||||||
|
fptr = (float *) num;
|
||||||
|
*fptr = (float)tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get index of current option in array options
|
||||||
|
* @param opt (i) - returning val of getopt_long
|
||||||
|
* @param options (i) - array of options
|
||||||
|
* @return index in array
|
||||||
|
*/
|
||||||
|
static int get_optind(int opt, myoption *options){
|
||||||
|
int oind;
|
||||||
|
myoption *opts = options;
|
||||||
|
assert(opts);
|
||||||
|
for(oind = 0; opts->name && opts->val != opt; oind++, opts++);
|
||||||
|
if(!opts->name || opts->val != opt) // no such parameter
|
||||||
|
showhelp(-1, options);
|
||||||
|
return oind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reallocate new value in array of multiple repeating arguments
|
||||||
|
* @arg paptr - address of pointer to array (**void)
|
||||||
|
* @arg type - its type (for realloc)
|
||||||
|
* @return pointer to new (next) value
|
||||||
|
*/
|
||||||
|
void *get_aptr(void *paptr, argtype type){
|
||||||
|
int i = 1;
|
||||||
|
void **aptr = *((void***)paptr);
|
||||||
|
if(aptr){ // there's something in array
|
||||||
|
void **p = aptr;
|
||||||
|
while(*p++) ++i;
|
||||||
|
}
|
||||||
|
size_t sz = 0;
|
||||||
|
switch(type){
|
||||||
|
default:
|
||||||
|
case arg_none:
|
||||||
|
/// "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÎÅÓËÏÌØËÏ ÐÁÒÁÍÅÔÒÏ× ÂÅÚ ÁÒÇÕÍÅÎÔÏ×!"
|
||||||
|
ERRX("Can't use multiple args with arg_none!");
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
sz = sizeof(int);
|
||||||
|
break;
|
||||||
|
case arg_longlong:
|
||||||
|
sz = sizeof(long long);
|
||||||
|
break;
|
||||||
|
case arg_double:
|
||||||
|
sz = sizeof(double);
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
sz = sizeof(float);
|
||||||
|
break;
|
||||||
|
case arg_string:
|
||||||
|
sz = 0;
|
||||||
|
break;
|
||||||
|
/* case arg_function:
|
||||||
|
sz = sizeof(argfn *);
|
||||||
|
break;*/
|
||||||
|
}
|
||||||
|
aptr = realloc(aptr, (i + 1) * sizeof(void*));
|
||||||
|
*((void***)paptr) = aptr;
|
||||||
|
aptr[i] = NULL;
|
||||||
|
if(sz){
|
||||||
|
aptr[i - 1] = malloc(sz);
|
||||||
|
}else
|
||||||
|
aptr[i - 1] = &aptr[i - 1];
|
||||||
|
return aptr[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse command line arguments
|
||||||
|
* ! If arg is string, then value will be strdup'ed!
|
||||||
|
*
|
||||||
|
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt`
|
||||||
|
* @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt`
|
||||||
|
* BE CAREFUL! if you wanna use full argc & argv, save their original values before
|
||||||
|
* calling this function
|
||||||
|
* @param options (i) - array of `myoption` for arguments parcing
|
||||||
|
*
|
||||||
|
* @exit: in case of error this function show help & make `exit(-1)`
|
||||||
|
*/
|
||||||
|
void parseargs(int *argc, char ***argv, myoption *options){
|
||||||
|
char *short_options, *soptr;
|
||||||
|
struct option *long_options, *loptr;
|
||||||
|
size_t optsize, i;
|
||||||
|
myoption *opts = options;
|
||||||
|
// check whether there is at least one options
|
||||||
|
assert(opts);
|
||||||
|
assert(opts[0].name);
|
||||||
|
// first we count how much values are in opts
|
||||||
|
for(optsize = 0; opts->name; optsize++, opts++);
|
||||||
|
// now we can allocate memory
|
||||||
|
short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts
|
||||||
|
long_options = calloc(optsize + 1, sizeof(struct option));
|
||||||
|
opts = options; loptr = long_options; soptr = short_options;
|
||||||
|
// in debug mode check the parameters are not repeated
|
||||||
|
#ifdef EBUG
|
||||||
|
char **longlist = MALLOC(char*, optsize);
|
||||||
|
char *shortlist = MALLOC(char, optsize);
|
||||||
|
#endif
|
||||||
|
// fill short/long parameters and make a simple checking
|
||||||
|
for(i = 0; i < optsize; i++, loptr++, opts++){
|
||||||
|
// check
|
||||||
|
assert(opts->name); // check name
|
||||||
|
#ifdef EBUG
|
||||||
|
longlist[i] = strdup(opts->name);
|
||||||
|
#endif
|
||||||
|
if(opts->has_arg){
|
||||||
|
assert(opts->type != arg_none); // check error with arg type
|
||||||
|
assert(opts->argptr); // check pointer
|
||||||
|
}
|
||||||
|
if(opts->type != arg_none) // if there is a flag without arg, check its pointer
|
||||||
|
assert(opts->argptr);
|
||||||
|
// fill long_options
|
||||||
|
// don't do memcmp: what if there would be different alignment?
|
||||||
|
loptr->name = opts->name;
|
||||||
|
loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1;
|
||||||
|
loptr->flag = opts->flag;
|
||||||
|
loptr->val = opts->val;
|
||||||
|
// fill short options if they are:
|
||||||
|
if(!opts->flag && opts->val){
|
||||||
|
#ifdef EBUG
|
||||||
|
shortlist[i] = (char) opts->val;
|
||||||
|
#endif
|
||||||
|
*soptr++ = opts->val;
|
||||||
|
if(loptr->has_arg) // add ':' if option has required argument
|
||||||
|
*soptr++ = ':';
|
||||||
|
if(loptr->has_arg == 2) // add '::' if option has optional argument
|
||||||
|
*soptr++ = ':';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sort all lists & check for repeating
|
||||||
|
#ifdef EBUG
|
||||||
|
int cmpstringp(const void *p1, const void *p2){
|
||||||
|
return strcmp(* (char * const *) p1, * (char * const *) p2);
|
||||||
|
}
|
||||||
|
int cmpcharp(const void *p1, const void *p2){
|
||||||
|
return (int)(*(char * const)p1 - *(char *const)p2);
|
||||||
|
}
|
||||||
|
qsort(longlist, optsize, sizeof(char *), cmpstringp);
|
||||||
|
qsort(shortlist,optsize, sizeof(char), cmpcharp);
|
||||||
|
char *prevl = longlist[0], prevshrt = shortlist[0];
|
||||||
|
for(i = 1; i < optsize; ++i){
|
||||||
|
if(longlist[i]){
|
||||||
|
if(prevl){
|
||||||
|
if(strcmp(prevl, longlist[i]) == 0) ERRX("double long arguments: --%s", prevl);
|
||||||
|
}
|
||||||
|
prevl = longlist[i];
|
||||||
|
}
|
||||||
|
if(shortlist[i]){
|
||||||
|
if(prevshrt){
|
||||||
|
if(prevshrt == shortlist[i]) ERRX("double short arguments: -%c", prevshrt);
|
||||||
|
}
|
||||||
|
prevshrt = shortlist[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// now we have both long_options & short_options and can parse `getopt_long`
|
||||||
|
while(1){
|
||||||
|
int opt;
|
||||||
|
int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[]
|
||||||
|
if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break;
|
||||||
|
if(opt == '?'){
|
||||||
|
opt = optopt;
|
||||||
|
optind = get_optind(opt, options);
|
||||||
|
if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR)
|
||||||
|
showhelp(optind, options); // need argument
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(opt == 0 || oindex > 0) optind = oindex;
|
||||||
|
else optind = get_optind(opt, options);
|
||||||
|
}
|
||||||
|
opts = &options[optind];
|
||||||
|
// if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag
|
||||||
|
// now check option
|
||||||
|
if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR)
|
||||||
|
if(!optarg) showhelp(optind, options); // need argument
|
||||||
|
void *aptr;
|
||||||
|
if(opts->has_arg == MULT_PAR){
|
||||||
|
aptr = get_aptr(opts->argptr, opts->type);
|
||||||
|
}else
|
||||||
|
aptr = opts->argptr;
|
||||||
|
bool result = TRUE;
|
||||||
|
// even if there is no argument, but argptr != NULL, think that optarg = "1"
|
||||||
|
if(!optarg) optarg = "1";
|
||||||
|
switch(opts->type){
|
||||||
|
default:
|
||||||
|
case arg_none:
|
||||||
|
if(opts->argptr) *((int*)aptr) += 1; // increment value
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
result = myatoll(aptr, optarg, arg_int);
|
||||||
|
break;
|
||||||
|
case arg_longlong:
|
||||||
|
result = myatoll(aptr, optarg, arg_longlong);
|
||||||
|
break;
|
||||||
|
case arg_double:
|
||||||
|
result = myatod(aptr, optarg, arg_double);
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
result = myatod(aptr, optarg, arg_float);
|
||||||
|
break;
|
||||||
|
case arg_string:
|
||||||
|
result = (*((void**)aptr) = (void*)strdup(optarg));
|
||||||
|
break;
|
||||||
|
case arg_function:
|
||||||
|
result = ((argfn)aptr)(optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!result){
|
||||||
|
showhelp(optind, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*argc -= optind;
|
||||||
|
*argv += optind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compare function for qsort
|
||||||
|
* first - sort by short options; second - sort arguments without sort opts (by long options)
|
||||||
|
*/
|
||||||
|
static int argsort(const void *a1, const void *a2){
|
||||||
|
const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2;
|
||||||
|
const char *l1 = o1->name, *l2 = o2->name;
|
||||||
|
int s1 = o1->val, s2 = o2->val;
|
||||||
|
int *f1 = o1->flag, *f2 = o2->flag;
|
||||||
|
// check if both options has short arg
|
||||||
|
if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg
|
||||||
|
return (s1 - s2);
|
||||||
|
}else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long
|
||||||
|
return strcmp(l1, l2);
|
||||||
|
}else{ // only one have short arg -- return it
|
||||||
|
if(f2 || !s2) return -1; // a1 have short - it is 'lesser'
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show help information based on myoption->help values
|
||||||
|
* @param oindex (i) - if non-negative, show only help by myoption[oindex].help
|
||||||
|
* @param options (i) - array of `myoption`
|
||||||
|
*
|
||||||
|
* @exit: run `exit(-1)` !!!
|
||||||
|
*/
|
||||||
|
void showhelp(int oindex, myoption *options){
|
||||||
|
int max_opt_len = 0; // max len of options substring - for right indentation
|
||||||
|
const int bufsz = 255;
|
||||||
|
char buf[bufsz+1];
|
||||||
|
myoption *opts = options;
|
||||||
|
assert(opts);
|
||||||
|
assert(opts[0].name); // check whether there is at least one options
|
||||||
|
if(oindex > -1){ // print only one message
|
||||||
|
opts = &options[oindex];
|
||||||
|
printf(" ");
|
||||||
|
if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val);
|
||||||
|
printf("--%s", opts->name);
|
||||||
|
if(opts->has_arg == 1) printf("=arg");
|
||||||
|
else if(opts->has_arg == 2) printf("[=arg]");
|
||||||
|
printf(" %s\n", _(opts->help));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
// header, by default is just "progname\n"
|
||||||
|
printf("\n");
|
||||||
|
if(strstr(helpstring, "%s")) // print progname
|
||||||
|
printf(helpstring, __progname);
|
||||||
|
else // only text
|
||||||
|
printf("%s", helpstring);
|
||||||
|
printf("\n");
|
||||||
|
// count max_opt_len
|
||||||
|
do{
|
||||||
|
int L = strlen(opts->name);
|
||||||
|
if(max_opt_len < L) max_opt_len = L;
|
||||||
|
}while((++opts)->name);
|
||||||
|
max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols
|
||||||
|
opts = options;
|
||||||
|
// count amount of options
|
||||||
|
int N; for(N = 0; opts->name; ++N, ++opts);
|
||||||
|
if(N == 0) exit(-2);
|
||||||
|
// Now print all help (sorted)
|
||||||
|
opts = options;
|
||||||
|
qsort(opts, N, sizeof(myoption), argsort);
|
||||||
|
do{
|
||||||
|
int p = sprintf(buf, " "); // a little indent
|
||||||
|
if(!opts->flag && opts->val) // .val is short argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val);
|
||||||
|
p += snprintf(buf+p, bufsz-p, "--%s", opts->name);
|
||||||
|
if(opts->has_arg == 1) // required argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "=arg");
|
||||||
|
else if(opts->has_arg == 2) // optional argument
|
||||||
|
p += snprintf(buf+p, bufsz-p, "[=arg]");
|
||||||
|
assert(p < max_opt_len); // there would be magic if p >= max_opt_len
|
||||||
|
printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after
|
||||||
|
++opts;
|
||||||
|
}while(--N);
|
||||||
|
printf("\n\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get suboptions from parameter string
|
||||||
|
* @param str - parameter string
|
||||||
|
* @param opt - pointer to suboptions structure
|
||||||
|
* @return TRUE if all OK
|
||||||
|
*/
|
||||||
|
bool get_suboption(char *str, mysuboption *opt){
|
||||||
|
int findsubopt(char *par, mysuboption *so){
|
||||||
|
int idx = 0;
|
||||||
|
if(!par) return -1;
|
||||||
|
while(so[idx].name){
|
||||||
|
if(strcasecmp(par, so[idx].name) == 0) return idx;
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
return -1; // badarg
|
||||||
|
}
|
||||||
|
bool opt_setarg(mysuboption *so, int idx, char *val){
|
||||||
|
mysuboption *soptr = &so[idx];
|
||||||
|
bool result = FALSE;
|
||||||
|
void *aptr = soptr->argptr;
|
||||||
|
switch(soptr->type){
|
||||||
|
default:
|
||||||
|
case arg_none:
|
||||||
|
if(soptr->argptr) *((int*)aptr) += 1; // increment value
|
||||||
|
result = TRUE;
|
||||||
|
break;
|
||||||
|
case arg_int:
|
||||||
|
result = myatoll(aptr, val, arg_int);
|
||||||
|
break;
|
||||||
|
case arg_longlong:
|
||||||
|
result = myatoll(aptr, val, arg_longlong);
|
||||||
|
break;
|
||||||
|
case arg_double:
|
||||||
|
result = myatod(aptr, val, arg_double);
|
||||||
|
break;
|
||||||
|
case arg_float:
|
||||||
|
result = myatod(aptr, val, arg_float);
|
||||||
|
break;
|
||||||
|
case arg_string:
|
||||||
|
result = (*((void**)aptr) = (void*)strdup(val));
|
||||||
|
break;
|
||||||
|
case arg_function:
|
||||||
|
result = ((argfn)aptr)(val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
char *tok;
|
||||||
|
bool ret = FALSE;
|
||||||
|
char *tmpbuf;
|
||||||
|
tok = strtok_r(str, ":,", &tmpbuf);
|
||||||
|
do{
|
||||||
|
char *val = strchr(tok, '=');
|
||||||
|
int noarg = 0;
|
||||||
|
if(val == NULL){ // no args
|
||||||
|
val = "1";
|
||||||
|
noarg = 1;
|
||||||
|
}else{
|
||||||
|
*val++ = '\0';
|
||||||
|
if(!*val || *val == ':' || *val == ','){ // no argument - delimeter after =
|
||||||
|
val = "1"; noarg = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int idx = findsubopt(tok, opt);
|
||||||
|
if(idx < 0){
|
||||||
|
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
|
||||||
|
WARNX(_("Wrong parameter: %s"), tok);
|
||||||
|
goto returning;
|
||||||
|
}
|
||||||
|
if(noarg && opt[idx].has_arg == NEED_ARG){
|
||||||
|
/// "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
|
||||||
|
WARNX(_("%s: argument needed!"), tok);
|
||||||
|
goto returning;
|
||||||
|
}
|
||||||
|
if(!opt_setarg(opt, idx, val)){
|
||||||
|
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
|
||||||
|
WARNX(_("Wrong argument \"%s\" of parameter \"%s\""), val, tok);
|
||||||
|
goto returning;
|
||||||
|
}
|
||||||
|
}while((tok = strtok_r(NULL, ":,", &tmpbuf)));
|
||||||
|
ret = TRUE;
|
||||||
|
returning:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
124
Z1000_focus/parseargs.h
Normal file
124
Z1000_focus/parseargs.h
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* parseargs.h - headers for parsing command line arguments
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __PARSEARGS_H__
|
||||||
|
#define __PARSEARGS_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>// bool
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// macro for argptr
|
||||||
|
#define APTR(x) ((void*)x)
|
||||||
|
|
||||||
|
// if argptr is a function:
|
||||||
|
typedef bool(*argfn)(void *arg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* type of getopt's argument
|
||||||
|
* WARNING!
|
||||||
|
* My function change value of flags by pointer, so if you want to use another type
|
||||||
|
* make a latter conversion, example:
|
||||||
|
* char charg;
|
||||||
|
* int iarg;
|
||||||
|
* myoption opts[] = {
|
||||||
|
* {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option};
|
||||||
|
* ..(parse args)..
|
||||||
|
* charg = (char) iarg;
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
arg_none = 0, // no arg
|
||||||
|
arg_int, // integer
|
||||||
|
arg_longlong, // long long
|
||||||
|
arg_double, // double
|
||||||
|
arg_float, // float
|
||||||
|
arg_string, // char *
|
||||||
|
arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)`
|
||||||
|
} argtype;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure for getopt_long & help
|
||||||
|
* BE CAREFUL: .argptr is pointer to data or pointer to function,
|
||||||
|
* conversion depends on .type
|
||||||
|
*
|
||||||
|
* ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext,
|
||||||
|
* but you can redefine it before `#include "parseargs.h"`
|
||||||
|
*
|
||||||
|
* if arg is string, then value wil be strdup'ed like that:
|
||||||
|
* char *str;
|
||||||
|
* myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option};
|
||||||
|
* *(opts[1].str) = strdup(optarg);
|
||||||
|
* in other cases argptr should be address of some variable (or pointer to allocated memory)
|
||||||
|
*
|
||||||
|
* NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr
|
||||||
|
*
|
||||||
|
* !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum{
|
||||||
|
NO_ARGS = 0, // first three are the same as in getopt_long
|
||||||
|
NEED_ARG = 1,
|
||||||
|
OPT_ARG = 2,
|
||||||
|
MULT_PAR
|
||||||
|
} hasarg;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
// these are from struct option:
|
||||||
|
const char *name; // long option's name
|
||||||
|
hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array)
|
||||||
|
int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0)
|
||||||
|
int val; // short opt name (if flag == NULL) or flag's value
|
||||||
|
// and these are mine:
|
||||||
|
argtype type; // type of argument
|
||||||
|
void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)`
|
||||||
|
const char *help; // help string which would be shown in function `showhelp` or NULL
|
||||||
|
} myoption;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Suboptions structure, almost the same like myoption
|
||||||
|
* used in parse_subopts()
|
||||||
|
*/
|
||||||
|
typedef struct{
|
||||||
|
const char *name;
|
||||||
|
hasarg has_arg;
|
||||||
|
argtype type;
|
||||||
|
void *argptr;
|
||||||
|
} mysuboption;
|
||||||
|
|
||||||
|
// last string of array (all zeros)
|
||||||
|
#define end_option {0,0,0,0,0,0,0}
|
||||||
|
#define end_suboption {0,0,0,0}
|
||||||
|
|
||||||
|
extern const char *__progname;
|
||||||
|
|
||||||
|
void showhelp(int oindex, myoption *options);
|
||||||
|
void parseargs(int *argc, char ***argv, myoption *options);
|
||||||
|
void change_helpstring(char *s);
|
||||||
|
bool get_suboption(char *str, mysuboption *opt);
|
||||||
|
|
||||||
|
#endif // __PARSEARGS_H__
|
||||||
50
Z1000_focus/sdo_abort_codes.h
Normal file
50
Z1000_focus/sdo_abort_codes.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// (c) vsher@sao.ru
|
||||||
|
#pragma once
|
||||||
|
#ifndef SDO_ABORT_CODES_H__
|
||||||
|
#define SDO_ABORT_CODES_H__
|
||||||
|
|
||||||
|
/* CANopen SDO response Abort codes*/
|
||||||
|
#define SDO_MAX_ERR 30
|
||||||
|
struct SDO_Abort_Codes {
|
||||||
|
unsigned long code;
|
||||||
|
char *text;
|
||||||
|
} sdo_error[SDO_MAX_ERR] = {
|
||||||
|
{0x05030000, "Toggle bit not alternated"},
|
||||||
|
{0x05040000, "SDO protocol timed out"},
|
||||||
|
{0x05040001, "Client/server command specifier not valid or unknown"},
|
||||||
|
{0x05040002, "Invalid block size (block mode only)"},
|
||||||
|
{0x05040003, "Invalid sequence number (block mode only)"},
|
||||||
|
{0x05040004, "CRC error (block mode only)"},
|
||||||
|
{0x05040005, "Out of memory"},
|
||||||
|
{0x06010000, "Unsupported access to an object"},
|
||||||
|
{0x06010001, "Attempt to read a write only object"},
|
||||||
|
{0x06010002, "Attempt to write a read only object"},
|
||||||
|
{0x06020000, "Object does not exist in the object dictionary"},
|
||||||
|
{0x06040041, "Object cannot be mapped to the PDO"},
|
||||||
|
{0x06040042, "The number and length of the objects to be mapped whould exeed PDO length"},
|
||||||
|
{0x06040043, "General parameter incompatibility reason"},
|
||||||
|
{0x06040047, "General internal incompatibility in the device"},
|
||||||
|
{0x06060000, "Access failed due to a hardware error"},
|
||||||
|
{0x06070010, "Data type does not match, length of service parameter does not match"},
|
||||||
|
{0x06070012, "Data type does not match, length of service parameter too hight"},
|
||||||
|
{0x06070013, "Data type does not match, length of service parameter too low"},
|
||||||
|
{0x06090011, "Sub-index does not exist."},
|
||||||
|
{0x06090030, "Value range of parameter exceeded (only for write access)"},
|
||||||
|
{0x06090031, "Value of parameter written too hight"},
|
||||||
|
{0x06090032, "Value of parameter written too low"},
|
||||||
|
{0x06090036, "Maximum value is less than minimum value"},
|
||||||
|
{0x08000000, "General error"},
|
||||||
|
{0x08000020, "Data cannot be transferred or stored to the application"},
|
||||||
|
{0x08000021, "Data cannot be transferred or stored to the application because of local control"},
|
||||||
|
{0x08000022, "Data cannot be transferred or stored to the application because ofthe present device state"},
|
||||||
|
{0x08000023, "Object dictionary dynamic generation fails or no object dictionary is present."},
|
||||||
|
{0,NULL}
|
||||||
|
};
|
||||||
|
static inline char *sdo_abort_text(unsigned long code) {
|
||||||
|
int i;
|
||||||
|
for(i=0; i<SDO_MAX_ERR && sdo_error[i].text!=NULL; i++)
|
||||||
|
if(code==sdo_error[i].code) return sdo_error[i].text;
|
||||||
|
return "SDO error of unknown type";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SDO_ABORT_CODES_H__
|
||||||
401
Z1000_focus/socket.c
Normal file
401
Z1000_focus/socket.c
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
/*
|
||||||
|
* geany_encoding=koi8-r
|
||||||
|
* socket.c - socket IO
|
||||||
|
*
|
||||||
|
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "can_encoder.h"
|
||||||
|
#include "HW_dependent.h"
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
#include "socket.h"
|
||||||
|
#include <netdb.h> // addrinfo
|
||||||
|
#include <arpa/inet.h> // inet_ntop
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <limits.h> // INT_xxx
|
||||||
|
#include <math.h> // fabs
|
||||||
|
#include <signal.h> // pthread_kill
|
||||||
|
#include <unistd.h> // daemon
|
||||||
|
#include <sys/syscall.h> // syscall
|
||||||
|
|
||||||
|
#include "cmdlnopts.h" // glob_pars
|
||||||
|
|
||||||
|
#define BUFLEN (10240)
|
||||||
|
// Max amount of connections
|
||||||
|
#define BACKLOG (30)
|
||||||
|
|
||||||
|
extern glob_pars *G;
|
||||||
|
|
||||||
|
/**************** COMMON FUNCTIONS ****************/
|
||||||
|
/**
|
||||||
|
* wait for answer from socket
|
||||||
|
* @param sock - socket fd
|
||||||
|
* @return 0 in case of error or timeout, 1 in case of socket ready
|
||||||
|
*/
|
||||||
|
static int waittoread(int sock){
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval timeout;
|
||||||
|
int rc;
|
||||||
|
timeout.tv_sec = 1; // wait not more than 1 second
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(sock, &fds);
|
||||||
|
do{
|
||||||
|
rc = select(sock+1, &fds, NULL, NULL, &timeout);
|
||||||
|
if(rc < 0){
|
||||||
|
if(errno != EINTR){
|
||||||
|
WARN("select()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}while(1);
|
||||||
|
if(FD_ISSET(sock, &fds)) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************** SERVER FUNCTIONS ****************/
|
||||||
|
// `canbus_mutex` used to exclude simultaneous CAN messages
|
||||||
|
// `moving_mutex` used to block simultaneous attempts to move motor
|
||||||
|
static pthread_mutex_t canbus_mutex = PTHREAD_MUTEX_INITIALIZER, moving_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
bool emerg_stop = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send data over socket
|
||||||
|
* @param sock - socket fd
|
||||||
|
* @param webquery - ==1 if this is web query
|
||||||
|
* @param buf - buffer with data (zero-terminated)
|
||||||
|
* @return 1 if all OK
|
||||||
|
*/
|
||||||
|
static int send_data(int sock, int webquery, char *buf){
|
||||||
|
if(!buf) return 0;
|
||||||
|
ssize_t L, Len = strlen(buf);
|
||||||
|
DBG("buf: %s, Len: %zd", buf, Len);
|
||||||
|
if(Len < 1) return 0;
|
||||||
|
char tbuf[BUFLEN];
|
||||||
|
// OK buffer ready, prepare to send it
|
||||||
|
if(webquery){
|
||||||
|
L = snprintf(tbuf, BUFLEN,
|
||||||
|
"HTTP/2.0 200 OK\r\n"
|
||||||
|
"Access-Control-Allow-Origin: *\r\n"
|
||||||
|
"Access-Control-Allow-Methods: GET, POST\r\n"
|
||||||
|
"Access-Control-Allow-Credentials: true\r\n"
|
||||||
|
"Content-type: text/plain\r\nContent-Length: %zd\r\n\r\n", Len);
|
||||||
|
if(L < 0){
|
||||||
|
WARN("sprintf()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(L != write(sock, tbuf, L)){
|
||||||
|
WARN("write");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Len != write(sock, buf, Len)){
|
||||||
|
WARN("write()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// search a first word after needle without spaces
|
||||||
|
static char* stringscan(char *str, char *needle){
|
||||||
|
char *a, *e;
|
||||||
|
char *end = str + strlen(str);
|
||||||
|
a = strstr(str, needle);
|
||||||
|
if(!a) return NULL;
|
||||||
|
a += strlen(needle);
|
||||||
|
while (a < end && (*a == ' ' || *a == '\r' || *a == '\t' || *a == '\r')) a++;
|
||||||
|
if(a >= end) return NULL;
|
||||||
|
e = strchr(a, ' ');
|
||||||
|
if(e) *e = 0;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief move_focus - separate thread moving focus to given position
|
||||||
|
* @param targpos - target position
|
||||||
|
*/
|
||||||
|
static void *move_focus(void *targpos){
|
||||||
|
double pos = *((double*)targpos);
|
||||||
|
DBG("MOVE FOCUS: %g", pos);
|
||||||
|
pthread_mutex_lock(&canbus_mutex);
|
||||||
|
// in any error case we should check end-switches and move out of them!
|
||||||
|
if(move2pos(pos)) go_out_from_ESW();
|
||||||
|
pthread_mutex_unlock(&moving_mutex);
|
||||||
|
pthread_mutex_unlock(&canbus_mutex);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *startmoving(double pos){
|
||||||
|
static double sp;
|
||||||
|
if(pthread_mutex_trylock(&moving_mutex)) return S_ANS_MOVING;
|
||||||
|
pthread_t m_thread;
|
||||||
|
DBG("startmoving: %g", pos);
|
||||||
|
sp = pos;
|
||||||
|
if(pthread_create(&m_thread, NULL, move_focus, (void*) &sp)){
|
||||||
|
WARN("pthread_create()");
|
||||||
|
pthread_mutex_unlock(&moving_mutex);
|
||||||
|
return S_ANS_ERR;
|
||||||
|
}else{
|
||||||
|
DBG("Thread created, detouch");
|
||||||
|
pthread_detach(m_thread); // don't care about thread state
|
||||||
|
}
|
||||||
|
return S_ANS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *handle_socket(void *asock){
|
||||||
|
#define getparam(x) (strncmp(found, x, sizeof(x)-1) == 0)
|
||||||
|
//putlog("handle_socket(): getpid: %d, pthread_self: %lu, tid: %lu",getpid(), pthread_self(), syscall(SYS_gettid));
|
||||||
|
int sock = *((int*)asock);
|
||||||
|
int webquery = 0; // whether query is web or regular
|
||||||
|
char buff[BUFLEN];
|
||||||
|
ssize_t rd;
|
||||||
|
double t0 = dtime();
|
||||||
|
while(dtime() - t0 < SOCKET_TIMEOUT){
|
||||||
|
if(!waittoread(sock)){ // no data incoming
|
||||||
|
DBG("no incoming data");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if((rd = read(sock, buff, BUFLEN-1)) < 1){
|
||||||
|
DBG("socket closed. Exit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DBG("Got %zd bytes", rd);
|
||||||
|
// add trailing zero to be on the safe side
|
||||||
|
buff[rd] = 0;
|
||||||
|
// now we should check what do user want
|
||||||
|
char *got, *found = buff;
|
||||||
|
if((got = stringscan(buff, "GET")) || (got = stringscan(buff, "POST"))){ // web query
|
||||||
|
webquery = 1;
|
||||||
|
char *slash = strchr(got, '/');
|
||||||
|
if(slash) found = slash + 1;
|
||||||
|
// web query have format GET /some.resource
|
||||||
|
}
|
||||||
|
// here we can process user data
|
||||||
|
DBG("user send: %s%s\n", buff, webquery ? ", web" : "");
|
||||||
|
// empty request == focus request
|
||||||
|
if(strlen(found) < 1 || getparam(S_CMD_FOCUS)){
|
||||||
|
DBG("position request");
|
||||||
|
snprintf(buff, BUFLEN, "%.3f", curPos());
|
||||||
|
}else if(getparam(S_CMD_STOP)){
|
||||||
|
DBG("Stop request");
|
||||||
|
emerg_stop = TRUE;
|
||||||
|
pthread_mutex_lock(&canbus_mutex);
|
||||||
|
pthread_mutex_lock(&moving_mutex);
|
||||||
|
if(stop()) sprintf(buff, S_ANS_ERR);
|
||||||
|
else sprintf(buff, S_ANS_OK);
|
||||||
|
emerg_stop = FALSE;
|
||||||
|
pthread_mutex_unlock(&moving_mutex);
|
||||||
|
pthread_mutex_unlock(&canbus_mutex);
|
||||||
|
}else if(getparam(S_CMD_TARGSPEED)){
|
||||||
|
char *ch = strchr(found, '=');
|
||||||
|
double spd;
|
||||||
|
if(pthread_mutex_trylock(&moving_mutex)) sprintf(buff, S_ANS_MOVING);
|
||||||
|
else{
|
||||||
|
pthread_mutex_lock(&canbus_mutex);
|
||||||
|
if(!ch || !str2double(&spd, ch+1) || fabs(spd) < MINSPEED || fabs(spd) > MAXSPEED || movewconstspeed(spd)) sprintf(buff, S_ANS_ERR);
|
||||||
|
else{
|
||||||
|
DBG("Move with constant speed %g request", spd);
|
||||||
|
sprintf(buff, S_ANS_OK);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&canbus_mutex);
|
||||||
|
pthread_mutex_unlock(&moving_mutex);
|
||||||
|
}
|
||||||
|
}else if(getparam(S_CMD_GOTO)){
|
||||||
|
char *ch = strchr(found, '=');
|
||||||
|
double pos;
|
||||||
|
if(!ch || !str2double(&pos, ch+1) || pos < FOCMIN_MM || pos > FOCMAX_MM) sprintf(buff, S_ANS_ERR);
|
||||||
|
else{
|
||||||
|
DBG("Move to position %g request", pos);
|
||||||
|
sprintf(buff, startmoving(pos));
|
||||||
|
}
|
||||||
|
}else sprintf(buff, S_ANS_ERR);
|
||||||
|
if(!send_data(sock, webquery, buff)){
|
||||||
|
WARNX("can't send data, some error occured");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
DBG("closed");
|
||||||
|
//putlog("socket closed, exit");
|
||||||
|
pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
#undef getparam
|
||||||
|
}
|
||||||
|
|
||||||
|
// main socket server
|
||||||
|
static void *server(void *asock){
|
||||||
|
putlog("server(): getpid: %d, pthread_self: %lu, tid: %lu",getpid(), pthread_self(), syscall(SYS_gettid));
|
||||||
|
int sock = *((int*)asock);
|
||||||
|
if(listen(sock, BACKLOG) == -1){
|
||||||
|
putlog("listen() failed");
|
||||||
|
WARN("listen");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
while(1){
|
||||||
|
socklen_t size = sizeof(struct sockaddr_in);
|
||||||
|
struct sockaddr_in their_addr;
|
||||||
|
int newsock;
|
||||||
|
if(!waittoread(sock)) continue;
|
||||||
|
newsock = accept(sock, (struct sockaddr*)&their_addr, &size);
|
||||||
|
if(newsock <= 0){
|
||||||
|
putlog("accept() failed");
|
||||||
|
WARN("accept()");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct sockaddr_in* pV4Addr = (struct sockaddr_in*)&their_addr;
|
||||||
|
struct in_addr ipAddr = pV4Addr->sin_addr;
|
||||||
|
char str[INET_ADDRSTRLEN];
|
||||||
|
inet_ntop(AF_INET, &ipAddr, str, INET_ADDRSTRLEN);
|
||||||
|
//putlog("get connection from %s", str);
|
||||||
|
DBG("Got connection from %s", str);
|
||||||
|
pthread_t handler_thread;
|
||||||
|
if(pthread_create(&handler_thread, NULL, handle_socket, (void*) &newsock)){
|
||||||
|
putlog("server(): pthread_create() failed");
|
||||||
|
WARN("pthread_create()");
|
||||||
|
}else{
|
||||||
|
DBG("Thread created, detouch");
|
||||||
|
pthread_detach(handler_thread); // don't care about thread state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putlog("server(): UNREACHABLE CODE REACHED!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// data gathering & socket management
|
||||||
|
static void daemon_(int sock){
|
||||||
|
if(sock < 0) return;
|
||||||
|
pthread_t sock_thread;
|
||||||
|
if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){
|
||||||
|
putlog("daemon_(): pthread_create() failed");
|
||||||
|
ERR("pthread_create()");
|
||||||
|
}
|
||||||
|
do{
|
||||||
|
if(pthread_kill(sock_thread, 0) == ESRCH){ // died
|
||||||
|
WARNX("Sockets thread died");
|
||||||
|
putlog("Sockets thread died");
|
||||||
|
pthread_join(sock_thread, NULL);
|
||||||
|
if(pthread_create(&sock_thread, NULL, server, (void*) &sock)){
|
||||||
|
putlog("daemon_(): new pthread_create() failed");
|
||||||
|
ERR("pthread_create()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usleep(500000); // sleep a little or thread's won't be able to lock mutex
|
||||||
|
// get current position
|
||||||
|
pthread_mutex_lock(&canbus_mutex);
|
||||||
|
getPos(NULL);
|
||||||
|
pthread_mutex_unlock(&canbus_mutex);
|
||||||
|
}while(1);
|
||||||
|
putlog("daemon_(): UNREACHABLE CODE REACHED!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run daemon service
|
||||||
|
*/
|
||||||
|
void daemonize(const char *port){
|
||||||
|
int sock = -1;
|
||||||
|
struct addrinfo hints, *res, *p;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
if(getaddrinfo(NULL, port, &hints, &res) != 0){
|
||||||
|
ERR("getaddrinfo");
|
||||||
|
}
|
||||||
|
struct sockaddr_in *ia = (struct sockaddr_in*)res->ai_addr;
|
||||||
|
char str[INET_ADDRSTRLEN];
|
||||||
|
inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN);
|
||||||
|
DBG("canonname: %s, port: %u, addr: %s\n", res->ai_canonname, ntohs(ia->sin_port), str);
|
||||||
|
// loop through all the results and bind to the first we can
|
||||||
|
for(p = res; p != NULL; p = p->ai_next){
|
||||||
|
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
|
||||||
|
WARN("socket");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int reuseaddr = 1;
|
||||||
|
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
|
||||||
|
ERR("setsockopt");
|
||||||
|
}
|
||||||
|
if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){
|
||||||
|
close(sock);
|
||||||
|
WARN("bind");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break; // if we get here, we have a successfull connection
|
||||||
|
}
|
||||||
|
if(p == NULL){
|
||||||
|
putlog("failed to bind socket, exit");
|
||||||
|
// looped off the end of the list with no successful bind
|
||||||
|
ERRX("failed to bind socket");
|
||||||
|
}
|
||||||
|
freeaddrinfo(res);
|
||||||
|
daemon_(sock);
|
||||||
|
close(sock);
|
||||||
|
putlog("socket closed, exit");
|
||||||
|
signals(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************** CLIENT FUNCTIONS ****************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief sock_send_data - send data to a socket
|
||||||
|
*/
|
||||||
|
void sock_send_data(const char *host, const char *port, const char *data){
|
||||||
|
int sock = 0;
|
||||||
|
struct addrinfo hints, *res, *p;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
if(getaddrinfo(host, port, &hints, &res) != 0){
|
||||||
|
ERR("getaddrinfo");
|
||||||
|
}
|
||||||
|
struct sockaddr_in *ia = (struct sockaddr_in*)res->ai_addr;
|
||||||
|
char str[INET_ADDRSTRLEN];
|
||||||
|
inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN);
|
||||||
|
DBG("canonname: %s, port: %u, addr: %s\n", res->ai_canonname, ntohs(ia->sin_port), str);
|
||||||
|
// loop through all the results and bind to the first we can
|
||||||
|
for(p = res; p != NULL; p = p->ai_next){
|
||||||
|
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
|
||||||
|
WARN("socket");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
|
||||||
|
WARN("connect()");
|
||||||
|
close(sock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break; // if we get here, we have a successfull connection
|
||||||
|
}
|
||||||
|
if(p == NULL) ERRX("failed to connect to server");
|
||||||
|
size_t L = strlen(data);
|
||||||
|
if(send(sock, data, L, 0) != (ssize_t)L){ WARN("send"); return;}
|
||||||
|
double t0 = dtime();
|
||||||
|
while(dtime() - t0 < SOCKET_TIMEOUT){
|
||||||
|
if(!waittoread(sock)) continue;
|
||||||
|
char buff[32];
|
||||||
|
int n = read(sock, buff, 31);
|
||||||
|
if(n > 0){
|
||||||
|
buff[n] = 0;
|
||||||
|
printf("%s\n", buff);
|
||||||
|
close(sock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WARN("No answer!");
|
||||||
|
}
|
||||||
50
Z1000_focus/socket.h
Normal file
50
Z1000_focus/socket.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* geany_encoding=koi8-r
|
||||||
|
* socket.h
|
||||||
|
*
|
||||||
|
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#ifndef __SOCKET_H__
|
||||||
|
#define __SOCKET_H__
|
||||||
|
|
||||||
|
#include "stdbool.h"
|
||||||
|
|
||||||
|
// timeout for socket closing
|
||||||
|
#define SOCKET_TIMEOUT (5.0)
|
||||||
|
// default port number (strinig)
|
||||||
|
#define DEFPORT "4444"
|
||||||
|
|
||||||
|
// commands through the socket
|
||||||
|
#define S_CMD_STOP "stop"
|
||||||
|
#define S_CMD_FOCUS "focus"
|
||||||
|
#define S_CMD_TARGSPEED "targspeed"
|
||||||
|
#define S_CMD_GOTO "goto"
|
||||||
|
|
||||||
|
// answers through the socket
|
||||||
|
#define S_ANS_ERR "error"
|
||||||
|
#define S_ANS_OK "OK"
|
||||||
|
#define S_ANS_MOVING "moving"
|
||||||
|
|
||||||
|
bool emerg_stop;
|
||||||
|
|
||||||
|
void daemonize(const char *port);
|
||||||
|
void sock_send_data(const char *host, const char *port, const char *data);
|
||||||
|
|
||||||
|
#endif // __SOCKET_H__
|
||||||
434
Z1000_focus/usefull_macros.c
Normal file
434
Z1000_focus/usefull_macros.c
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
/*
|
||||||
|
* usefull_macros.h - a set of usefull functions: memory, color etc
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "usefull_macros.h"
|
||||||
|
#include <time.h>
|
||||||
|
#include <linux/limits.h> // PATH_MAX
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function for different purposes that need to know time intervals
|
||||||
|
* @return double value: time in seconds
|
||||||
|
*/
|
||||||
|
double dtime(){
|
||||||
|
double t;
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Coloured terminal
|
||||||
|
\******************************************************************************/
|
||||||
|
int globErr = 0; // errno for WARN/ERR
|
||||||
|
|
||||||
|
// pointers to coloured output printf
|
||||||
|
int (*red)(const char *fmt, ...);
|
||||||
|
int (*green)(const char *fmt, ...);
|
||||||
|
int (*_WARN)(const char *fmt, ...);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* format red / green messages
|
||||||
|
* name: r_pr_, g_pr_
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int r_pr_(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
printf(RED);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
printf(OLDCOLOR);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
int g_pr_(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
printf(GREEN);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
printf(OLDCOLOR);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* print red error/warning messages (if output is a tty)
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int r_WARN(const char *fmt, ...){
|
||||||
|
va_list ar; int i = 1;
|
||||||
|
fprintf(stderr, RED);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
if(globErr){
|
||||||
|
errno = globErr;
|
||||||
|
vwarn(fmt, ar);
|
||||||
|
errno = 0;
|
||||||
|
}else
|
||||||
|
i = vfprintf(stderr, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i++;
|
||||||
|
fprintf(stderr, OLDCOLOR "\n");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char stars[] = "****************************************";
|
||||||
|
/*
|
||||||
|
* notty variants of coloured printf
|
||||||
|
* name: s_WARN, r_pr_notty
|
||||||
|
* @param fmt ... - printf-like format
|
||||||
|
* @return number of printed symbols
|
||||||
|
*/
|
||||||
|
int s_WARN(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
i = fprintf(stderr, "\n%s\n", stars);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
if(globErr){
|
||||||
|
errno = globErr;
|
||||||
|
vwarn(fmt, ar);
|
||||||
|
errno = 0;
|
||||||
|
}else
|
||||||
|
i = +vfprintf(stderr, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i += fprintf(stderr, "\n%s\n", stars);
|
||||||
|
i += fprintf(stderr, "\n");
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
int r_pr_notty(const char *fmt, ...){
|
||||||
|
va_list ar; int i;
|
||||||
|
i = printf("\n%s\n", stars);
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i += vprintf(fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
i += printf("\n%s\n", stars);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run this function in the beginning of main() to setup locale & coloured output
|
||||||
|
*/
|
||||||
|
void initial_setup(){
|
||||||
|
// setup coloured output
|
||||||
|
if(isatty(STDOUT_FILENO)){ // make color output in tty
|
||||||
|
red = r_pr_; green = g_pr_;
|
||||||
|
}else{ // no colors in case of pipe
|
||||||
|
red = r_pr_notty; green = printf;
|
||||||
|
}
|
||||||
|
if(isatty(STDERR_FILENO)) _WARN = r_WARN;
|
||||||
|
else _WARN = s_WARN;
|
||||||
|
// Setup locale
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
setlocale(LC_NUMERIC, "C");
|
||||||
|
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
|
||||||
|
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
|
||||||
|
textdomain(GETTEXT_PACKAGE);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Memory
|
||||||
|
\******************************************************************************/
|
||||||
|
/*
|
||||||
|
* safe memory allocation for macro ALLOC
|
||||||
|
* @param N - number of elements to allocate
|
||||||
|
* @param S - size of single element (typically sizeof)
|
||||||
|
* @return pointer to allocated memory area
|
||||||
|
*/
|
||||||
|
void *my_alloc(size_t N, size_t S){
|
||||||
|
void *p = calloc(N, S);
|
||||||
|
if(!p) ERR("malloc");
|
||||||
|
//assert(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mmap file to a memory area
|
||||||
|
*
|
||||||
|
* @param filename (i) - name of file to mmap
|
||||||
|
* @return stuct with mmap'ed file or die
|
||||||
|
*/
|
||||||
|
mmapbuf *My_mmap(char *filename){
|
||||||
|
int fd;
|
||||||
|
char *ptr;
|
||||||
|
size_t Mlen;
|
||||||
|
struct stat statbuf;
|
||||||
|
/// "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!"
|
||||||
|
if(!filename){
|
||||||
|
WARNX(_("No filename given!"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if((fd = open(filename, O_RDONLY)) < 0){
|
||||||
|
/// "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
|
||||||
|
WARN(_("Can't open %s for reading"), filename);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(fstat (fd, &statbuf) < 0){
|
||||||
|
/// "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
|
||||||
|
WARN(_("Can't stat %s"), filename);
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Mlen = statbuf.st_size;
|
||||||
|
if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){
|
||||||
|
/// "ïÛÉÂËÁ mmap"
|
||||||
|
WARN(_("Mmap error for input"));
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/// "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
|
||||||
|
if(close(fd)) WARN(_("Can't close mmap'ed file"));
|
||||||
|
mmapbuf *ret = MALLOC(mmapbuf, 1);
|
||||||
|
ret->data = ptr;
|
||||||
|
ret->len = Mlen;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void My_munmap(mmapbuf *b){
|
||||||
|
if(munmap(b->data, b->len)){
|
||||||
|
/// "îÅ ÍÏÇÕ munmap"
|
||||||
|
ERR(_("Can't munmap"));
|
||||||
|
}
|
||||||
|
FREE(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* Terminal in no-echo mode
|
||||||
|
\******************************************************************************/
|
||||||
|
static struct termios oldt, newt; // terminal flags
|
||||||
|
static int console_changed = 0;
|
||||||
|
// run on exit:
|
||||||
|
void restore_console(){
|
||||||
|
if(console_changed)
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
|
||||||
|
console_changed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial setup:
|
||||||
|
void setup_con(){
|
||||||
|
if(console_changed) return;
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
|
||||||
|
/// "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
|
||||||
|
WARN(_("Can't setup console"));
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||||
|
signals(0); //quit?
|
||||||
|
}
|
||||||
|
console_changed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read character from console without echo
|
||||||
|
* @return char readed
|
||||||
|
*/
|
||||||
|
int read_console(){
|
||||||
|
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 = 10000;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getchar() without echo
|
||||||
|
* wait until at least one character pressed
|
||||||
|
* @return character readed
|
||||||
|
*/
|
||||||
|
int mygetchar(){ // getchar() without need of pressing ENTER
|
||||||
|
int ret;
|
||||||
|
do ret = read_console();
|
||||||
|
while(ret == 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************\
|
||||||
|
* TTY with select()
|
||||||
|
\******************************************************************************/
|
||||||
|
static struct termio oldtty, tty; // TTY flags
|
||||||
|
static int comfd = -1; // TTY fd
|
||||||
|
|
||||||
|
// run on exit:
|
||||||
|
void restore_tty(){
|
||||||
|
if(comfd == -1) return;
|
||||||
|
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
|
||||||
|
close(comfd);
|
||||||
|
comfd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef BAUD_RATE
|
||||||
|
#define BAUD_RATE B4800
|
||||||
|
#endif
|
||||||
|
// init:
|
||||||
|
void tty_init(char *comdev){
|
||||||
|
DBG("\nOpen port %s ...\n", comdev);
|
||||||
|
do{
|
||||||
|
comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK);
|
||||||
|
}while (comfd == -1 && errno == EINTR);
|
||||||
|
if(comfd < 0){
|
||||||
|
WARN("Can't use port %s\n",comdev);
|
||||||
|
signals(-1); // quit?
|
||||||
|
}
|
||||||
|
// make exclusive open
|
||||||
|
if(ioctl(comfd, TIOCEXCL)){
|
||||||
|
WARN(_("Can't do exclusive open"));
|
||||||
|
close(comfd);
|
||||||
|
signals(2);
|
||||||
|
}
|
||||||
|
DBG(" OK\nGet current settings... ");
|
||||||
|
if(ioctl(comfd,TCGETA,&oldtty) < 0){ // Get settings
|
||||||
|
/// "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÎÁÓÔÒÏÊËÉ"
|
||||||
|
WARN(_("Can't get settings"));
|
||||||
|
signals(-1);
|
||||||
|
}
|
||||||
|
tty = oldtty;
|
||||||
|
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||||
|
tty.c_oflag = 0;
|
||||||
|
tty.c_cflag = BAUD_RATE|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl
|
||||||
|
tty.c_cc[VMIN] = 0; // non-canonical mode
|
||||||
|
tty.c_cc[VTIME] = 5;
|
||||||
|
if(ioctl(comfd,TCSETA,&tty) < 0){
|
||||||
|
/// "îÅ ÍÏÇÕ ÕÓÔÁÎÏ×ÉÔØ ÎÁÓÔÒÏÊËÉ"
|
||||||
|
WARN(_("Can't set settings"));
|
||||||
|
signals(-1);
|
||||||
|
}
|
||||||
|
DBG(" OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from TTY
|
||||||
|
* @param buff (o) - buffer for data read
|
||||||
|
* @param length - buffer len
|
||||||
|
* @return amount of bytes read
|
||||||
|
*/
|
||||||
|
size_t read_tty(char *buff, size_t length){
|
||||||
|
ssize_t L = 0, l;
|
||||||
|
char *ptr = buff;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
int retval;
|
||||||
|
do{
|
||||||
|
l = 0;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(comfd, &rfds);
|
||||||
|
// wait for 100ms
|
||||||
|
tv.tv_sec = 0; tv.tv_usec = 100000;
|
||||||
|
retval = select(comfd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
if (!retval) break;
|
||||||
|
if(FD_ISSET(comfd, &rfds)){
|
||||||
|
if((l = read(comfd, ptr, length)) < 1){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ptr += l; L += l;
|
||||||
|
length -= l;
|
||||||
|
}
|
||||||
|
}while(l);
|
||||||
|
return (size_t)L;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_tty(char *buff, size_t length){
|
||||||
|
ssize_t L = write(comfd, buff, length);
|
||||||
|
if((size_t)L != length){
|
||||||
|
/// "ïÛÉÂËÁ ÚÁÐÉÓÉ!"
|
||||||
|
WARN("Write error");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely convert data from string to double
|
||||||
|
*
|
||||||
|
* @param num (o) - double number read from string
|
||||||
|
* @param str (i) - input string
|
||||||
|
* @return 1 if success, 0 if fails
|
||||||
|
*/
|
||||||
|
int str2double(double *num, const char *str){
|
||||||
|
double res;
|
||||||
|
char *endptr;
|
||||||
|
if(!str) return 0;
|
||||||
|
res = strtod(str, &endptr);
|
||||||
|
if(endptr == str || *str == '\0' || *endptr != '\0'){
|
||||||
|
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ double!"
|
||||||
|
WARNX("Wrong double number format!");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *Flog = NULL; // log file descriptor
|
||||||
|
char *logname = NULL;
|
||||||
|
time_t log_open_time = 0;
|
||||||
|
/**
|
||||||
|
* Try to open log file
|
||||||
|
* if failed show warning message
|
||||||
|
*/
|
||||||
|
void openlogfile(char *name){
|
||||||
|
if(!name){
|
||||||
|
WARNX(_("Need filename"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
green(_("Try to open log file %s in append mode\n"), name);
|
||||||
|
if(!(Flog = fopen(name, "a"))){
|
||||||
|
WARN(_("Can't open log file"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log_open_time = time(NULL);
|
||||||
|
logname = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save message to log file, rotate logs every 24 hours
|
||||||
|
*/
|
||||||
|
int putlog(const char *fmt, ...){
|
||||||
|
if(!Flog) return 0;
|
||||||
|
time_t t_now = time(NULL);
|
||||||
|
if(t_now - log_open_time > 86400){ // rotate log
|
||||||
|
fprintf(Flog, "\n\t\t%sRotate log\n", ctime(&t_now));
|
||||||
|
fclose(Flog);
|
||||||
|
char newname[PATH_MAX];
|
||||||
|
snprintf(newname, PATH_MAX, "%s.old", logname);
|
||||||
|
if(rename(logname, newname)) WARN("rename()");
|
||||||
|
openlogfile(logname);
|
||||||
|
if(!Flog) return 0;
|
||||||
|
}
|
||||||
|
int i = fprintf(Flog, "\n\t\t%s", ctime(&t_now));
|
||||||
|
va_list ar;
|
||||||
|
va_start(ar, fmt);
|
||||||
|
i = vfprintf(Flog, fmt, ar);
|
||||||
|
va_end(ar);
|
||||||
|
fprintf(Flog, "\n");
|
||||||
|
fflush(Flog);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
140
Z1000_focus/usefull_macros.h
Normal file
140
Z1000_focus/usefull_macros.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* usefull_macros.h - a set of usefull macros: memory, color etc
|
||||||
|
*
|
||||||
|
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||||
|
*
|
||||||
|
* 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef __USEFULL_MACROS_H__
|
||||||
|
#define __USEFULL_MACROS_H__
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
|
||||||
|
/*
|
||||||
|
* GETTEXT
|
||||||
|
*/
|
||||||
|
#include <libintl.h>
|
||||||
|
#define _(String) gettext(String)
|
||||||
|
#define gettext_noop(String) String
|
||||||
|
#define N_(String) gettext_noop(String)
|
||||||
|
#else
|
||||||
|
#define _(String) (String)
|
||||||
|
#define N_(String) (String)
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <termio.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
// unused arguments with -Wall -Werror
|
||||||
|
#define _U_ __attribute__((__unused__))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Coloured messages output
|
||||||
|
*/
|
||||||
|
#define RED "\033[1;31;40m"
|
||||||
|
#define GREEN "\033[1;32;40m"
|
||||||
|
#define OLDCOLOR "\033[0;0;0m"
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ERROR/WARNING messages
|
||||||
|
*/
|
||||||
|
extern int globErr;
|
||||||
|
extern void signals(int sig);
|
||||||
|
#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); signals(9);}while(0)
|
||||||
|
#define ERRX(...) do{globErr=0; _WARN(__VA_ARGS__); signals(9);}while(0)
|
||||||
|
#define WARN(...) do{globErr=errno; _WARN(__VA_ARGS__);}while(0)
|
||||||
|
#define WARNX(...) do{globErr=0; _WARN(__VA_ARGS__);}while(0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* print function name, debug messages
|
||||||
|
* debug mode, -DEBUG
|
||||||
|
*/
|
||||||
|
#ifdef EBUG
|
||||||
|
#define FNAME() fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__)
|
||||||
|
#define DBG(...) do{fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fprintf(stderr, "\n");} while(0)
|
||||||
|
#else
|
||||||
|
#define FNAME() do{}while(0)
|
||||||
|
#define DBG(...) do{}while(0)
|
||||||
|
#endif //EBUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory allocation
|
||||||
|
*/
|
||||||
|
#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type)))
|
||||||
|
#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type)))
|
||||||
|
#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0)
|
||||||
|
|
||||||
|
#ifndef DBL_EPSILON
|
||||||
|
#define DBL_EPSILON (2.2204460492503131e-16)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
double dtime();
|
||||||
|
|
||||||
|
// functions for color output in tty & no-color in pipes
|
||||||
|
extern int (*red)(const char *fmt, ...);
|
||||||
|
extern int (*_WARN)(const char *fmt, ...);
|
||||||
|
extern int (*green)(const char *fmt, ...);
|
||||||
|
void * my_alloc(size_t N, size_t S);
|
||||||
|
void initial_setup();
|
||||||
|
|
||||||
|
// mmap file
|
||||||
|
typedef struct{
|
||||||
|
char *data;
|
||||||
|
size_t len;
|
||||||
|
} mmapbuf;
|
||||||
|
mmapbuf *My_mmap(char *filename);
|
||||||
|
void My_munmap(mmapbuf *b);
|
||||||
|
|
||||||
|
void restore_console();
|
||||||
|
void setup_con();
|
||||||
|
int read_console();
|
||||||
|
int mygetchar();
|
||||||
|
|
||||||
|
void restore_tty();
|
||||||
|
void tty_init(char *comdev);
|
||||||
|
size_t read_tty(char *buff, size_t length);
|
||||||
|
int write_tty(char *buff, size_t length);
|
||||||
|
|
||||||
|
int str2double(double *num, const char *str);
|
||||||
|
|
||||||
|
void openlogfile(char *name);
|
||||||
|
int putlog(const char *fmt, ...);
|
||||||
|
#endif // __USEFULL_MACROS_H__
|
||||||
Loading…
x
Reference in New Issue
Block a user