diff --git a/asyn/Makefile b/asyn/Makefile index 889eff333..be3ae156d 100644 --- a/asyn/Makefile +++ b/asyn/Makefile @@ -12,8 +12,9 @@ include $(TOP)/configure/CONFIG ASYN = $(TOP)/asyn #USR_CFLAGS += -DDEBUG -USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET +USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET USR_CPPFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET +USR_CXXFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET -DUSE_TYPED_DRVET --std=c++11 USR_INCLUDES_cygwin32 += -I/usr/include/tirpc @@ -149,18 +150,18 @@ ifneq ($(EPICS_LIBCOM_ONLY),YES) INSTALL_DBDS += $(INSTALL_DBD)/devEpics.dbd endif SRC_DIRS += $(ASYN)/devEpics - devEpics_DBD += devAsynOctet.dbd - devEpics_DBD += devAsynInt32.dbd - devEpics_DBD += devAsynInt8Array.dbd - devEpics_DBD += devAsynInt16Array.dbd - devEpics_DBD += devAsynInt32Array.dbd - devEpics_DBD += devAsynInt32TimeSeries.dbd - devEpics_DBD += devAsynUInt32Digital.dbd - devEpics_DBD += devAsynFloat64.dbd - devEpics_DBD += devAsynFloat32Array.dbd - devEpics_DBD += devAsynFloat64Array.dbd - devEpics_DBD += devAsynFloat64TimeSeries.dbd - devEpics_DBD += devAsynRecord.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynOctet.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt32.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt8Array.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt16Array.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt32Array.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt32TimeSeries.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynUInt32Digital.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynFloat64.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynFloat32Array.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynFloat64Array.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynFloat64TimeSeries.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynRecord.dbd DB += asynInt32TimeSeries.db DB += asynFloat64TimeSeries.db INC += asynEpicsUtils.h @@ -190,7 +191,7 @@ ifneq ($(EPICS_LIBCOM_ONLY),YES) # 3.15 and above support lsi, lso and printf records ifdef BASE_3_15 USR_CFLAGS += -DHAVE_LSREC - devEpics_DBD += devAsynOctetLs.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynOctetLs.dbd endif # do we have EPICS calc module (scalcout etc.) @@ -204,20 +205,20 @@ ifneq ($(EPICS_LIBCOM_ONLY),YES) ifdef HAVE_DEVINT64 USR_CFLAGS += -DHAVE_DEVINT64 USR_CXXFLAGS += -DHAVE_DEVINT64 - devEpics_DBD += devAsynInt64.dbd - devEpics_DBD += devAsynInt64Array.dbd - devEpics_DBD += devAsynInt64TimeSeries.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt64.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt64Array.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt64TimeSeries.dbd asyn_SRCS += devAsynInt64Array.c asyn_SRCS += devAsynInt64TimeSeries.c endif - devEpics_DBD += devAsynInt64Misc.dbd + devEpics_DBD += $(INSTALL_DBD)/devAsynInt64Misc.dbd asyn_SRCS += devAsynInt64.c DBD += $(devEpics_DBD) SRC_DIRS += $(ASYN)/asynRecord DBDINC += asynRecord - DBD += devAsynRecord.dbd + DBD += $(INSTALL_DBD)/devAsynRecord.dbd asyn_SRCS += asynRecord.c asyn_SRCS += drvAsyn.c DB += asynRecord.db @@ -261,6 +262,18 @@ ifeq ($(DRV_USBTMC),YES) DBD += drvAsynUSBTMC.dbd endif + +ifeq ($(DRV_HISLIP),YES) + USR_CXXFLAGS += -fpermissive + SRC_DIRS += $(ASYN)/drvAsynHiSLIP + SRC_DIRS += $(ASYN)/drvAsynHiSLIP/cPyHiSLIP + asyn_SRCS += drvAsynHiSLIP.cpp + asyn_SRCS += HiSLIPMessage.cpp + DBD += drvAsynHiSLIP.dbd + INC += drvAsynHiSLIP.h + INC += HiSLIPMessage.h +endif + ifeq ($(DRV_FTDI),YES) SRC_DIRS += $(ASYN)/drvAsynFTDI asyn_SRCS += ftdiDriver.cpp drvAsynFTDIPort.cpp diff --git a/asyn/devGpib/devGpib.dbd b/asyn/devGpib/devGpib.dbd index 2dbbe59a8..7190b542c 100644 --- a/asyn/devGpib/devGpib.dbd +++ b/asyn/devGpib/devGpib.dbd @@ -1 +1 @@ -device(ai,GPIB_IO,devGpib,"GPIB init/report") +device(ai, GPIB_IO, devGpib,"GPIB init/report") diff --git a/asyn/drvAsynHiSLIP/Makefile b/asyn/drvAsynHiSLIP/Makefile new file mode 100644 index 000000000..ed03db663 --- /dev/null +++ b/asyn/drvAsynHiSLIP/Makefile @@ -0,0 +1,12 @@ +#!make + +.phony: markdown +.SUFFIXES : .rst .md .txt + +.rst.md: + /usr/local/bin/pandoc -f rst -t markdown -o $@ $< + +.rst.txt: + /usr/local/bin/pandoc -f rst -t plain -o $@ $< + +markdown: README.md README.txt diff --git a/asyn/drvAsynHiSLIP/README.md b/asyn/drvAsynHiSLIP/README.md new file mode 100644 index 000000000..837701c1a --- /dev/null +++ b/asyn/drvAsynHiSLIP/README.md @@ -0,0 +1,90 @@ +EPICS ASYN support for HiSLIP device +==================================== + +What is this? +------------- + +This module provides EPICS asyn Octet driver for HiSLIP protocol. + +It should work with Stream Device library as sameway as USBTMC and +VXI11. SRQ is supported also throgh asyn octet driver. + +HiSLIP protocol is implemented in cPyHiSLIP/HiSLIPMessage.{cpp,h} files. +These files does not have direct dependency on EPICS, and can be used +with any other programs. As an example, cPyHiSLIP/cPyHiSLIP.{pxd,pyx} +and setup.py are provided to build python modules for HiSLIP device. + +This moduled is based on the specification descibed in: + +> IVI-6.1: IVI High-Speed LAN Instument Protocol (Rev. 1.1, Feb.24, +> 2011)[^1] + +How to use +---------- + +### For EPICS + +It is assumed that this module will be used togather with the Stream +Deivce. You may need to adjust configure/RELEASE adn +configure/CONFIG\_SITE files for your environment. In some Linux +systems, you need to turn on TIRPC switch to true in +configure/CONFIG\_SITE. + +To add this drvAsynHiSLIP support into StreamApp. After building a +libasyn library with drvAsynHiSLIP, you must add the following line to +the Makefile in the StreamApp directory: + +> streamApp\_DBD += drvAsynHiSLIP.dbd + +In the iocsh start up command, you neeed to configure asyn port for +HiSLIP device. The \"HiSLIPConfigure\" command, like \"vxi11Configure\" +for VXI-11 devices is provided.: + +> \# HiSLIPConfigure \"port name\", \"host address\", +> max\_message\_size, \"priority\" HiSLIPConfigure +> \"L0\",\"172.28.68.228\", 1048560, 0 \# Keysight DSOX1204A + +A port name can be any ID string if you just uses Stream driver. +However, if you may want to use SRQ with this suppor, you better to +stick with devGPIB/asyn port name convention, i.e. \"L\\". + +### As Python module + +At first, you need to build and install Python module based on this +library. To do so, go to the cPyHiSLIP directory under +asyn/drvAsynHiSLIP directory, then issue: + +> python3 -m pip build clean install + +You must have pip module and Cython tool installed. You might need to +give appropriate priviledge to install the module in the proper +location. + +Build Issues +------------ + +> HiSLIP portocol is implemented in HiSPLIPMessage.{cpp,h}. cPyHiSLIP +> provides python module to access HiSLIP devices. It uses cython to +> wrap c/c++ librarlies. It can be used without any componets from +> EPICS. drvAsynHiSLIP provide asyOctet interface to access device. +> SRQ(IO Intr) is implemented also as asynOcted driver. So, only SI +> record can be used with SRQ. + +Acknowledgements +---------------- + +This is based on USB TMC support by Eric Norum \<\>. +And also PyHiSLIP modules by Levshinovskiy +Mikhail() is used as a reference +for the implementation. + +LXI Ports, Protocols, and Services + + + +LXI\_HiSLIP\_Extended\_Function\_Test\_Procedures\_v1\_01.pdf + +[^1]: The latest version IVI-6.1 Rev.2.0 was publised in Feb.23, 2020. + New features, \"Encrypted connections\" and \"Client and server + authentication\" are not supported by this version of drAsynHiSLIP + library. diff --git a/asyn/drvAsynHiSLIP/README.rst b/asyn/drvAsynHiSLIP/README.rst new file mode 100644 index 000000000..3569b7a66 --- /dev/null +++ b/asyn/drvAsynHiSLIP/README.rst @@ -0,0 +1,90 @@ +=============================================================================== +EPICS ASYN support for HiSLIP device +=============================================================================== + +What is this? +================== +This module provides EPICS asyn Octet driver for HiSLIP protocol. + +It should work with Stream Device library as sameway as USBTMC and VXI11. +SRQ is supported also throgh asyn octet driver. + + +HiSLIP protocol is implemented in cPyHiSLIP/HiSLIPMessage.{cpp,h} files. +These files does not have direct dependency on EPICS, and can be used with any other +programs. As an example, cPyHiSLIP/cPyHiSLIP.{pxd,pyx} and setup.py are provided to +build python modules for HiSLIP device. + +This moduled is based on the specification descibed in: + + IVI-6.1: IVI High-Speed LAN Instument Protocol (Rev. 1.1, Feb.24, 2011)[#]_ + + +.. [#] The latest version IVI-6.1 Rev.2.0 was publised in Feb.23, 2020. New features, "Encrypted connections" and "Client and server authentication" are not supported by this version of drAsynHiSLIP library. + + +How to use +============= + +For EPICS +-------------- + + + +It is assumed that this module will be used togather with the Stream Deivce. +You may need to adjust configure/RELEASE adn configure/CONFIG_SITE files +for your environment. In some Linux systems, you need to turn on TIRPC switch to true +in configure/CONFIG_SITE. + +To add this drvAsynHiSLIP support into StreamApp. After building a libasyn library +with drvAsynHiSLIP, you must add the following +line to the Makefile in the StreamApp directory: + + streamApp_DBD += drvAsynHiSLIP.dbd + +In the iocsh start up command, you neeed to configure asyn port +for HiSLIP device. The "HiSLIPConfigure" command, like "vxi11Configure" for +VXI-11 devices is provided.: + + # HiSLIPConfigure "port name", "host address", max_message_size, "priority" + HiSLIPConfigure "L0","172.28.68.228", 1048560, 0 # Keysight DSOX1204A + +A port name can be any ID string if you just uses Stream driver. +However, if you may want to use SRQ with this suppor, you better to +stick with devGPIB/asyn port name convention, i.e. "L". + + +As Python module +---------------------- +At first, you need to build and install Python module based on this library. +To do so, go to the cPyHiSLIP directory under asyn/drvAsynHiSLIP directory, then +issue: + + python3 -m pip build clean install + +You must have pip module and Cython tool installed. You might need to give +appropriate priviledge to install the module in the proper location. + + + + +Build Issues +============ + HiSLIP portocol is implemented in HiSPLIPMessage.{cpp,h}. + cPyHiSLIP provides python module to access HiSLIP devices. It uses cython to wrap c/c++ librarlies. + It can be used without any componets from EPICS. + drvAsynHiSLIP provide asyOctet interface to access device. SRQ(IO Intr) is implemented also as asynOcted driver. So, only SI record can be used with SRQ. + +Acknowledgements +================ +This is based on USB TMC support by Eric Norum . +And also PyHiSLIP modules by Levshinovskiy Mikhail(https://github.com/llemish/PyHiSLIP) is used +as a reference for the implementation. + + +LXI Ports, Protocols, and Services + +https://www.lxistandard.org/About/LXI-Protocols.aspx + + +LXI_HiSLIP_Extended_Function_Test_Procedures_v1_01.pdf diff --git a/asyn/drvAsynHiSLIP/README.txt b/asyn/drvAsynHiSLIP/README.txt new file mode 100644 index 000000000..026e45b7b --- /dev/null +++ b/asyn/drvAsynHiSLIP/README.txt @@ -0,0 +1,83 @@ +EPICS ASYN support for HiSLIP device + +What is this? + +This module provides EPICS asyn Octet driver for HiSLIP protocol. + +It should work with Stream Device library as sameway as USBTMC and +VXI11. SRQ is supported also throgh asyn octet driver. + +HiSLIP protocol is implemented in cPyHiSLIP/HiSLIPMessage.{cpp,h} files. +These files does not have direct dependency on EPICS, and can be used +with any other programs. As an example, cPyHiSLIP/cPyHiSLIP.{pxd,pyx} +and setup.py are provided to build python modules for HiSLIP device. + +This moduled is based on the specification descibed in: + + IVI-6.1: IVI High-Speed LAN Instument Protocol (Rev. 1.1, Feb.24, + 2011)[1] + +How to use + +For EPICS + +It is assumed that this module will be used togather with the Stream +Deivce. You may need to adjust configure/RELEASE adn +configure/CONFIG_SITE files for your environment. In some Linux systems, +you need to turn on TIRPC switch to true in configure/CONFIG_SITE. + +To add this drvAsynHiSLIP support into StreamApp. After building a +libasyn library with drvAsynHiSLIP, you must add the following line to +the Makefile in the StreamApp directory: + + streamApp_DBD += drvAsynHiSLIP.dbd + +In the iocsh start up command, you neeed to configure asyn port for +HiSLIP device. The "HiSLIPConfigure" command, like "vxi11Configure" for +VXI-11 devices is provided.: + + # HiSLIPConfigure "port name", "host address", max_message_size, + "priority" HiSLIPConfigure "L0","172.28.68.228", 1048560, 0 # Keysight + DSOX1204A + +A port name can be any ID string if you just uses Stream driver. +However, if you may want to use SRQ with this suppor, you better to +stick with devGPIB/asyn port name convention, i.e. "L". + +As Python module + +At first, you need to build and install Python module based on this +library. To do so, go to the cPyHiSLIP directory under +asyn/drvAsynHiSLIP directory, then issue: + + python3 -m pip build clean install + +You must have pip module and Cython tool installed. You might need to +give appropriate priviledge to install the module in the proper +location. + +Build Issues + + HiSLIP portocol is implemented in HiSPLIPMessage.{cpp,h}. cPyHiSLIP + provides python module to access HiSLIP devices. It uses cython to + wrap c/c++ librarlies. It can be used without any componets from + EPICS. drvAsynHiSLIP provide asyOctet interface to access device. + SRQ(IO Intr) is implemented also as asynOcted driver. So, only SI + record can be used with SRQ. + +Acknowledgements + +This is based on USB TMC support by Eric Norum . And +also PyHiSLIP modules by Levshinovskiy +Mikhail(https://github.com/llemish/PyHiSLIP) is used as a reference for +the implementation. + +LXI Ports, Protocols, and Services + +https://www.lxistandard.org/About/LXI-Protocols.aspx + +LXI_HiSLIP_Extended_Function_Test_Procedures_v1_01.pdf + +[1] The latest version IVI-6.1 Rev.2.0 was publised in Feb.23, 2020. New +features, "Encrypted connections" and "Client and server authentication" +are not supported by this version of drAsynHiSLIP library. diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/.gitignore b/asyn/drvAsynHiSLIP/cPyHiSLIP/.gitignore new file mode 100644 index 000000000..060bf7115 --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/.gitignore @@ -0,0 +1,21 @@ +/build/ +/dist/ +/cfg/ +/bin/ +/lib/ +/db/ +/dbd/ +/html/ +/include/ +/templates/ +/configure/*.local +*_2.* +*_3.* +*~ +O.* +*.swp +*_BAK.adl +/QtC-* +cdCommands +envPaths +dllPath.bat diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPFunctionTest.py b/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPFunctionTest.py new file mode 100644 index 000000000..1f6b56bfe --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPFunctionTest.py @@ -0,0 +1,174 @@ +#!python3 +# -*- coding:utf-8 -*- +""" +LXI HiSLIP Test Procedures +LXI HiSLIP Extended Function +Revision 1.01 +20 October, 2011 Edition +""" +import unittest + +import os,sys,time +import subprocess + +from cPyHiSLIP import HiSLIP + +class HiSLIPFunctionTestMethods(unittest.TestCase): + """ + ESE 0:OPC/(1:Reqest Control)/2:Query Error/3:Dev.Dep.Error/4:Execution Error/5:Command Error/(6:User Request)/7:PowerOn + ESE=32=0x20 : Command Error + SRE/STB bit 4(0x10):Message Avairlable bit 5(0x20):ESB bit, bit 6(0x40) RQS + + bit 2:0x04 Error/Event Available for some device. or MSG + bit 3:Questionable Data + https://studfile.net/preview/6372846/page:24/ + """ + + Query1=b"*IDN?" + Query2=b"*OPC?" + EnableSRQ=b"*ESE 32;*SRE 48;" # command Error , MAV/ESB + + @classmethod + def setUpClass(cls): + #cls.hostname=b"172.28.68.228" # Keysight + #cls.hostname=b"169.254.29.115" # Keysight + cls.hostname=b"169.254.100.192" # Kikusui + + def setUp(self): + self.dev=HiSLIP() + self.dev.connect(self.hostname) + + def tearDown(self): + self.dev.device_clear(0) + del self.dev + time.sleep(3) + + def test_connection(self): + print("test_connection") + self.dev.write(self.Query1) + self.assertTrue(self.dev.read() != None) + print("end of test_connection") + return 0 + + def test_SRQ_status_byte(self): + print("test_SRQ_status_byte") + print("test_SRQ_status_byte:EnableSRQ") + self.dev.write(self.EnableSRQ) + print("test_SRQ_status_byte:sendQuery{}".format(self.Query2)) + self.dev.write(self.Query2) + print("test_SRQ_status_byte:waitSRQ") + self.dev.wait_Service_Request(10000) + # print("test_SRQ_status_byte:checkSRQ") + # self.dev.check_SRQ() + print("test_SRQ_status_byte:status_Query") + st=self.dev.status_query() + print ("Status Query:",hex(st),hex(st&0x70)) + self.assertEqual(st & 0x70, 80) # SRQ(64)+ MAV(16) + print("test_SRQ_status_byte:readResutl") + r=self.dev.read() + print("test_SRQ_status_byte:readResutl {}".format(r)) + print("test_SRQ_status_byte:statusQuery") + st=self.dev.status_query() + self.assertEqual(st &0x70, 0) + print("end test_SRQ_status_byte") + return 0 + + def test_lock_info(self): + print("test_lock_info") + ret=self.dev.request_lock(b"shared") # shared lock + self.assertEqual(ret,1) + info=self.dev.lock_info() + self.assertEqual(info, (0,1)) + + ret=self.dev.request_lock()# get exclusive lock + self.assertEqual(ret,1) + + info=self.dev.lock_info() + self.assertEqual(info, (1,1)) + + ret=self.dev.request_lock()# try to get lock already locked + self.assertEqual(ret,3) # error! + + info=self.dev.lock_info() + self.assertEqual(info, (1,1)) + + ret=self.dev.release_lock() # release exclusive lock + self.assertEqual(ret,1) + info=self.dev.lock_info() + self.assertEqual(info, (0,1)) + + ret=self.dev.release_lock() # release shared lock + self.assertEqual(ret,2) + info=self.dev.lock_info() + self.assertEqual(info, (0,0)) + print("end test_lock_info") + return 0 + + def test_InterruptedMode(self): + print("test_InterruptedMode") + idn=self.dev.ask(self.Query1) + self.dev.device_clear(0) + time.sleep(1) + if self.dev.get_overlap_mode() == 0: + self.dev.write(self.Query1) + self.dev.write(self.Query2) + r=self.dev.read() + self.assertEqual(int(r[1]), 1) + print("end test_Interrupted") + return 0 + + def test_OverlappedMode(self): + print("test_OverlappedMode") + idn=self.dev.ask(self.Query1) + self.dev.device_clear(1) + time.sleep(1) + if self.dev.get_overlap_mode() == 1: + self.dev.write(self.Query1) + self.dev.write(self.Query2) + self.assertEqual(self.dev.read()[1], idn) + self.assertEqual(int(self.dev.read()[1]), 1) + print("end test_OverlappedMode") + return 0 + + + # def test_locking(self): + # def check1(): + # self.dev.request_lock() + # c.subprocess( + # del dev + # dev=HiSLIP(self.hostname) + # self.dev.write(Query1) + # self.dev.read() + # self.dev.status_query() + # else: + # os.wait() + + def test_device_clear(self): + print("test_device_clear") + print("device_clear:Query") + self.dev.write(self.Query1) + print("device_clear",self.dev.read()) + self.dev.device_clear(0) + print("device_clear done") + time.sleep(1) + with self.assertRaises(RuntimeError): + self.dev.read(timeout=3000) + print("end test_device_clear") + return 0 + +def suite(): + suite = unittest.TestSuite() + suite.addTest(HiSLIPFunctionTestMethods("test_connection")) + suite.addTest(HiSLIPFunctionTestMethods("test_device_clear")) + suite.addTest(HiSLIPFunctionTestMethods("test_SRQ_status_byte")) + # suite.addTest(HiSLIPFunctionTestMethods("test_OverlappedMode")) + # suite.addTest(HiSLIPFunctionTestMethods("test_lock_info")) + # suite.addTest(HiSLIPFunctionTestMethods("test_InterruptedMode")) + return suite + +if __name__ == '__main__': + #unittest.main() + runner = unittest.TextTestRunner() + runner.run(suite()) + + diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPMessage.cpp b/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPMessage.cpp new file mode 100644 index 000000000..47aef56a7 --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPMessage.cpp @@ -0,0 +1,1203 @@ +#include "HiSLIPMessage.h" + +using nsHiSLIP::CC_request_t; +using nsHiSLIP::CC_Lock_t; +using nsHiSLIP::HiSLIP_t; +using nsHiSLIP::HiSLIP; +using nsHiSLIP::Message_t; + +#define MAX_PAYLOAD_CAPACITY 4096 +#define IDSTRING_CAPACITY 100 +#define MAX_ERRMSG_CAPACITY 1024 + +#define SUCCESS 0 +#define FAIL -1 + +// HiSLIP methods +namespace nsHiSLIP{ + + ssize_t Header::recv(int socket, void *buffer){ + ssize_t rsize; + + //rsize= ::recv(socket, buffer, HEADER_SIZE, 0); + //rsize= ::read(socket, buffer, HEADER_SIZE); + rsize = ::recvfrom(socket, buffer, HEADER_SIZE, 0, nullptr, nullptr); + if (rsize < HEADER_SIZE){ + //raise exception? + throw Error_t("Too Short Header in HiSLIP Message"); + } + + if (memcmp(this->prologue, buffer, 2) != 0){ + return -1; + } + + this->message_type=*((u_int8_t *) ((char *) buffer+2)); + this->control_code=*((u_int8_t *) ((char *) buffer+3)); + this->message_parameter.word = ntohl(*(u_int32_t *) ((char *) buffer+4)); + this->payload_length = be64toh(*(u_int64_t *) ((char *) buffer+8)); + + assert(rsize == HEADER_SIZE); + return rsize; // should be HEADER_SIZE + } + + ssize_t Message::recv(int socket, Message_Types_t expected_message_type){ + size_t rsize; + size_t status; + + status=this->Header::recv(socket); + if (status < 0){ + // Error! + return status; + } + + // now prepare for a payload. + if (this->payload == NULL && this->payload_length > 0){ + this->payload = (void *) calloc(this->payload_length,1); + if (this->payload == NULL){ + perror("faile to allocate memory for payload."); + throw std::runtime_error("faile to allocate memory for payload."); + //return -1; + } + //this->clean_on_exit=1; + this->clean_on_exit=0; + } + + rsize=0; //returns size of recieved payload. should be same as payload_length + if (this->payload_length > 0){ + size_t bytestoread=this->payload_length; + + //::printf("reading data payload 0x%p, length:%zu recvd:%zu\n", this->payload, bytestoread, rsize); + while (bytestoread){ + status = ::recvfrom(socket, ((u_int8_t *)this->payload+rsize), + bytestoread, 0, nullptr, nullptr); + if (status <= 0){ + perror("payload read error:"); + throw std::runtime_error("payload read error"); + return -1; + } + rsize += status; + if (status >= bytestoread){ + break; + } + bytestoread -=status; + } + //::printf("reading data payload 0x%p, length:%zu\n",this->payload, bytestoread); + } + // should be handled in HiSLIP class not in Message class + // handle error / or urgent messages: Error/FatalError /interrupted/AsyncInterrupted/AsyncServiceRequest + // + //this->print("msg::recv:"); // for debug + //::printf("reading data payload 0x%p...\n",this->payload); // for debug + if(this->message_type == nsHiSLIP::FatalError){ + ::printf("Error: %d %s\n", + this->control_code, nsHiSLIP::Fatal_Error_Messages[this->control_code]); + if (this->payload_length >0){ + ::printf("Fatal Error msg: %s\n", (char *) this->payload); + } + //return -(this->control_code+1); + throw FatalError_t((char *) this->payload); + } + else if(this->message_type == nsHiSLIP::Error){ + ::printf("Fatal Error: %d %s\n", + this->control_code, nsHiSLIP::Error_Messages[this->control_code]); + if (this->payload_length >0){ + ::printf("Error msg: %s\n", (char *) this->payload); + } + //return -(this->control_code+2); + throw HiSLIP_Error((char *) this->payload); + } + else if( (this->message_type == nsHiSLIP::Interrupted) || + (this->message_type == nsHiSLIP::AsyncInterrupted)) + { + ::printf("Interrupted %d %s\n", + this->control_code, nsHiSLIP::Fatal_Error_Messages[this->control_code]); + throw HiSLIP_Interrupted((char *) this->payload); + } + else if(this->message_type == nsHiSLIP::AsyncServiceRequest){ + //::printf("SRQ received : %d\n", this->control_code); + throw SRQ_t("Service Request"); + //return -(this->control_code); + } + // check if expected type or not + if((expected_message_type == AnyMessages) || + (expected_message_type == this->message_type)){ + return 0; + } + // for debug + //this->print("msg:recv failed"); + throw std::runtime_error("msg:recv failed"); + return -1; + } + + void HiSLIP::connect(const char *hostname, + const char *dev_name, + const int port //, + //const char vendor_id[2] + ){ + int status; + //const char vendor_id[]=Default_vendor_id; + + struct addrinfo hints, *res=NULL; + memset(&hints,0, sizeof(struct addrinfo)); + hints.ai_flags=AI_NUMERICSERV | AI_CANONNAME; + hints.ai_flags=AI_NUMERICSERV; + hints.ai_family=AF_INET; // IPv4 + hints.ai_socktype=SOCK_STREAM; + hints.ai_protocol=0; // any protocol + hints.ai_addrlen=0; + hints.ai_addr=NULL; + hints.ai_canonname=NULL; + hints.ai_next=NULL; + + { + char service[IDSTRING_CAPACITY]={'\x0'}; + if (snprintf(service, IDSTRING_CAPACITY, "%d",port) // "4880" for example. + > 0){ + status=getaddrinfo(hostname, service, &hints, &res); + } + else{ + status=getaddrinfo(hostname, "hislip", &hints, &res); + perror("snprintf failes"); + } + } + + if ((status !=0) || res == NULL){ + char msg[MAX_ERRMSG_CAPACITY]={'\x0'}; + if (snprintf(msg, MAX_ERRMSG_CAPACITY, "getaddrinfo error status:%d res %p",status,res) >0){ + perror(msg); + } + else{ + perror("getaddrinfo"); + } + exit (9999); + } + + this->sync_channel= ::socket(AF_INET, SOCK_STREAM , 0); + this->async_channel=::socket(AF_INET, SOCK_STREAM , 0); + // now Message::send put both header portion and pyload portion as single transaction. So we dont need to use this + // portion of codes(?). +# ifdef USE_TCP_NODELAY + { + // disable the Nagle algorithm on socket to improve responce with small packet size + // this may increase number of small packets. So may degrade overall network performance. + int flag=1, reta, rets; + rets = setsockopt( this->sync_channel, IPPROTO_TCP, TCP_NODELAY, + (char *)&flag, sizeof(flag) ); + reta = setsockopt( this->async_channel, IPPROTO_TCP, TCP_NODELAY, + (char *)&flag, sizeof(flag) ); + if ((rets == -1) || (reta == -1)){ + perror("Couldn't set sockopt(TCP_DELAY).\n"); + } + } +# endif + if (res-> ai_addr == NULL || res->ai_addrlen == 0){ + perror("empty addinfo"); + freeaddrinfo(res); + exit (999); + }; + status = ::connect(this->sync_channel, res->ai_addr, res->ai_addrlen); + if (status!=0){ + // Error handling + //::printf("connection to sync_channel failed\n"); + perror(__FUNCTION__); + throw std::runtime_error("cannot connect to sync_channel"); + } + status = ::connect(this->async_channel, res->ai_addr,res->ai_addrlen); + if (status!=0){ + // Error handling + //::printf("connection to async_channel failed\n"); + perror(__FUNCTION__); + throw std::runtime_error("cannot connect to async_channel"); + } + + freeaddrinfo(res); + + { + Message msg(nsHiSLIP::Initialize, + 0, + message_parameter((u_int16_t) nsHiSLIP::PROTOCOL_VERSION_MAX, + (char *) Default_vendor_id), + (u_int64_t) 0, (u_int8_t *) NULL); + msg.send(this->sync_channel); + } + + { Message resp(AnyMessages); + + int rc=resp.recv(this->sync_channel, nsHiSLIP::InitializeResponse); + if (rc !=0){ + //Error! + throw std::runtime_error("Error to recieve InitializeResponse"); + } + + this->overlap_mode=resp.control_code; + this->session_id=resp.message_parameter.getSessionId(); + this->server_protocol_version=resp.message_parameter.getServerProtocolVersion(); + } + { + Message msg(nsHiSLIP::AsyncInitialize); + msg.message_parameter.word=this->session_id; + msg.send(this->async_channel); + } + + { + Message resp(AnyMessages); + int rc=-1; + try{ + rc=resp.recv(this->async_channel, nsHiSLIP::AsyncInitializeResponse); + } + catch (...){ + ::printf("uncaught exception\n"); + } + + if (rc !=0){ + throw std::runtime_error("Error to recieve AsyncInitializeResponse"); + } + this->server_vendorID=resp.message_parameter.word; + this->overlap_mode=resp.control_code; + } + + //now setup poll object + this->reset_message_id(); + this->rmt_delivered = false; + + this->sync_poll.fd=this->sync_channel; + this->sync_poll.events=POLLIN; + this->sync_poll.revents=0; + + this->async_poll.fd=this->async_channel; + this->async_poll.events=POLLIN; + this->async_poll.revents=0; + + // start a thread to recieve async channel. + this->start_async_receiver_thread(); + this->set_max_size(this->maximum_message_size); + + }; + + long HiSLIP::set_max_size(unsigned long message_size){ + //this routine may be called before starting async_server . + //Message resp(AnyMessages); + //int rc=-1; + u_int64_t msg_size=htobe64(message_size); + + Message msg=Message(nsHiSLIP::AsyncMaximumMessageSize, + 0, + message_parameter(0), + sizeof(msg_size), (u_int8_t *) &msg_size); + + auto fut_resp = this->async_send_msg(msg, nsHiSLIP::AsyncMaximumMessageSizeResponse); + // ::printf("wait future\n"); + try{ + Message resp=fut_resp.get(); + // ::printf("got response\n"); + // resp.print("response from future"); + // ::printf("get payload@%p v:%llu\n",resp.payload, be64toh(*((u_int64_t *)(resp.payload)))); + // rc=0; + // ::printf("get payload@%p v:%llu\n",resp.payload, be64toh(*((u_int64_t *)(resp.payload)))); + //The 8-byte buffer size is sent in network order as a 64-bit integer. + this->maximum_message_size=be64toh(*((u_int64_t *)(resp.payload))); + this->maximum_payload_size = this->maximum_message_size - HEADER_SIZE; + //::printf("get max message size %ld\n",this->maximum_message_size); + if (message_size < this->maximum_message_size){ + this->current_message_size=message_size; + this->current_payload_size=message_size - HEADER_SIZE; + } + else{ + this->current_message_size = this->maximum_message_size; + this->current_payload_size = this->maximum_payload_size; + } + //::printf("get max message size %ld\n",this->maximum_message_size); + return this->maximum_message_size; + } + catch (...){ + ::printf("uncaught exception"); + throw std::runtime_error("uncaught exception"); + } + }; + + int HiSLIP::device_clear(u_int8_t feature_request){ + // feature_rueust: bit 0: overlapped(1)/synchronized(0) bit1: encription mode, bit2:Initial Encryption + int ready,rc; + + Message *msg=new Message(nsHiSLIP::AsyncDeviceClear, + 0, + 0, + 0,NULL); + auto fut_resp = this->async_send_msg(*msg, nsHiSLIP::AsyncDeviceClearAcknowledge); + { + Message resp=fut_resp.get(); + //resp.print("received message"); + this->feature_preference=resp.control_code; + } + + // msg->send(this->async_channel); + // this->async_poll.revents=0; + // ready=poll(&this->async_poll, 1, this->socket_timeout); + // if ( ready == 0){ + // return -1; + // } + // try{ + // rc=resp.recv(this->async_channel, nsHiSLIP::AsyncDeviceClearAcknowledge); + // } + // catch (...){ + // ::printf("Uncaught exception"); + // } + // if (rc !=0){ + // // Error! + // } + + // resp.print("received message"); + + // this->feature_preference=resp.control_code; + + { + Message resp(AnyMessages); + msg=new Message(nsHiSLIP::DeviceClearComplete, + feature_request, + 0, + 0, NULL); + //msg->print(); + + msg->send(this->sync_channel); + + ready=poll(&this->sync_poll, 1, this->socket_timeout); + if ( ready == 0){ + return -1; + } + + rc=resp.recv(this->sync_channel, nsHiSLIP::DeviceClearAcknowledge); + + //resp.print("Device Clear Acknowledge"); + + if (rc !=0){ + // Error! + } + + this->overlap_mode=resp.control_code & 0x01; + this->feature_setting = resp.control_code; + } + + this->reset_message_id(); + this->rmt_delivered = false; + + this->clear_srq_stacks(); + + return 0; + }; + + int HiSLIP::status_query(){ + u_int8_t status; + //Message resp(AnyMessages); + Message msg((u_int8_t) nsHiSLIP::AsyncStatusQuery, + (u_int8_t) this->rmt_delivered, + message_parameter((u_int32_t) this->most_recent_message_id), + 0, NULL); + // if (this->overlap_mode){ + // //msg.message_parameter=message_parameter((u_int32_t) this->most_recent_received_message_id); + // msg.message_parameter=message_parameter((u_int32_t) this->most_recent_message_id); + // } + if (this->message_id == 0xffffff00){ + msg.message_parameter=message_parameter((u_int32_t) INITIAL_LAST_MESSAGE_ID); + } + auto fut_resp = this->async_send_msg(msg, nsHiSLIP::AsyncStatusResponse); + + Message resp=fut_resp.get(); + + //resp.print(); + status= resp.control_code & 0xff; + return status; + } + + // long HiSLIP::write(u_int8_t *data_str, long timeout){ + // return this->write(data_str, this->maximum_message_size,timeout); + // }; + + long HiSLIP::write(u_int8_t const* data_str, size_t const dsize, long timeout){ + + size_t max_payload_size = this->maximum_message_size - nsHiSLIP::HEADER_SIZE; + size_t bytestosend=dsize; + const u_int8_t *buffer=data_str; + size_t delivered=0; + size_t count; + + while(bytestosend){ + if (bytestosend < max_payload_size){ + Message msg(nsHiSLIP::DataEnd, + this->rmt_delivered, + nsHiSLIP::message_parameter(this->message_id), + bytestosend, (u_int8_t *) buffer); + buffer += bytestosend; + count=msg.send(this->sync_channel); + this->rmt_delivered=false; + count -=HEADER_SIZE; + bytestosend = 0; + delivered += count ; + } + else{ + Message msg(nsHiSLIP::Data, + this->rmt_delivered, + nsHiSLIP::message_parameter(this->message_id), + max_payload_size, (u_int8_t *) buffer); + count=msg.send(this->sync_channel); + this->rmt_delivered=false; + count -= HEADER_SIZE; + bytestosend -=count; + delivered += count; + buffer += max_payload_size; + } + this->increment_message_id(); + } + return delivered; + }; + + long HiSLIP::read(size_t *received, u_int8_t **buffer, long timeout){ + bool eom=false; + long rstatus=0; + Message resp(AnyMessages); + + *received=0; + this->rmt_delivered = false; + if (*buffer == NULL){ + *buffer=(u_int8_t *) calloc(1,this->current_message_size); + } + if (*buffer == NULL){ + perror("buffer allocation error"); + return -1; + } + while(!eom) { + int ready; + + ready=poll(&this->sync_poll, 1, this->socket_timeout); + if ( ready == 0){ + throw std::runtime_error("HiSLIP::read poll:" "timeout"); + //throw nsHiSLIP::HiSLIP_TimeoutError("HiSLIP::read poll:" "timeout"); + return -1; + } + + rstatus=resp.recv(this->sync_channel); + if (rstatus < 0){ // would be -1 + perror(__FUNCTION__); + throw std::runtime_error("HiSLIP::read recv:" "timeout"); + //throw nsHiSLIP::HiSLIP_RuntimeError(__FUNCTION__); + return -2; + }; + + // ::printf("HiSLIP read() received status:%ld pay_load Size:%llu \n", + // rstatus, resp.payload_length); + + // may not be a good idea to reallocate memory evertime. + // 'message_parameter' should be checkd + // aginst most_recent_message_id and overlap_mode + // + { u_int8_t *newbuf; + + // newbuf=(u_int8_t *) reallocarray(*buffer, 1, + // *received+resp.payload_length); + newbuf = (u_int8_t *) realloc( *buffer, + *received + resp.payload_length); + + if (newbuf == NULL){ + *buffer=NULL; + return -3; + } + else{ + *buffer=newbuf; + } + } + ::memcpy((*buffer + *received), resp.payload, resp.payload_length); + *received +=resp.payload_length; + u_int32_t messageid = resp.message_parameter.word; + + //resp.print(); + + if ( resp.message_type == nsHiSLIP::Data){ + if ( this->overlap_mode || + ( ( messageid == UNKNOWN_MESSAGE_ID) || (messageid == this->most_recent_message_id))){ + this->most_recent_received_message_id=this->most_recent_message_id; + continue; + } + else{ + *received=0; + continue; + } + } else if ( resp.message_type == nsHiSLIP::DataEnd){ + if (( this->overlap_mode || + ( messageid == UNKNOWN_MESSAGE_ID) || (messageid == this->most_recent_message_id))){ + eom=true; + this->rmt_delivered=true; + this->most_recent_received_message_id=this->most_recent_message_id; + return 0; + } + else{ + *received=0; + continue; + } + } else if (( resp.message_type == nsHiSLIP::Interrupted)|| + ( resp.message_type == nsHiSLIP::AsyncInterrupted)){ + if (this->overlap_mode){ + continue; + } + else{ + *received=0; + continue; + } + } else{ + // error unexpected message type. + return -999; + } + } + return -999; + }; + + long HiSLIP::read(size_t *received, + u_int8_t *buffer, size_t bsize, + long timeout){ + bool eom=false; + long rstatus; + + *received=0; + this->rmt_delivered = false; + + if (buffer==NULL || bsize <= 0){ + return -1; + } + if (bsize < this->maximum_payload_size){ + ::printf("HiSLIP buffersize:%ld is smaller than maximum_payload_size:%ld \n ",bsize,this->maximum_payload_size); + } + while(!eom) { + int ready; + Message resp(AnyMessages); + + ready=::poll(&this->sync_poll, 1, timeout); + + if (ready == 0){ + return -1; + } + + rstatus=resp.recv(this->sync_channel); + + // ::printf("HiSLIP read() received status:%ld pay_load Size:%llu buffer size: %ld \n", + // rstatus, resp.payload_length, bsize); + + if (rstatus < 0){ + return -1; + }; + if (( (*received) + resp.payload_length) > bsize){ + ::memcpy( (buffer + *received), resp.payload, (bsize - *received)); + *received = bsize; + return 0; + } + else{ + ::memcpy( (buffer + *received), resp.payload, resp.payload_length); + + *received +=resp.payload_length; + } + u_int32_t messageid = resp.message_parameter.word; + //resp.print(); + if ( resp.message_type == nsHiSLIP::Data){ + if ( this->overlap_mode || + ( ( messageid == UNKNOWN_MESSAGE_ID) || (messageid == this->most_recent_message_id))){ + this->most_recent_received_message_id=this->most_recent_message_id; + continue; + } + else{ + *received=0; + continue; + } + } else if (resp.message_type == nsHiSLIP::DataEnd){ + if (( this->overlap_mode || + ( messageid == UNKNOWN_MESSAGE_ID) || (messageid == this->most_recent_message_id))){ + eom=true; + this->rmt_delivered=true; + this->most_recent_received_message_id=this->most_recent_message_id; + return 0; + } + else{ + *received=0; + continue; + } + } else if (( resp.message_type == nsHiSLIP::Interrupted)|| + ( resp.message_type == nsHiSLIP::AsyncInterrupted)){ + if (this->overlap_mode){ + continue; + } + else{ + *received=0; + continue; + } + } else{ + // resp.print(); + // error unexpected message type. + return -1; + } + } + return -1; + }; + + long HiSLIP::ask(u_int8_t *const data_str, size_t const dsize, + u_int8_t **rbuffer, + long wait_time){ + size_t rsize=-1; + u_int8_t *buffer=NULL; + + this->write(data_str, dsize); + + if(::poll(&this->sync_poll, 1, wait_time) == 0){ + // error + buffer=NULL; + return -1; + }; + + long status=this->read(&rsize, &buffer, wait_time); + + // read will allocate memory area pointed by buffer. + if (status !=0){ + rsize=-1; + } + if (rsize > 0){ + *rbuffer=buffer; + } + else { + if (buffer != NULL) free(buffer); + buffer=NULL; + } + return rsize; + }; + + long HiSLIP::trigger_message(void){ + Message msg(nsHiSLIP::Trigger, + (u_int8_t) this->rmt_delivered, + message_parameter((u_int32_t) this->message_id), + 0, NULL); + msg.send(this->sync_channel); + this->increment_message_id(); + this->rmt_delivered=false; + return 0; + }; + + long HiSLIP::remote_local(u_int8_t request){ + //int rc; + Message msg(nsHiSLIP::AsyncRemoteLocalControl, + request, + message_parameter((u_int32_t) this->most_recent_message_id), + 0, NULL), resp(AnyMessages); + + auto fut_resp = this->async_send_msg(msg, nsHiSLIP::AsyncRemoteLocalResponse); + + try{ + Message resp=fut_resp.get(); + } + catch(...){ + return -1; + } + //resp.print(); + + return 0; + + // msg.send(this->async_channel); + + // try{ + // rc=resp.recv(this->async_channel, nsHiSLIP::AsyncRemoteLocalResponse); + // } + // catch(...){ + // return -1; + // } + + // if (rc !=0){ + // return -1; + // } + return 0; + }; + + long HiSLIP::request_lock(const char* lock_string){ + //int rc; + Message msg(nsHiSLIP::AsyncLock, + 1, // request + this->lock_timeout, + strnlen(lock_string, 256), (u_int8_t *) lock_string); + + auto fut_resp = this->async_send_msg(msg, nsHiSLIP::AsyncLockResponse); + + try{ + Message resp=fut_resp.get(); + //resp.print("reuqest_lock"); + return resp.control_code; + } + catch(...){ + throw std::runtime_error("request lock failed"); + return 3; + } + // msg.send(this->async_channel); + + // try{ + // rc=resp.recv(this->async_channel, nsHiSLIP::AsyncLockResponse); + // } + // catch (...){ + // return -1; + // } + // if (rc !=0){ + // //error! + // return -1; + // } + // return resp.control_code; + }; + + long HiSLIP::release_lock(void){ + //int rc=-1; + long message_id =this->most_recent_message_id; + if ( message_id == nsHiSLIP::INITIAL_MESSAGE_ID){ + message_id=0; + } + Message msg(nsHiSLIP::AsyncLock, + 0, // release + message_id, + 0, NULL); + + auto fut_resp = this->async_send_msg(msg, nsHiSLIP::AsyncLockResponse); + + try{ + Message resp=fut_resp.get(); + //resp.print(); + return resp.control_code; + } + catch(...){ + throw std::runtime_error("request lock failed"); + return 3; + } + // msg.send(this->async_channel); + + // try { + // rc=resp.recv(this->async_channel, nsHiSLIP::AsyncLockResponse); + // } + // catch (...){ + // return -1; + // } + // if(rc != 0){ + // //Error + // return -1; + // } + // return resp.control_code; + }; + + long HiSLIP::lock_info(void){ + u_int8_t lock_exclusive=0; + long lock_shared=0; + //int ready=0, rc=0; + + //Message resp(AnyMessages); + Message msg((u_int8_t) nsHiSLIP::AsyncLockInfo, + 0, + 0, + 0, NULL); + + auto fut_resp = this->async_send_msg(msg, nsHiSLIP::AsyncLockInfoResponse); + + try{ + Message resp=fut_resp.get(); + //resp.print("lock_info"); + lock_exclusive = resp.control_code & 0xff; + lock_shared = resp.message_parameter.word; + return ((lock_shared << 8) & 0xffffffff00 )+ lock_exclusive; + } + catch(...){ + throw std::runtime_error("lock info"); + //return -1; + } + } + + long HiSLIP::request_srq_lock(void){ + // if (sem_wait(&(this->srq_lock)) == 0){ + // return 0; + // } + // else{ + // perror("request_srq_lock"); + // return -1; + // } + if (pthread_mutex_lock(&(this->srq_lock)) == 0){ + return 0; + } + else{ + perror("request_srq_lock"); + throw std::runtime_error("request_srq_lock"); + //return -1; + } + }; + + long HiSLIP::release_srq_lock(void){ + int rc=pthread_mutex_unlock(&(this->srq_lock)); + if ( rc == 0){ + return 0; + } + else if (rc == EPERM){ + return rc; + } + else{ + perror("release_srq_lock"); + throw std::runtime_error("release_srq_lock"); + //return -1; + } + }; + + int HiSLIP::check_srq_lock(void){ + int rc=pthread_mutex_trylock(&(this->srq_lock)); + if (rc == 0){ + pthread_mutex_unlock(&(this->srq_lock)); + return 1; + } + else if (rc == EBUSY){ + return 0; + } + perror("check_srq_lock"); + throw std::runtime_error("check_srq_lock"); + } + + int HiSLIP::check_and_lock_srq_lock(void){ + int rc=pthread_mutex_trylock(&(this->srq_lock)); + switch (rc){ + case 0: + return 1; + case EBUSY: + return 0; + default: + perror("check_and_lock_srq_lock"); + throw std::runtime_error("check_and_lock_srq_lock"); + //return -1; + } + } + // used in asyn/drvAsynHiSLIP : + int HiSLIP::get_Service_Request(void){ + // register promise for SRT and wait SRQ using future. + if (! this->srq_msg_stack.empty()){ + Message msg=this->pop_srq_msg(); + return msg.control_code; + } + else{ + std::promise p; + std::future f = p.get_future(); + this->push_srq_promise(std::move(p)); + Message msg=f.get(); + return msg.control_code; + } + + + // Message *msg=new Message(nsHiSLIP::AnyMessages); + // long status; + + // try{ + // status= msg->recv(this->async_channel, nsHiSLIP::AnyMessages);// Service Request cause SRQ_t exception. + // msg->print(); // other async response should be handled separately. + // return status; + // } + // catch(SRQ_t &e){ // Recieved SRT + // // for debug + // msg->print(); + // //if ((status & 0x80000000) != 0){ + // if (status < 0){ + // // should handle Error/Fatal Error/Async Interrupted messages. + // perror(__FUNCTION__); + // msg->print("SRQ handler:"); + // return 0; + // } + // else{ + // this->release_srq_lock(); + // return msg->control_code; + // } + // } + // catch(...){ + // return -1; + // } + }; + // + void HiSLIP::register_async_promise( + Message_Types_t responseType, + std::promise p ){ + + std::lock_guard guard(this->promise_map_mutex);//create lock==lock mutex + if (this->promise_map.count(responseType) == 0){ + this->promise_map.emplace(responseType, std::move(p)); + } + else{ + this->promise_map[responseType]= std::move(p); + } + }; + + void HiSLIP::remove_async_promise(Message_Types_t responseType){ + std::lock_guard guard(this->promise_map_mutex);//create lock==lock mutex + this->promise_map.erase(responseType); + }; + // + void HiSLIP::push_srq_promise(std::promise p){ + std::lock_guard guard(this->srq_promise_mutex);//create lock==lock mutex + this->srq_promise_stack.push(std::move(p)); + }; + + std::promise HiSLIP::pop_srq_promise(void){ + std::lock_guard guard(this->srq_promise_mutex);//create lock==lock mutex + std::promise msg; + msg=std::move(this->srq_promise_stack.top()); this->srq_promise_stack.pop(); + return msg; + + }; // use .top() and .pop() of stack class. + void HiSLIP::clear_srq_promise(void){ + std::lock_guard guard(this->srq_promise_mutex);//create lock==lock mutex + while(! this->srq_promise_stack.empty()){ + this->srq_promise_stack.pop(); + } + }; + // + void HiSLIP::push_srq_msg(Message msg){ + std::lock_guard guard(this->srq_msg_mutex);//create lock==lock mutex + this->srq_msg_stack.push(std::move(msg)); + }; + Message HiSLIP::pop_srq_msg(void){ + std::lock_guard guard(this->srq_msg_mutex);//create lock==lock mutex + Message msg=this->srq_msg_stack.top(); this->srq_msg_stack.pop(); + return msg; + }; // use .top() and .pop() of stack class. + void HiSLIP::clear_srq_msg(void){ + std::lock_guard guard(this->srq_msg_mutex);//create lock==lock mutex + while(! this->srq_msg_stack.empty()){ + this->srq_msg_stack.pop(); + } + }; + void HiSLIP::clear_srq_stacks(void){ + auto futprm = std::async(std::launch::async, [this]{ this->clear_srq_promise();} ); + auto futmsg = std::async(std::launch::async, [this]{ this->clear_srq_msg();} ); + }; +// + std::future HiSLIP::async_send_msg(Message msg, + Message_Types_t responseType) + { + std::promise p; + std::future f = p.get_future(); + + //::printf("sending async message\n"); + + msg.send(this->async_channel); + //::printf("message sent\n"); + + if (responseType != AnyMessages) { + this->register_async_promise(responseType, std::move(p)); + this->rmt_delivered = false; + } + else{ + ::printf("no response is expected\n"); + p.set_value(std::move(msg)); + //p.set_value(msg); + } + //::printf("return future\n"); + return f; + }; + + void HiSLIP::start_async_receiver_thread(void){ + + // use lambda expression. + this->async_receiver_thread= new std::thread([this](){this->async_receiver_loop();} ); + this->async_recceiver_active=true; + }; + + void HiSLIP::async_receiver_loop(void){ + // should be executed in other thread. + using FpMilliseconds = + std::chrono::duration; + + static_assert(std::chrono::treat_as_floating_point::value, + "Rep required to be floating point"); + + Message *msg=new Message(nsHiSLIP::AnyMessages); + long status; + + while (this->async_recceiver_active){ + // Note that implicit conversion is allowed here + this->async_poll.revents=0; + //::poll(&this->async_poll, 1, -1 /* forever*/ ); + { // with time limit to wait. + int rc=::poll(&this->async_poll, 1, 500 /* m sec*/ ); + if ( rc == 0){ /* time limit expires */ + continue; + } + else if (rc < 0){ // Error EBADF/EFAULT/EINTR/EINVAL/ENOMEM + //::printf("return code : %d\n",rc); + ::perror(__FUNCTION__); + //continue; + throw std::runtime_error(__FUNCTION__); + } + }; + //::printf("receiver got data\n"); //for debug + try{ + status = msg->recv(this->async_channel, nsHiSLIP::AnyMessages); + } + catch(SRQ_t &e){ + if(this->srq_promise_stack.empty()){ + this->push_srq_msg(std::move(msg)); + } + else{ + std::promise p = this->pop_srq_promise(); + p.set_value(std::move(msg)); + } + this->release_srq_lock(); + } + catch (FatalError_t &e){ + this->Fatal_Error_handler(msg); + } + catch (Error_t &e){ + this->Error_handler(msg); + } + catch (Interrupted_t &e){ + this->Error_handler(msg); + } + catch(...){ + ::printf("uncahugt exception, ignored"); + } + //msg->print(); // for debug + + switch (msg->message_type ){ + case nsHiSLIP::AsyncLockResponse: + case nsHiSLIP::AsyncLockInfoResponse: + case nsHiSLIP::AsyncRemoteLocalResponse: + case nsHiSLIP::AsyncDeviceClearAcknowledge: + case nsHiSLIP::AsyncMaximumMessageSizeResponse: + case nsHiSLIP::GetDescriptorsResponse: + case nsHiSLIP::AsyncStatusResponse: + case nsHiSLIP::AsyncStartTLSResponse: + case nsHiSLIP::AsyncEndTLSResponse: + //msg->print("Asycn Responce"); // for debug + if (this->promise_map.count(msg->message_type) ==0){ + msg->print("No promise"); // for debug + } + else{ + // get promise and set message to promise + this->promise_map[msg->message_type].set_value(std::move(*msg)); + this->remove_async_promise(msg->message_type); + } + break; + case nsHiSLIP::AsyncInitializeResponse://should be handleld in connect. + break; + case nsHiSLIP::FatalError: + case nsHiSLIP::Error: + case nsHiSLIP::AsyncInterrupted: + case nsHiSLIP::AsyncServiceRequest: + // should be handled in Message::recv. + //throw std::runtime_error("asyn_recv loop exception"); + break; + default: + //msg->print("Asycn Other/Vendor Specific(?)"); // for debug + if (this->promise_map.count(msg->message_type) == 0){ + //msg->print("No promise"); // for debug + } + else{ + this->promise_map[msg->message_type].set_value(std::move(*msg)); + this->remove_async_promise(msg->message_type); + } + break; + } + continue; + }; + + } + + int HiSLIP::wait_Service_Request(int wait_time){ + // register promise for SRT and wait SRQ using future. with-timeout. + // + // register promise for SRT and wait SRQ using future. + if (! this->srq_msg_stack.empty()){ + Message msg=this->pop_srq_msg(); + return msg.control_code; + } + else{ + std::promise p; + std::future f = p.get_future(); + + this->push_srq_promise(std::move(p)); + std::future_status rst=f.wait_for(std::chrono::milliseconds(wait_time)); + if (rst != std::future_status::timeout) { + Message msg=f.get(); + return msg.control_code; + } + return -1; + } + + // using FpMilliseconds = + // std::chrono::duration; + + // static_assert(std::chrono::treat_as_floating_point::value, + // "Rep required to be floating point"); + + // steady_clock::time_point clk_begin = steady_clock::now(); + + // Message *msg=new Message(nsHiSLIP::AnyMessages); + // long status; + + // // check srq_msg_stack if stack is not empty + // this->async_poll.revents=0; + // while (::poll(&this->async_poll, 1, wait_time ) ){ + // steady_clock::time_point clk_now = steady_clock::now(); + // // Note that implicit conversion is allowed here + // auto time_spent = FpMilliseconds(clk_now - clk_begin); + // try{ + // status = msg->recv(this->async_channel, nsHiSLIP::AnyMessages); + // } + // catch(SRQ_t &e){ + // this->release_srq_lock(); + // return msg->control_code; + // } + // catch (FatalError_t &e){ + // this->Fatal_Error_handler(msg); + // return -1; + // } + // catch (Error_t &e){ + // this->Error_handler(msg); + // return -1; + // } + // catch (Interrupted_t &e){ + // this->Error_handler(msg); + // return -1; + // } + // catch(...){ + // ::printf("uncahugt exception"); + // return -1; + // } + // //msg->print(); // for debug + + // switch (msg->message_type ){ + // case nsHiSLIP::AsyncLockResponse: + // case nsHiSLIP::AsyncRemoteLocalResponse: + // case nsHiSLIP::AsyncStatusResponse: + // case nsHiSLIP::AsyncMaximumMessageSizeResponse: + // case nsHiSLIP::AsyncDeviceClearAcknowledge: + // case nsHiSLIP::AsyncLockInfoResponse: + // case nsHiSLIP::AsyncStartTLSResponse: + // case nsHiSLIP::AsyncEndTLSResponse: + // msg->print("unexpected responce"); // for debug + // default: + // wait_time -= (time_spent.count()); + // if (wait_time <= 0){ + // return -1; + // } + // break; + // } + // continue; + // }; + // return -2; + }; + // + void HiSLIP::Fatal_Error_handler(Message *msg){ + msg->print("Fatal Error"); // for debug + }; + void HiSLIP::Error_handler(Message *msg){ + msg->print("Error"); // for debug + }; + + void HiSLIP::report_Fatal_Error(const u_int8_t erc, const char *errmsg){ + Message + msg(nsHiSLIP::FatalError, erc, 0, strnlen(errmsg,256), (u_int8_t *) errmsg); + msg.send(this->async_channel); + }; + + void HiSLIP::report_Error(const u_int8_t erc, const char *errmsg){ + Message + msg(nsHiSLIP::Error, erc, 0, strnlen(errmsg,256), (u_int8_t *) errmsg); + msg.send(this->async_channel); + }; +} // end of namespace HiSLIP + diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPMessage.h b/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPMessage.h new file mode 100644 index 000000000..888068add --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/HiSLIPMessage.h @@ -0,0 +1,586 @@ +//-*- coding:utf-8 -*- +#define NDEBUG 1 +#define DEBUG 1 + +#include +#include + +//#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include +#include +#include +#include //memcpy, strlen, etc +#include +#include //* for MAXHOSTNAMELEN */ +#include //* close() and others */ + +#include +#include +#include +#include +#include // for MacOS + + +#include +#include +#include +#include +using namespace std::chrono; +namespace chrono = std::chrono; + +#include +#include //for std::exception +#include +#include + +// for async +#include + +//#include +// #include + +//#include + +#ifdef __linux__ +# include // network endian is "be". +#else // i.e. macOSX +inline u_int64_t htobe64(u_int64_t q) { + return (htonl(q>>32) +((u_int64_t)htonl(q & 0x00000000ffffffff)<<32)) ; +}; + +inline u_int64_t be64toh(u_int64_t q) { + return (ntohl(q>>32) +((u_int64_t)ntohl(q & 0x00000000ffffffff)<<32)) ; +}; +#endif + +//#include +//#include +//#include +//#include +//#include +//#include + +template struct Property { + T& r; + operator T() {return r;} + void operator =(const T v){ r=v;} +}; + +namespace nsHiSLIP{ + typedef class HiSLIP_FatalError:std::runtime_error { + public: + HiSLIP_FatalError(const std::string& msg):std::runtime_error(msg){}; + HiSLIP_FatalError(const char * msg):std::runtime_error(msg){ + } + } FatalError_t; + + typedef class HiSLIP_Error:std::exception { + const char *msg; + public: + HiSLIP_Error(const char * const msg){ + this->msg=msg; + } + } Error_t; + + typedef class HiSLIP_Interrupted:std::exception { + const char *msg; + public: + HiSLIP_Interrupted(const char * const msg){ + this->msg=msg; + } + } Interrupted_t; + + typedef class HiSLIP_SRQ:std::exception { + const char *msg; + public: + HiSLIP_SRQ(const char * const msg){ + this->msg=msg; + } + } SRQ_t; + + typedef class HiSLIP_TimeoutError:std::runtime_error{ + public: + HiSLIP_TimeoutError(const std::string& msg):std::runtime_error(msg){}; + HiSLIP_TimeoutError(const char * msg):std::runtime_error(msg){} + } TimeoutError_t; + + typedef class HiSLIP_RuntimeError:std::runtime_error{ + public: + HiSLIP_RuntimeError(const std::string& msg):std::runtime_error(msg){}; + HiSLIP_RuntimeError(const char * msg):std::runtime_error(msg){} + } RuntimeoutError_t; + + //constants + typedef enum CC_reuqest{ + RemoteDisable=0, + RemoteEnable=1, + RemoteDisableGTL=2, // disable remote and goto local + RemoteEnableGTR=3, // Enable remote and goto remote + RemoteEnableLLO=4, // Enable remote and lock out local + RemoteEnableGTRLLO=5, // + RTL=6 + } CC_request_t; + + typedef enum CC_Lock{ + release =0, + request =1 + } CC_Lock_t; + + typedef enum CC_LockRequestResponse{ + fail=0, //Lock was requested but not granted + success=1, //release of exclusive lock + locK_error=3 // Invalid + } CC_LockRequestResponse_t; + + typedef enum CC_LockReleaseResponse{ + success_exclusive=1, //release of exclusive lock + success_shared=2, //release of shared lock + release_error=3 // Invalid + } CC_LockReleaseResponse_t; + + static const long PROTOCOL_VERSION_MAX = 0x7f7f ; // # = <1><1> that is 257 + static const long INITIAL_MESSAGE_ID = 0xffffff00 ; + static const long INITIAL_LAST_MESSAGE_ID = 0xfffffefe; //i.e. 0xffffff00-2 + static const long UNKNOWN_MESSAGE_ID = 0xffffffff; + + //Following VISA 256 bytes + header length 16 bytes + static const long MAXIMUM_MESSAGE_SIZE_VISA = 272; + static const long MAXIMUM_MESSAGE_SIZE= 8192;//R&S accept more than this + static const long HEADER_SIZE=16; + static const long SOCKET_TIMEOUT = 1000; //# Socket timeout in msec + static const long LOCK_TIMEOUT = 3000;//# Lock timeout + static const long Default_Port = 4880; + static const char Default_device_name[]="hslip0"; + static const char Default_vendor_id[]={'E','P'}; + static const char Prologue[]={'H','S'}; + // + typedef enum Message_Types{ + Initialize = 0, // SC + InitializeResponse = 1, //SR + FatalError = 2, // E + Error = 3, // E + AsyncLock = 4,// AC + AsyncLockResponse = 5, //AR + Data = 6, + DataEnd = 7, + DeviceClearComplete = 8, + DeviceClearAcknowledge = 9, + AsyncRemoteLocalControl = 10, //AC + AsyncRemoteLocalResponse = 11, //AR + Trigger = 12, + Interrupted = 13, + AsyncInterrupted = 14, + AsyncMaximumMessageSize = 15, //AC + AsyncMaximumMessageSizeResponse = 16, //AR + AsyncInitialize = 17, //AC + AsyncInitializeResponse = 18, //AR + AsyncDeviceClear = 19, //AC + AsyncServiceRequest = 20, + AsyncStatusQuery = 21, //AC + AsyncStatusResponse = 22, //AR + AsyncDeviceClearAcknowledge = 23, + AsyncLockInfo = 24, //AC + AsyncLockInfoResponse = 25, //AR + // for HiSLIP ver2 + GetDescriptors =26, + GetDescriptorsResponse =27, + StartTLS = 28, + AsyncStartTLS = 29, + AsyncStartTLSResponse = 30, + EndTLS = 31, + AsyncEndTLS = 32, + AsyncEndTLSResponse = 33, + GetSaslMechanismList = 34, + GetSaslMechanismListResponse = 35, + AuthenticationStart = 36, + AuthenticationExchange = 37, + AuthenticationResult = 38, + // 39-127 are reserved for future use. + // I don't watn to use negative value to + // represent ANY message. + // So I picked 127 from reserved values + // for this purpose. + AnyMessages=127 // 128-255 are reserved for vendor use. + } _Message_Types_t; + + typedef u_int8_t Message_Types_t; + + typedef enum Error_code{ + UnidentifiedError, + UnrecognizedMessageType, + UnrecognizedControlCode, + UnrecognizedVendorDefinedMessage, + MessageTooLarge + } Error_code_t; + static const char *Error_Messages[] = + { + "Unidentified error", + "Unrecognized Message Type", + "Unrecognized control code", + "Unrecognized Vendor Defined Message", + "Message too large" + }; + typedef enum Fatal_Error_code { + UnidentifiedFatalError, + PoorlyFormedMmessageHeader, + AttemptToUseConnectionWithoutBothChannels, + InvalidInitializationSequence, + ServerRefusedConnection + } Fatal_Erro_code_t; + static const char *Fatal_Error_Messages[] = + { + "Unidentified error", + "Poorly formed message header", + "Attempt to use connection without both channels established", + "Invalid Initialization Sequence", + "Server refused connection due to maximum number of clients exceeded" + }; + + typedef class message_parameter{ + public: + u_int32_t word; + // struct InitializeParameter{ + // u_int16_t protocol_version; + // char vendor_id[2]={0x00 ,0x00}; + // } initParm; + // struct InitializeResponseParameter{ + // u_int16_t session_id; + // u_int16_t protocol_version; + // } initResp; + message_parameter(u_int32_t word){this->word=word;}; + message_parameter(u_int16_t proto, char vers[2]){ + this->word= ((int32_t)proto << 16) + + (vers[1] << 8) + (vers[0]<<0); + }; + message_parameter(u_int16_t proto, u_int16_t sid){ + this->word = (proto << 16) + sid; + }; + u_int16_t getServerProtocolVersion(){ + return (u_int16_t) (((this->word) & 0xffff0000)>>16); + } + u_int16_t getSessionId(){ + return ((u_int16_t) ((this->word) & 0xffff)); + } + } message_parameter_t; + + class Header{ + public: + const char prologue[2]={'H','S'}; + u_int8_t message_type; + u_int8_t control_code; + message_parameter_t message_parameter; + u_int64_t payload_length; + // + Header(Message_Types_t message_type):control_code(0),message_parameter(0),payload_length(0){ + this->message_type=message_type; + } + // + Header(const void *buffer):message_parameter(0){ + assert(memcmp(this->prologue, buffer,2) == 0); + this->message_type=*((u_int8_t *) ((char *) buffer+2)); + this->control_code=*((u_int8_t *) ((char *) buffer+3)); + this->message_parameter.word = ntohl(*(u_int32_t *) ((char *) buffer+4)); + this->payload_length = be64toh(*(u_int64_t *) ((char *) buffer+8)); + } + // + Header(const u_int8_t type, + const u_int8_t cce, + const message_parameter_t param, + const u_int64_t length):message_parameter(param.word){ + this->message_type=type; + this->control_code=cce; + this->payload_length=length; + } + // + void print(const char *msg=NULL){ + if (msg != NULL){ + ::printf("Header dump for: %s\n", msg); + } + ::printf("message type:%d\n",this->message_type); + ::printf("control_code:%d\n",this->control_code); + ::printf("message_parameter: 0x%0x\n",this->message_parameter.word); + ::printf("payload length: %llu\n",this->payload_length); + } + // + ssize_t send(int socket){ + char hbuf[HEADER_SIZE]; + ssize_t ssize; + + this->toRawData(hbuf); + + //ssize=::send(socket, hbuf, sizeof(hbuf), 0); + { + auto fut=std::async(std::launch::async, + ::send, + socket,hbuf,sizeof(hbuf),0); + ssize= fut.get(); + } + return ssize; + } + // + ssize_t recv(int socket){ + unsigned char buffer[HEADER_SIZE]; + return this->recv(socket, buffer); + } + // + ssize_t recv(int socket, void *buffer); + + int fromRawData(void *buffer){ //DeSerialize + if (memcmp(this->prologue, buffer, 2) != 0){ + //error + return -1; + } + this->message_type=*((u_int8_t *) ((char *) buffer+2)); + this->control_code=*((u_int8_t *) ((char *) buffer+3)); + this->message_parameter.word = ntohl(*(u_int32_t *) ((char *) buffer+4)); + this->payload_length = be64toh(*(u_int64_t *) ((char *) buffer+8)); + return 0; + } + int toRawData(void *buffer){ //Serialize this as bytes data in buffer. + memcpy( buffer, this->prologue, 2); + *((char *) buffer + 2) = this->message_type; + *((char *) buffer + 3) = this->control_code; + //*((u_int32_t *)((char *) buffer+4))=htobe32(this->message_parameter.word); + *((u_int32_t *)((char *) buffer+4))=htonl(this->message_parameter.word); + *((u_int64_t *)((char *) buffer+8))=htobe64(this->payload_length); + return 0; + } + }; + + typedef class Message:public Header{ + public: + void *payload=NULL; + int clean_on_exit=0; + + ~Message(){ + if ((this->payload != NULL) && (this->clean_on_exit==1)){ + ::free(this->payload); + this->payload=NULL; + this->clean_on_exit=0; + } + }; + + Message(Message_Types_t message_type):Header(message_type){ + this->payload=NULL; + this->clean_on_exit=0; + }; + Message(void *raw_header):Header(raw_header){ + this->payload=NULL; + this->clean_on_exit=0; + //this->fromRawData(raw_header); + }; + Message(void *raw_header, void *payload):Message(raw_header){ + this->payload = calloc(this->payload_length, 1); + this->clean_on_exit=1; + if (this->payload != NULL){ + memcpy(this->payload, payload, this->payload_length); + }; + } + Message(u_int8_t type, + u_int8_t cce, + message_parameter_t param, + u_int64_t length, + u_int8_t *payload):Header(type, + cce, + param, + length), + payload(payload) { + this->clean_on_exit=0; + } + // + size_t send(int socket){ + size_t ssize; + // ssize=this->Header::send(socket); + // if (ssize < HEADER_SIZE){ + // return -1; + // } + // return (ssize + ::send(socket, this->payload, this->payload_length,0)); + u_int8_t *buffer = (u_int8_t *) calloc(sizeof(Header)+this->payload_length, 1); + this->toRawData(buffer); + memcpy(buffer+sizeof(Header),this->payload, this->payload_length); + ssize= ::send(socket, buffer, sizeof(Header)+this->payload_length,0); + free(buffer); + return ssize; + } + + ssize_t recv(int socket, Message_Types_t expected_message_type = AnyMessages); + + } Message_t; + + typedef class HiSLIP { + public: + unsigned long maximum_message_size = MAXIMUM_MESSAGE_SIZE; // max message size server accept + unsigned long maximum_payload_size = MAXIMUM_MESSAGE_SIZE; + unsigned long current_message_size = MAXIMUM_MESSAGE_SIZE - HEADER_SIZE; // current max message size setting + unsigned long current_payload_size = MAXIMUM_MESSAGE_SIZE - HEADER_SIZE; + // + long socket_timeout = SOCKET_TIMEOUT; + long lock_timeout = LOCK_TIMEOUT; + int sync_channel = 0 ; + int async_channel = 0 ; + struct pollfd sync_poll; + struct pollfd async_poll; + // + u_int8_t feature_setting = 0; + u_int8_t feature_preference = 0 ; + int session_id; + int server_protocol_version; + unsigned int server_vendorID; + //mode variables for HiSLIP protocols. + bool overlap_mode = false; // false for synchronized mode, true for overlapped mode + bool interrupted = false; + bool async_interrupted = false; + bool rmt_delivered = false; + // + u_int32_t message_id; + u_int32_t most_recent_message_id; + u_int32_t most_recent_received_message_id; + //sem_t srq_lock; + pthread_mutex_t srq_lock=PTHREAD_MUTEX_INITIALIZER; + // + std::mutex srq_promise_mutex; + std::stack> srq_promise_stack; + // + std::mutex srq_msg_mutex; + std::stack srq_msg_stack; + // + std::mutex promise_map_mutex; + std::map> promise_map; + // + bool async_recceiver_active; + std::thread *async_receiver_thread=NULL; + // + ~HiSLIP(){ + //::printf("destructor for HiSLIP\n"); + this->stop_async_receiver_thread(); + if (this->check_srq_lock()){ + this->release_srq_lock(); + } + } + // + void connect(char const* hostname){ + this->connect(hostname, + Default_device_name, + Default_Port); + }; + + void connect(char const* hostname, + char const* dev_name, + int port); + // + void start_async_receiver_thread(void); + void stop_async_receiver_thread(void){ + this->async_recceiver_active=false; + if (this->async_receiver_thread !=NULL){ + this->async_receiver_thread->join(); + }; + }; + void restart_async_receiver_thread(void){ + this->stop_async_receiver_thread(); + this->start_async_receiver_thread(); + }; + void async_receiver_loop(void); + std::future async_send_msg(Message msg, + Message_Types_t responseType=AnyMessages); + void register_async_promise(Message_Types_t responseType, + std::promise p); + void remove_async_promise(Message_Types_t responseType); + // + void push_srq_promise(std::promise p); + std::promise pop_srq_promise(void); + void clear_srq_promise(void); + // + void push_srq_msg(Message msg); + Message pop_srq_msg(void); + void clear_srq_msg(void); + void clear_srq_stacks(void); + // + void set_timeout( long timeout){ + this->socket_timeout=timeout; + }; + + long get_timeout(void){ + return this->socket_timeout; + }; + + void set_lock_timeout( long timeout){ + this->lock_timeout=timeout; + }; + + long get_lock_timeout(void){ + return this->lock_timeout; + }; + + long set_max_size(unsigned long message_size); + int device_clear(u_int8_t); + int status_query(void); + //long write(u_int8_t *data_str, long timeout=LOCK_TIMEOUT); + long write(const u_int8_t *data_str, const size_t size, + long timeout=LOCK_TIMEOUT); + long ask(u_int8_t *data_str, size_t size, u_int8_t **rbuffer, + long wait_time=LOCK_TIMEOUT); + long read(size_t *received, long timeout=LOCK_TIMEOUT ); + long read(size_t *received, u_int8_t **buffer, + long timeout=LOCK_TIMEOUT); + long read(size_t *received, u_int8_t *buffer, size_t bsize, + long timeout=LOCK_TIMEOUT); + + long async_ask(u_int8_t *data_str, size_t size, long wait_time=LOCK_TIMEOUT); + long async_read( size_t *received, u_int8_t *buffer, size_t bsize, + long timeout=LOCK_TIMEOUT); + + long trigger_message(void); + long remote_local(u_int8_t request); + long request_lock(const char* lock_string = NULL); + long release_lock(void); + long lock_info(void); + long handle_error_msg(void); + // + long request_srq_lock(void); + long release_srq_lock(void); + int check_srq_lock(void); + int check_and_lock_srq_lock(void); + // + int get_Service_Request(void); + int wait_Service_Request(int wait_time); + // + int wait_for_Async(int wait_time){ + // just used in EPICS device support. + // shoudl use promise-future. + // create promise and retister it to async-promise list. + // then wait future. + this->async_poll.revents=0; + return ::poll(&this->async_poll, 1, wait_time); + } + + void disconnect(){ + if (this->sync_channel != 0){ + close(this->sync_channel); + this->sync_channel=0; + }; + if (this->async_channel != 0){ + close(this->async_channel); + this->async_channel=0; + }; + }; + + void Fatal_Error_handler(Message *msg); + void Error_handler(Message *msg); + void report_Fatal_Error(const u_int8_t erc, const char *errmsg); + void report_Error(const u_int8_t erc, const char *errmsg); + // + + private: + void reset_message_id(void){ + this->most_recent_message_id=INITIAL_LAST_MESSAGE_ID; + this->most_recent_received_message_id=INITIAL_MESSAGE_ID; + this->message_id = INITIAL_MESSAGE_ID; + } + + u_int32_t increment_message_id(void){ + this->most_recent_message_id=this->message_id; + this->message_id = (this->message_id +2) & 0xffffffff; + return this->message_id; + }; + } HiSLIP_t; +}; // namespace diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/MANIFEST b/asyn/drvAsynHiSLIP/cPyHiSLIP/MANIFEST new file mode 100644 index 000000000..692f97851 --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/MANIFEST @@ -0,0 +1,13 @@ +# file GENERATED by distutils, do NOT edit +HiSLIPFunctionTest.py +HiSLIPMessage.cpp +HiSLIPMessage.h +Kikusui_PWR401MH_test_for_cPyHiSLIP.py +MANIFEST.in +Makefile +README.rst +README.txt +cPyHiSLIP.pxd +cPyHiSLIP.pyx +memleak_test.py +setup.py diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/MANIFEST.in b/asyn/drvAsynHiSLIP/cPyHiSLIP/MANIFEST.in new file mode 100644 index 000000000..bab5d4c47 --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/MANIFEST.in @@ -0,0 +1,17 @@ +include MANIFEST.in +include setup.py +include Makefile +include README.rst +include HiSLIPMessage.h +include HiSLIPMessage.cpp +include cPyHiSLIP.pyx +include cPyHiSLIP.pxd +exclude cPyHiSLIP_*.* +include *.py +include *.txt +prune docs +prune build +prune dist +prune */build +prune ._* +exclude build/* diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/Makefile b/asyn/drvAsynHiSLIP/cPyHiSLIP/Makefile new file mode 100644 index 000000000..30cc9070e --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/Makefile @@ -0,0 +1,18 @@ +#!make +#HGVERSION:= $(shell hg parents --template 'HGTagShort = \\\"{latesttag}.{latesttagdistance}\\\"') +VERSIONGIT:= $(shell git describe --tags) +VERSION:= $(shell python3 setup.py --version) + +.phony: markdown +.SUFFIXES : .rst .md .txt + +.rst.md: + /usr/local/bin/pandoc -f rst -t markdown -o $@ $< + +.rst.txt: + /usr/local/bin/pandoc -f rst -t plain -o $@ $< + +markdown: README.md README.txt + +upload: + python3 -m twine upload dist/cPyHiSLIP-$(VERSION).tar.gz diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/README.md b/asyn/drvAsynHiSLIP/cPyHiSLIP/README.md new file mode 100644 index 000000000..6c12349cb --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/README.md @@ -0,0 +1,91 @@ +README for cPyHiSLIP +==================== + +Author + +: 山本 昇 Noboru Yamamoto + +Organization + +: 高エネルギー加速器研究機構 加速器研究施設 Accelerator Control Group, + Accelerator Laboratory, KEK, JAPAN J-PARCセンタ 加速器ディビジョン + 制御グループ Control Groups Accelerator Division JPARC Center + +Address + +: 〒305-0801 茨城県つくば市大穂1-1 1-1 Oho Tsukuba, Ibaraki, JAPAN + 〒319-1195 茨城県那珂郡東海村大字白方2-4 J-PARC中央制御棟 2-4 + Shirakata, Tokai, Naka Ibaraki, 319-1195 JAPAN + +How to install +-------------- + +you need cython to build cPyHiSLIP module from cPyHiSLIP.pyx and +cPyHiSLIP.pxd. If you dont have installed cython, try: + +> pip install cython + +or (I personally recomend this way): + +> python -m pip install cython + +To build and install the module, run: + +> python setup.py build install + +You may need a privilege for installation. + +How to Use +---------- + +A very Simple example: + +> import cPyHiSLIP device=cPyHiSLIP.HiSLIP(b\"xxx.yyy.zzz.ttt\") print +> (dev.ask(b\"\*IDN?\") + +Note that arguments to the method are given as bytes, but not strings. A +return value from the method is also anbyte array, not strings. + +Methods , .write(), .read() and .ask() , are provided for the +communication through sync channe. + +For the communication throug async channel, specfied methods are +privided, such as, device\_clear, status\_query, trigger\_device, +remote-local, request\_lock, release\_lock, lock\_info. + +An interrupt mode can be actiavated by: + +> dev.device\_clear(1) + +### SRQ support + +Async designe memo +------------------ + +async channelで送受信されるメッセージ + +c\ Error \<-\> FatalError + +c\>s AsyncLock c\s AsynLockInfo c\s AsyncRemoteLocalControl c\s AsyncDeviceClear c\s AsyncMaximumMessageSize c\s GetDescriptors c\s AsyncInitialize c\s AsyncStatusQuery c\s AsyncStartTLS c\s AsyncEndTLS c\s VendorSpecific diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/README.rst b/asyn/drvAsynHiSLIP/cPyHiSLIP/README.rst new file mode 100644 index 000000000..e2f0c8709 --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/README.rst @@ -0,0 +1,124 @@ +README for cPyHiSLIP +====================== + + +:Author: 山本 昇 + Noboru Yamamoto + +:Organization: 高エネルギー加速器研究機構 + 加速器研究施設 + Accelerator Control Group, + Accelerator Laboratory, + KEK, JAPAN + J-PARCセンタ + 加速器ディビジョン + 制御グループ + Control Groups + Accelerator Division + JPARC Center + +:Address: 〒305-0801 + 茨城県つくば市大穂1-1 + 1-1 Oho + Tsukuba, Ibaraki, + JAPAN + 〒319-1195 + 茨城県那珂郡東海村大字白方2-4 + J-PARC中央制御棟 + 2-4 Shirakata, Tokai, Naka + Ibaraki, 319-1195 + JAPAN + + +How to install +-------------- +you need cython to build cPyHiSLIP module from cPyHiSLIP.pyx and cPyHiSLIP.pxd. +If you dont have installed cython, try: + + pip install cython + +or (I personally recomend this way): + + python -m pip install cython + + +To build and install the module, run: + + python setup.py build install + +You may need a privilege for installation. + + +How to Use +-------------- +A very Simple example: + + import cPyHiSLIP + device=cPyHiSLIP.HiSLIP(b"xxx.yyy.zzz.ttt") + print (dev.ask(b"*IDN?") + +Note that arguments to the method are given as bytes, but not strings. +A return value from the method is also anbyte array, not strings. + +Methods , .write(), .read() and .ask() , are provided for the communication +through sync channe. + +For the communication throug async channel, specfied methods are privided, +such as, device_clear, status_query, trigger_device, remote-local, request_lock, +release_lock, lock_info. + +An interrupt mode can be actiavated by: + + dev.device_clear(1) + +SRQ support +++++++++++++++++ + + + + + +Async designe memo +---------------------- + +async channelで送受信されるメッセージ + +c Error +<-> FatalError + +c>s AsyncLock +cs AsynLockInfo +cs AsyncRemoteLocalControl +cs AsyncDeviceClear +cs AsyncMaximumMessageSize +cs GetDescriptors +cs AsyncInitialize +cs AsyncStatusQuery +cs AsyncStartTLS +cs AsyncEndTLS +cs VendorSpecific diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/README.txt b/asyn/drvAsynHiSLIP/cPyHiSLIP/README.txt new file mode 100644 index 000000000..e4274462b --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/README.txt @@ -0,0 +1,87 @@ +README for cPyHiSLIP + +Author + + 山本 昇 Noboru Yamamoto + +Organization + + 高エネルギー加速器研究機構 加速器研究施設 Accelerator Control Group, + Accelerator Laboratory, KEK, JAPAN J-PARCセンタ 加速器ディビジョン + 制御グループ Control Groups Accelerator Division JPARC Center + +Address + + 〒305-0801 茨城県つくば市大穂1-1 1-1 Oho Tsukuba, Ibaraki, JAPAN + 〒319-1195 茨城県那珂郡東海村大字白方2-4 J-PARC中央制御棟 2-4 + Shirakata, Tokai, Naka Ibaraki, 319-1195 JAPAN + +How to install + +you need cython to build cPyHiSLIP module from cPyHiSLIP.pyx and +cPyHiSLIP.pxd. If you dont have installed cython, try: + + pip install cython + +or (I personally recomend this way): + + python -m pip install cython + +To build and install the module, run: + + python setup.py build install + +You may need a privilege for installation. + +How to Use + +A very Simple example: + + import cPyHiSLIP device=cPyHiSLIP.HiSLIP(b"xxx.yyy.zzz.ttt") print + (dev.ask(b"*IDN?") + +Note that arguments to the method are given as bytes, but not strings. A +return value from the method is also anbyte array, not strings. + +Methods , .write(), .read() and .ask() , are provided for the +communication through sync channe. + +For the communication throug async channel, specfied methods are +privided, such as, device_clear, status_query, trigger_device, +remote-local, request_lock, release_lock, lock_info. + +An interrupt mode can be actiavated by: + + dev.device_clear(1) + +SRQ support + +Async designe memo + +async channelで送受信されるメッセージ + +c Error <-> FatalError + +c>s AsyncLock cs AsynLockInfo cs AsyncRemoteLocalControl cs AsyncDeviceClear cs AsyncMaximumMessageSize cs GetDescriptors cs AsyncInitialize cs AsyncStatusQuery cs AsyncStartTLS cs AsyncEndTLS cs VendorSpecific diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/cPyHiSLIP.pxd b/asyn/drvAsynHiSLIP/cPyHiSLIP/cPyHiSLIP.pxd new file mode 100644 index 000000000..0426a0d4d --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/cPyHiSLIP.pxd @@ -0,0 +1,104 @@ +#!cython +# -*- coding:utf-8 -*- +# distutils: language=c++ + +cdef int SCPIRawSocketPort=5025 # both udp/tcp +cdef int SCPITelnetPort=5024 # both udp/tcp + +cdef extern from "stdlib.h": + cdef void free (void *) + +cdef extern from "HiSLIPMessage.h": + # you don’t need to match the type exactly, + # just use something of the right general kind (int, float, etc). + + ctypedef unsigned long u_long + ctypedef unsigned int u_int + ctypedef unsigned short u_short + ctypedef unsigned char u_char + ctypedef int bool_t + + ctypedef signed char int8_t + ctypedef signed short int int16_t + ctypedef signed int int32_t + ctypedef signed long int int64_t + + ctypedef unsigned char u_int8_t + ctypedef unsigned short int u_int16_t + ctypedef unsigned int u_int32_t + ctypedef unsigned long int u_int64_t + +cdef extern from "HiSLIPMessage.h" namespace "nsHiSLIP": + # exceptions + cdef cppclass HiSLIP_Error "nsHiSLIP:HiSLIP_Error" + cdef cppclass HiSLIP_FatalError "nsHiSLIP:HiSLIP_FatalError" + cdef cppclass HiSLIP_Interrupted "nsHiSLIP:HiSLIP_Interrupted" + cdef cppclass HiSLIP_TimeoutError "nsHiSLIP:HiSLIP_TimeoutError" + cdef cppclass HiSLIP_RutimeError "nsHiSLIP:HiSLIP_RuntimeError" + # static variables + cdef const long MAXIMUM_MESSAGE_SIZE "nsHiSLIP::MAXIMUM_MESSAGE_SIZE" + cdef char * Default_device_name "nsHiSLIP::Default_device_name" + cdef int HiSLIPPort "nsHiSLIP::Default_Port" # not in /etc/services + # classes + cdef cppclass message_parameter "nsHiSLIP::message_parameter" + cdef cppclass Header "nsHiSLIP::Header" + cdef cppclass Message "nsHiSLIP::Message" + + cdef cppclass cHiSLIP "nsHiSLIP::HiSLIP": + # members + unsigned long maximum_message_size + unsigned long maximum_payload_size + unsigned long current_message_size + unsigned long current_payload_size + + int overlap_mode; + u_int8_t feature_setting + u_int8_t feature_preference + int session_id; + int server_protocol_version; + unsigned int server_vendorID; + + int rmt_delivered; + u_int32_t message_id; + u_int32_t most_recent_message_id; + u_int32_t most_recent_received_message_id; + # methods + cHiSLIP() except+ + + void connect(char * hostname, + char * dev_name, + int port) nogil except+ + + void start_async_receiver_thread() nogil except+ + void stop_async_receiver_thread() nogil except+ + void restart_async_receiver_thread() nogil except+ + + void set_timeout(long) nogil except+ + long get_timeout() nogil except+ + void set_lock_timeout(long) nogil except+ + long get_lock_timeout() nogil except+ + + long set_max_size(long) nogil except+ + int device_clear(u_int8_t) nogil except+ + u_int8_t status_query() nogil except+ + long write(u_int8_t *, size_t, long) nogil except+ + long read(size_t *, u_int8_t **, long) nogil except+ + long ask(u_int8_t *, size_t, u_int8_t **, long) nogil except+ + long trigger_message() nogil except+ + long remote_local(u_int8_t) nogil except+ + long request_lock(char *) nogil except+ + long release_lock() nogil except+ + long lock_info() nogil except+ + + long request_srq_lock() nogil except+ + long release_srq_lock() nogil except+ + int check_srq_lock() nogil except+ + int check_and_lock_srq_lock() nogil except+ + int get_Service_Request() nogil except+ + int wait_Service_Request(int wait_time) nogil except+ + void clear_srq_stacks() nogil except+ + # int wait_for_Async(int) nogil except+ + void disconnect() nogil except+ + void report_Fatal_Error(const u_int8_t erc, const char *errmsg) nogil except+ + void report_Error(const u_int8_t erc, const char *errmsg) nogil except+ + diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/cPyHiSLIP.pyx b/asyn/drvAsynHiSLIP/cPyHiSLIP/cPyHiSLIP.pyx new file mode 100644 index 000000000..52fa53afe --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/cPyHiSLIP.pyx @@ -0,0 +1,524 @@ +#!cython +#-*- coding:utf-8 -*- +# distutils: sources = HiSLIPMessage.cpp +# distutils: language=c++ + +from cython.operator cimport dereference as deref, preincrement as inc, address as addressof + +# +import sys + +from cPyHiSLIP cimport cHiSLIP +#from cPyHiSLIP cimport HiSLIPPort +#from cPyHiSLIP cimport Default_device_name +import logging +from logging import info,debug,warn,log,fatal,warning +#logging.root.setLevel(logging.DEBUG) + +cdef class HiSLIP: + cdef cHiSLIP *thisobj + + def __cinit__(self, char *host=NULL): + self.thisobj=new cHiSLIP() + + def __init__(self, char *host=NULL): + cdef char *chost=host + if host: + self.connect(host) + + def __dealloc__(self): + del self.thisobj + debug("dealloced cHiSLIP object") + pass + + def connect(self, host, device=Default_device_name, port=HiSLIPPort): + cdef char *chost=host + cdef char *cdevice =device + cdef int cport =port + debug("connect to {} {} {}".format(host, device, port)) + + with nogil: + self.thisobj.connect(chost,cdevice,cport) + + # debug("start async_receiver to {} {} {}".format(host, device, port)) + # with nogil: + # self.thisobj.start_async_receiver_thread() + + # debug("setmaxsize to {} {} {}".format(host, device, port)) + # with nogil: + # self.thisobj.set_max_size(MAXIMUM_MESSAGE_SIZE) + + + @property + def session_id(self): + """ + a getter function for the property session_id. + """ + return self.thisobj.session_id + + @property + def message_id(self): + """ + """ + return self.thisobj.message_id + + @property + def most_recent_message_id(self): + """ + """ + return self.thisobj.most_recent_message_id + + @property + def most_recent_received_message_id(self): + """ + """ + return self.thisobj.most_recent_received_message_id + + @property + def overlap_mode(self): + """ + """ + return self.thisobj.overlap_mode + + def write(self, msg, long timeout=3000): + cdef u_int8_t *cmsg=msg + cdef size_t ssize=len(msg) + with nogil: + self.thisobj.write(cmsg, ssize, timeout) + + cdef _cread(self,long timeout=3000) except+: + cdef unsigned char *buffer=NULL + cdef unsigned char **pbuffer=&buffer + cdef size_t recieved=0, + cdef size_t *precieved=&recieved + cdef int rt + # debug("calling read in c++") + try: + with nogil: + rt=self.thisobj.read(precieved, pbuffer, timeout) + except RuntimeError as e: + debug("catch c++ exception error: {}".format(sys.exc_info())) + #print (e) + raise + except: + debug("Unexpected error: {}".format(sys.exc_info()[0])) + raise + + if rt == -1: + if (buffer != NULL): + free(buffer) + buffer=NULL + raise RuntimeError("Timeout") + elif rt < 0: + if (buffer != NULL): + free(buffer) + buffer=NULL + raise RuntimeError + if recieved == 0: + if (buffer != NULL): + free(buffer) + buffer=NULL + return (recieved, None) + else: + if (buffer == NULL): + raise RuntimeError("NULL pointer") + rval=(recieved, buffer[:recieved]) # to avoid truncated string by \x00 in rawdata + free(buffer) + buffer=NULL + return rval + + def read(self, long timeout=3000): + recieved, buffer=self._cread(timeout) + debug("cread result {} {} {}".format(recieved,len(buffer), + buffer[:recieved] + ) + ) + return (recieved, buffer[:recieved]) + + def ask(self, msg, long wait_time=3000): + rsize, result=self._ask(msg, wait_time) + # debug("ask res size: {}".format(rsize)) + if result == None: + return None + elif (rsize > 0): + return result[:rsize] + else: + return None + + cdef _ask(self, u_int8_t *msg, long wait_time): + cdef unsigned char *buffer=NULL + cdef unsigned char **pbuffer=&buffer; + cdef size_t recieved=0 + cdef int rt + cdef size_t msgsz=len(msg) + + with nogil: + recieved=self.thisobj.ask(msg, msgsz, + pbuffer, + wait_time) + debug("_ask recieved: {}".format(hex(recieved))) + if (recieved & 0x8000000000000000L): + if (buffer != NULL): + free(buffer) + buffer=NULL + return (-1,None) + elif (recieved > 0 and (buffer != NULL)): + rval=(recieved, buffer[:recieved]) + free(buffer) + buffer=NULL + return rval + else: + return (recieved, None) + + def set_timeout(self,long timeout): + with nogil: + self.thisobj.set_timeout(timeout) + return + + def get_timeout(self): + cdef long to + with nogil: + to=self.thisobj.get_timeout() + return to + + def set_lock_timeout(self, long to): + with nogil: + self.thisobj.set_lock_timeout(to) + return + + def get_lock_timeout(self): + cdef long to + with nogil: + to=self.thisobj.get_lock_timeout() + return to + + def set_max_message_size(self, size_t message_size): + cdef long ms + with nogil: + ms=self.thisobj.set_max_size(message_size) + return ms + + def get_max_message_size(self): + cdef size_t ms + ms=self.thisobj.maximum_message_size + return ms + + def get_max_payload_size(self): + cdef long ms + ms=self.thisobj.maximum_payload_size + return ms + + def get_message_size(self): + cdef size_t ms + ms=self.thisobj.current_message_size + return ms + + def get_payload_size(self): + cdef long ms + ms=self.thisobj.current_payload_size + return ms + + def device_clear(self, u_int8_t request=0): + cdef long rc + with nogil: + rc=self.thisobj.device_clear(request) + return rc + + def status_query(self): + """ + bit/weight/name + 0 1 Reserved + 1 2 Reserved + 2 4 Error/Event Queue + 3 8 Questionable Status Register (QUES) + 4 16 Message Available (MAV) + 5 32 Standard Event Status Bit Summary (ESB) + 6 64 Request Service (RQS)/Master Status Summary (MSS) + 7 128 Operation Status Register (OPER) + + SCPI/IEEE4882/Tektronix: + -/RQS(64)/ESB(32)/MAV(16)/---- + STB for Agilent/Keysight: + OPER(128)/RQS(64)/ESB(32)/MAV(16)/-/MSG(4)/USR(2)/TRG(1) + STB for Kikusui PWR01 + OPER(128)/RQS(64)/ESB(32)/MAV(16)/QUES(8)/ERR(4)/-/- + """ + + cdef u_int8_t rc + + # if self.thisobj.wait_for_Async(1): + # self.thisobj.get_Service_Request() + with nogil: + rc=self.thisobj.status_query() + return rc + + def trigger_device(self): + cdef long rc + with nogil: + rc=self.thisobj.trigger_message() + return rc + + def remote_local(self, u_int8_t request): + """ + Table 18: Remote Local Control Transactions + 0 - Disable remote + 1 - Enable remote + 2 - Disable remote and go to LOCAL + 3 - Enable remote and go to REMOTE + 4 - Enable remote and Lock Out LOCAL + 5 - Enable remote, go to REMOTE, and set local LOCKOUT + 6 - go to local without changing state of remote enable. + """ + cdef long rc + #cdef u_int8_t _request=request + with nogil: + rc=self.thisobj.remote_local(request) + return rc + + def request_lock(self, char *lock_string=b""): + """ + response to request: + 0 - Failure + 1 - Success + 3 - Error + """ + cdef long rc + cdef char *c_lock_string + if lock_string != b"": + c_lock_string=lock_string + with nogil: + rc=self.thisobj.request_lock(c_lock_string) + else: + with nogil: + rc=self.thisobj.request_lock(lock_string) + return rc + + def release_lock(self): + """ + response to release: + 1 - Success exclusive + 2 - Success shared + 3 - Error + """ + cdef long rc + with nogil: + rc=self.thisobj.release_lock() + return rc + + def lock_info(self): + """ + return (lock_exclusiv, lock_shared) pair. + where: + lock_exclusive: 1 if exclusively locked + lock_shared: number of clients has sahred lock. + """ + cdef long rc=0 + cdef long lock_shared=0 + cdef u_int8_t lock_exclusive=0 + + with nogil: + rc=self.thisobj.lock_info() + #debug ("rc: {}".format(rc)) + if rc >= 0: + lock_exclusive= rc & 0xff + lock_shared = (rc >> 8) & 0xffffffff + #debug ("rc: {} {}".format(lock_exclusive, lock_shared)) + return (lock_exclusive, lock_shared) + + + def request_srq_lock(self): + """ + implemented in HiSLIPMessage.cpp. + rc :0 success + :others error + """ + cdef long rc=-1 + with nogil: + rc=self.thisobj.request_srq_lock() + return rc + + def release_srq_lock(self): + """ + Not implemented in .cpp yet. + 0: success + other: error + """ + cdef long rc=-1 + with nogil: + rc=self.thisobj.release_srq_lock() + return rc + + def check_srq_lock(self): + """ + 1: released + 0: locked + -1: + """ + cdef int rc=-1 + with nogil: + rc=self.thisobj.check_srq_lock() + return rc + + def check_and_lock_srq_lock(self): + """ + 1: released and now locked. + 0: already locked + """ + cdef int rc + with nogil: + rc=self.thisobj.check_and_lock_srq_lock() + return rc + + def get_overlap_mode(self): + cdef int ovm=self.thisobj.overlap_mode + return ovm + + def get_feature_setting(self): + cdef int fs=self.thisobj.feature_setting + return fs + + def get_feature_preference(self): + cdef int fp=self.thisobj.feature_preference + return fp + + def get_protocol_version(self): + cdef int spv=self.thisobj.server_protocol_version + major=(spv&0xff00)>>8 + minor=(spv & 0x00ff) + return (major, minor) + + def get_Service_Request(self): + cdef u_int8_t rc=0 + with nogil: + rc=self.thisobj.get_Service_Request() + return rc + + # def check_SRQ(self): + # cdef u_int8_t rc=0 + # with nogil: + # rc=self.thisobj.wait_for_Async(0) + # if rc: + # rc=self.thisobj.get_Service_Request() + # return rc + + def wait_Service_Request(self, int wait_time): + cdef int8_t rc=0 + + with nogil: + rc=self.thisobj.wait_Service_Request(wait_time) + return rc + + def clear_srq_stacks(self): + with nogil: + self.thisobj.clear_srq_stacks() + + # def wait_for_Async(self, long wait_time=1000): + # """ + # return 1 if async port is ready to read + # """ + # cdef int rc=-1 + # with nogil: + # rc=self.thisobj.wait_for_Async(wait_time) + # return rc + + # def run_svc(self,int wait=1000): + # cdef int rc=-1 + # cdef u_int8_t st + # cdef int cwait=wait + # with nogil: + # while 1: + # rc=self.thisobj.wait_for_Async(cwait) # msec , wait_for_SRQ does not release GIL so we use shorter time + # if rc: + # st=self.thisobj.get_Service_Request() # will release lock + # if not self.thisobj.session_id: + # break + # return + + def report_FatalError(self, u_int8_t erc, char * errmsg): + self.thisobj.report_Fatal_Error(erc, errmsg) + + def report_Error(self, u_int8_t erc, char * errmsg): + self.thisobj.report_Error(erc, errmsg) + +cdef class enumType: + @classmethod + def getKey(cls, v): + for k,kv in cls.__dict__.iteritems(): + if kv == v: + return k + return None + + @classmethod + def getValue(cls, k): + return cls.__dict__.get(k,None) + +cdef class HiSLIPMessageType(enumType): + Initialize=0 + InitializeResPonse=1 + FatalError=2 + Error=3 + AsynLock=4 + AsynLockResponse=5, + Data=6 + DataEnd=7 + DeviceClearComplete=8 + DeviceCLearAcknowledge=9 + AsyncRemoteLocalContro=10 + AsyncRemoteLocalResponcse=11 + Trigger=12 + Interrupted=13 + AsyncInterrupted=14 + AsyncMaximumMessageSize=15 + AsyncMaximumMessageSizeResponse=16 + AsyncInitilalize=17 + AsyncIntializeResponse=18 + AsyncDeviceClear=19 + AsyncServiceRequest=20 + AsynStatusQuery=21 + AsyncStatusResponse=22 + AsyncDeviceClearAcknowledge=23 + AsynLockInfo=24 + AsynLockInfoResponse=25 + # VendorSpecific 128-255 inclusive + +cdef class FatalErrorCodes(enumType): # Defined Fatal Error codes. Table-7 + UndefinedFatalError=0 + PoorlyFormedMessage=1 + UnEstablishedConnection=2 + InvalidInitializationSequence=3 + ServerRefued=4 + # 5-127 : reserved + FirstDeviceDefinedError=128 + # 128-255 : Device Defined Error + +cdef class ErrorCode(enumType): # defined Error codes(non-fatal). Table-9 + UndefinedError=0 + UnrecognizedMessageType=1 + UnrecognizedControlCode=2 + UnrecognizedVendorDefinedMessage=3 + MessageTooLarge=4 + # 5-127 : Reserved + FirstDviceDefinedError=128 + #128-255:Device Defined Error + +cdef class LockControlCode(enumType): + release=0 + request=1 + +cdef class LockResponseControlCode(enumType): + fail=0 + success=1 + successSharedLock=2 + error=3 + +cdef class RemoteLocalControlCode(enumType): + disableRemote=0 + enableRemote=1 + disableAndGTL=2 + enableAndGotoRemote=3 + enableAndLockoutLocal=4 + enableAndGTRLLO=5 + justGTL=6 + + + diff --git a/asyn/drvAsynHiSLIP/cPyHiSLIP/setup.py b/asyn/drvAsynHiSLIP/cPyHiSLIP/setup.py new file mode 100644 index 000000000..b01faaf19 --- /dev/null +++ b/asyn/drvAsynHiSLIP/cPyHiSLIP/setup.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +""" +Author:Noboru Yamamoto, KEK, Japan (c) 2020 + +contact info: https://souran.kek.jp/kss/top/ + +Revision Info: +$Author: $ +$Date: $ (isodatesec ) +$HGdate: $ +$Header: $ +$Id: setup.py $ +$RCSfile: $ +$Revision: $ +$Source: $ + +change log: +2020/06/25 : created +""" +import os,platform,re,sys,os.path + +from Cython.Distutils.extension import Extension +from Cython.Distutils import build_ext +from Cython.Build import cythonize + +# python2/python3 +extra=dict() + +# if sys.version_info >= (3,): +# extra['use_2to3'] = True + +try: + from distutils.command.build_py import build_py_2to3 as build_py #for Python3 +except ImportError: + from distutils.command.build_py import build_py # for Python2 + +from distutils.core import setup +#from distutils.extension import Extension +# +rev="0.2.2.4" +# + +sysname=platform.system() + +ext_modules=[] + +# cPyHiSLIP-2.pyx and cPyHiSLIP-3.pyx are hard links to cPyHiSLIP.pyx +cPyHiSLIP_source_PY2="cPyHiSLIP_2.pyx" +cPyHiSLIP_source_PY3="cPyHiSLIP_3.pyx" + +if sys.version_info >= (3,): + PY3=True + cPyHiSLIP_source=cPyHiSLIP_source_PY3 +else: + PY3=False + cPyHiSLIP_source=cPyHiSLIP_source_PY2 + +if not os.path.exists(cPyHiSLIP_source): + os.link("cPyHiSLIP.pyx", cPyHiSLIP_source) +elif not os.path.samefile("cPyHiSLIP.pyx", cPyHiSLIP_source): + os.remove(cPyHiSLIP_source) + os.link("cPyHiSLIP.pyx", cPyHiSLIP_source) + +ext_modules.append(Extension("cPyHiSLIP", + [ cPyHiSLIP_source, # Cython source. i.e. .pyx + ] + ,libraries=[] + ,depends=["cPyHiSLIP.pxd"] # Cython interface file + ,language="c++" + ,cython_cplus=True + ,undef_macros =[] # ["CFLAGS"] + ,define_macros=[] # [("USE_TCP_NODELAY",1)] + ,extra_compile_args=["-std=c++14", "-O3"], + # c++98/c++11/c++14/c++17/c++2a + # py2 : c++14,py3:c++2a +)) + + +ext_modules=cythonize( # generate .c files. + ext_modules, + compiler_directives={"language_level":"3" if PY3 else "2"}, # "2","3","3str" +) + +setup(name="cPyHiSLIP", + version=rev, + author="Noboru Yamamoto, KEK, JAPAN", + author_email = "Noboru.YAMAMOTO@kek.jp", + description='A Cython based Python module to control devices over HiSLIP protocol.', + url="http://www-cont.j-parc.jp/", + classifiers=['Programming Language :: Python', + 'Programming Language :: Cython', + 'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator', + ], + ext_modules=ext_modules, + cmdclass = {'build_ext': build_ext, + # 'build_py':build_py # for 2to3 + }, + py_modules=[ ], + **extra +) diff --git a/asyn/drvAsynHiSLIP/drvAsynHiSLIP.cpp b/asyn/drvAsynHiSLIP/drvAsynHiSLIP.cpp new file mode 100644 index 000000000..0310f9235 --- /dev/null +++ b/asyn/drvAsynHiSLIP/drvAsynHiSLIP.cpp @@ -0,0 +1,758 @@ +/* + * ASYN support for HiSLIP + * + + *************************************************************************** + * Copyright (c) 2020 N. Yamamoto + * based on AsynUSBTMC supoort by + * Copyright (c) 2013 W. Eric Norum * + * This file is distributed subject to a Software License Agreement found * + * in the file LICENSE that is included with this distribution. * + *************************************************************************** + */ +#include "drvAsynHiSLIP.h" + +using nsHiSLIP::CC_request_t; +using nsHiSLIP::CC_Lock_t; +using nsHiSLIP::HiSLIP_t; +using nsHiSLIP::HiSLIP; +using nsHiSLIP::Message_t; + +#define MAX_PAYLOAD_CAPACITY 4096 +#define IDSTRING_CAPACITY 100 + +#define ASYN_REASON_SRQ 4345 +#define ASYN_REASON_STB 4346 +#define ASYN_REASON_REN 4347 + +// EPICS Async Support +typedef struct drvPvt { + /* + * Matched device + * HiSLIP private + */ + + HiSLIP_t *device; + char *hostname; + long port; + bool isConnected; + int termChar; + + char deviceVendorID[IDSTRING_CAPACITY]; + + /* + * Asyn interfaces we provide + */ + char *portName; + asynInterface asynCommon; + asynInterface asynOctet; + void *asynOctetInterruptPvt; + asynInterface asynDrvUser; + + /* + * Interrupt through async_channel handling + */ + bool enableInterruptSRQ; + char *interruptThreadName; + epicsThreadId interruptTid; + epicsMutexId interruptTidMutex; + epicsEventId pleaseTerminate; + epicsEventId didTerminate; + epicsMessageQueueId statusByteMessageQueue; + + /* + * I/O buffer: Asyn clients may request data size smaller than + max_payload size. + */ + unsigned char *buf;// + size_t bufSize; + size_t bufCount; + const unsigned char *bufp; //top of unread data buffer. + + /* + * Statistics + */ + + size_t connectionCount; + size_t interruptCount; + size_t bytesSentCount; + size_t bytesReceivedCount; +} drvPvt; + +static asynStatus disconnect(void *pvt, asynUser *pasynUser); + +// forward declarations. +static asynStatus +asynOctetFlush(void *pvt, asynUser *pasynUser); + +/* + * Interrupt endpoint support + */ +static void +interruptThread(void *arg) +{ + drvPvt *pdpvt = (drvPvt *)arg; + int s; + assert(pdpvt); + if (pdpvt == NULL){ + errlogPrintf(" NULL drvPvt as an argument.\n"); + } + + while(true) { + s = pdpvt->device->wait_for_Async(60000); // wait for async-channel. + + if (s == 0){ + errlogPrintf("timeout poll for async channel.\n"); + continue; + } + if (s > 0 ){ + int stb = pdpvt->device->get_Service_Request(); + errlogPrintf("Get SRQ with STB: 0x%2x\n", stb ); + if ((stb & 0x40) != 0){ // may need mask here. Just SRQ + ELLLIST *pclientList; + interruptNode *pnode; + + pdpvt->interruptCount +=1; + pasynManager->interruptStart(pdpvt->asynOctetInterruptPvt, + &pclientList); + { + pnode = (interruptNode *)ellFirst(pclientList); + // see asynDriver/asynDriver.h + while (pnode) { + //int eomReason=0; + // 0:None, ASYN_EOM_CNT:0x0001, + //ASYN_EOM_EOS:0x0002 End of StrIng detected + //ASYN_EOM_END: 0x0004 End indicator detected + //size_t numchars=1; + asynOctetInterrupt *octetInterrupt = + (asynOctetInterrupt *) pnode->drvPvt; + errlogPrintf("scan pnode in InterruptThread " + "pnode:%p reason:0x%x\n", + pnode, octetInterrupt->pasynUser->reason + ); + if (octetInterrupt->pasynUser->reason == ASYN_REASON_SRQ) { + octetInterrupt->callback(octetInterrupt->userPvt, + octetInterrupt->pasynUser, + (char *) &stb, + sizeof(stb), + ASYN_REASON_SRQ); + } + pnode = (interruptNode *)ellNext(&pnode->node); + } + pasynManager->interruptEnd(pdpvt->asynOctetInterruptPvt); + stb = pdpvt->device->status_query(); + errlogPrintf("Finish SRQ process 0x%2x\n", stb & 0xff); + } + } + continue; + } + else{ + errlogPrintf("srq poll return value:%d.\n",s); + } + if (epicsEventTryWait(pdpvt->pleaseTerminate) == epicsEventWaitOK){ + errlogPrintf("Terminate Interrupt Thread.\n"); + break; + } + }// while + epicsMutexLock(pdpvt->interruptTidMutex); + pdpvt->interruptTid = 0; + epicsEventSignal(pdpvt->didTerminate); + epicsMutexUnlock(pdpvt->interruptTidMutex); +} + +static void +startInterruptThread(drvPvt *pdpvt) +{ + epicsMutexLock(pdpvt->interruptTidMutex); + if (pdpvt->interruptTid == 0) { + epicsEventTryWait(pdpvt->pleaseTerminate); + epicsEventTryWait(pdpvt->didTerminate); + pdpvt->interruptTid = epicsThreadCreate(pdpvt->interruptThreadName, + epicsThreadGetPrioritySelf(), + epicsThreadGetStackSize(epicsThreadStackSmall), + interruptThread, + pdpvt); + if (pdpvt->interruptTid == 0) + errlogPrintf("----- WARNING ----- " + "Can't start interrupt handler thread %s.\n", + pdpvt->interruptThreadName); + } + epicsMutexUnlock(pdpvt->interruptTidMutex); +} + + +/* + * Show a byte number + */ +static void +pcomma(FILE *fp, size_t n) +{ + if (n < 1000) { + fprintf(fp, "%zu", n); + return; + } + pcomma(fp, n/1000); + fprintf(fp, ",%03zu", n%1000); +} + +static void +showCount(FILE *fp, const char *label, size_t count) +{ + fprintf(fp, "%22s Count: ", label); + pcomma(fp, count); + fprintf(fp, "\n"); +} + +/* + * asynCommon methods + */ +static void +report(void *pvt, FILE *fp, int details) +{ + drvPvt *pdpvt = (drvPvt *)pvt; + + fprintf(fp, "%20sonnected, Interrupt handler thread %sactive\n", + pdpvt->isConnected ? "C" : "Disc", + pdpvt->interruptTid ? "" : "in"); + if (details > 0) { + if (pdpvt->termChar >= 0) + fprintf(fp, "%28s: %x\n", "Terminator", pdpvt->termChar); + } + if (details > 1) { + showCount(fp, "Connection", pdpvt->connectionCount); + showCount(fp, "Interrupt", pdpvt->interruptCount); + showCount(fp, "Send", pdpvt->bytesSentCount); + showCount(fp, "Receive", pdpvt->bytesReceivedCount); + // + fprintf(fp, "HiSLIP device Info:\n"); + fprintf(fp, "\t" "%s: %lu\n", "maximum_message_size", pdpvt->device->maximum_message_size); + fprintf(fp, "\t" "%s: %lu\n", "maximum_payload_size", pdpvt->device->maximum_payload_size); + fprintf(fp, "\t" "%s: %x\n", "Terminator", pdpvt->termChar); + fprintf(fp, "\t" "HiSLIP device address:%s\n", + pdpvt->hostname); + fprintf(fp, "\t" "overlap/sync. mode:%s\n", + pdpvt->device->overlap_mode?"ovelap":"synchronized"); + fprintf(fp, "\t" "Session ID:%d\n", + pdpvt->device->session_id); + fprintf(fp, "\t" "server_protocol_version/server_vendorID : %#x %#x\n", + pdpvt->device->server_protocol_version, + pdpvt->device->server_vendorID); + fprintf(fp, "\t" "sync/async socket number %d/%d\n", + pdpvt->device->sync_channel, + pdpvt->device->async_channel); + fprintf(fp, "socket/locktime %ld/%ld\n", + pdpvt->device->socket_timeout, + pdpvt->device->lock_timeout); + fprintf(fp, "current message id/most recent message id %x/%x\n", + pdpvt->device->message_id,pdpvt->device->most_recent_message_id); + } +}; + +/* + * Disconnect when it appears that device has gone away + */ +static void +disconnectIfGone(drvPvt *pdpvt, asynUser *pasynUser, int s) +{ + if(s == nsHiSLIP::ServerRefusedConnection){ + disconnect(pdpvt, pasynUser); + } +} + + +static asynStatus +connect(void *pvt, asynUser *pasynUser) +{ + long rstatus; + drvPvt *pdpvt = (drvPvt *)pvt; + + if (!pdpvt->isConnected || (pdpvt->device == NULL)) { + if (pdpvt->device == NULL){ + pdpvt->device=new HiSLIP(); + } + pdpvt->device->connect(pdpvt->hostname); + } + pdpvt->isConnected = true; + pdpvt->connectionCount +=1; + + // setup private buffer for read. + if (pdpvt->bufSize > 0){ + rstatus=pdpvt->device->set_max_size(pdpvt->bufSize); + } + else{ + rstatus=pdpvt->device->set_max_size(nsHiSLIP::MAXIMUM_MESSAGE_SIZE); + } + pdpvt->bufSize=pdpvt->device->maximum_payload_size; + + if (pdpvt->bufSize <=0){ + errlogPrintf("HiSLIP::connect invalid buffersize\n"); + return asynError; + } + + pdpvt->buf=(unsigned char *)callocMustSucceed( + 1, + pdpvt->bufSize+1, + pdpvt->portName); + //ensure buffer ends with a null character. + *(pdpvt->buf + pdpvt->bufSize)=0x00; + + if(pdpvt->buf ==0){ + errlogPrintf("HiSLIP::failed to allocate input buffer\n"); + return asynError; + } + asynOctetFlush(pvt, pasynUser); + // pdpvt->bufCount=0; + // pdpvt->bufp=0; // NO data in buf. + pasynManager->exceptionConnect(pasynUser); + return asynSuccess; +} + +static asynStatus disconnect(void *pvt, asynUser *pasynUser) +{ + drvPvt *pdpvt = (drvPvt *)pvt; + + if (pdpvt->isConnected) { + int pass = 0; + epicsThreadId tid; + for (;;) { + epicsMutexLock(pdpvt->interruptTidMutex); + tid = pdpvt->interruptTid; + epicsMutexUnlock(pdpvt->interruptTidMutex); + if (tid == 0) + break; + if (++pass == 10) { + errlogPrintf + ("----- WARNING ----- Thread %s won't terminate!\n", + pdpvt->interruptThreadName); + break; + } + + /* + * Send signal then force an Interrupt-In message + */ + epicsEventSignal(pdpvt->pleaseTerminate); + pdpvt->device->status_query(); + // libusb_control_transfer(pdpvt->handle, + // 0xA1, // bmRequestType: Dir=IN, Type=CLASS, Recipient=INTERFACE + // 128, // bRequest: READ_STATUS_BYTE + // 127, // wValue (bTag) + // pdpvt->bInterfaceNumber, // wIndex + // cbuf, // data + // 3, // wLength + // 100); // timeout (ms) + epicsEventWaitWithTimeout(pdpvt->didTerminate, 2.0); + } + pdpvt->device->disconnect(); + } + pdpvt->isConnected = false; + pasynManager->exceptionDisconnect(pasynUser); + return asynSuccess; +} +static asynCommon commonMethods = { report, connect, disconnect }; + +/* + * asynOctet methods + */ +static asynStatus +asynOctetWrite(void *pvt, asynUser *pasynUser, + const char *data, size_t numchars, size_t *nbytesTransfered) +{ + drvPvt *pdpvt = (drvPvt *)pvt; + int timeout = pasynUser->timeout * 1000; + if (timeout == 0) timeout = 1; + + /* + * Common to all writes + */ + errlogPrintf("OctetWrite %s %ld %ld\n",data,numchars,*nbytesTransfered); + /* + * Send + */ + *nbytesTransfered = 0; + while (numchars) { + size_t nSent; + + nSent = pdpvt->device->write((u_int8_t *) data, numchars, timeout); + + if (nSent < 0) { + disconnectIfGone(pdpvt, pasynUser, 0); + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "data transfer failed: %s", + "Error in write operation"); + return asynError; + } + if (nSent != numchars) { + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Asked to send %lu, actually sent %lu", numchars,nSent); + return asynError; + } + data += nSent; + numchars -= nSent; + *nbytesTransfered += nSent; + } + pdpvt->bytesSentCount += *nbytesTransfered; + return asynSuccess; +} + +static asynStatus +asynOctetRead(void *pvt, asynUser *pasynUser, + char *data, size_t maxchars, size_t *nbytesTransfered, + int *eomReason) +{ + drvPvt *pdpvt = (drvPvt *)pvt; + size_t ioCount, nCopy; + int status; + int eom=0; + size_t nout=0; + int timeout = pasynUser->timeout * 1000; + if (timeout == 0) timeout = 1; + + assert(pdpvt->device); + assert(nbytesTransfered); + + if(eomReason) {*eomReason=eom;} + if(nbytesTransfered) {*nbytesTransfered = nout = 0;} + + // errlogPrintf("asynOctetRead timeout:%g host:%s maxchars:%ld bufCount:%ld reason:%x\n", + // pasynUser->timeout, + // pdpvt->hostname, + // maxchars, pdpvt->bufCount, + // pasynUser->reason); + // /* + // * Special case for stream device which requires an asynTimeout return. + // */ + while(eom==0){ + if ((pasynUser->timeout == 0) && (pdpvt->bufCount == 0)) + return asynTimeout; + if (pdpvt->bufCount ==0) { + // read adittional data from device + status = pdpvt->device->read( &ioCount, + (u_int8_t *) pdpvt->buf, + pdpvt->bufSize, + timeout); + epicsSnprintf(pasynUser->errorMessage, + pasynUser->errorMessageSize, + "HiSLIP-asynOctetRead" + " ioCount: %ld, buffer: %p, bffersize:%lu, timeout:%d status:%d\n", + ioCount, pdpvt->buf, pdpvt->bufSize, timeout, status); + + if (status != 0) { + disconnectIfGone(pdpvt, pasynUser, 0); + epicsSnprintf(pasynUser->errorMessage, + pasynUser->errorMessageSize, + "Data transfer request failed: %s %d", + "Error in read operation",status); + return asynError; + } + assert(ioCount); + pdpvt->bufp=pdpvt->buf; + pdpvt->bufCount = ioCount; + pdpvt->bytesReceivedCount += ioCount; + epicsSnprintf(pasynUser->errorMessage, + pasynUser->errorMessageSize, + "HiSLIP-asynOctetRead data:%s, " + "maxchars:%ld, nbytesTransfered:%ld, eomReason:%d\n", + data, maxchars, nout, eom); + } + if (pdpvt->bufCount) { + if (maxchars > pdpvt->bufCount){ + nCopy = pdpvt->bufCount; + } + else{ + nCopy = maxchars; + } + memcpy(data, pdpvt->bufp, nCopy); + pdpvt->bufCount -= nCopy; + if(pdpvt->bufCount<0){ + errlogPrintf("woo. something wrong"); + exit(-1); + } + if (pdpvt->bufCount == 0){ + pdpvt->bufp = 0; + } + else{ + pdpvt->bufp += nCopy; + } + maxchars -= nCopy; + nout += nCopy; + pdpvt->bytesReceivedCount += nCopy; + data += nCopy; + if (maxchars == 0){ + eom |= ASYN_EOM_CNT; + } + if(pdpvt->bufCount == 0 ){ + if (pdpvt->device->rmt_delivered){ + eom |=ASYN_EOM_END; + } + } + } + } + if(eomReason) *eomReason = eom; + if(nbytesTransfered) {*nbytesTransfered = nout;} + + return asynSuccess; +} + +/* + * I see no mechanism for determining when it is necessary/possible to issue + * MESSAGE_ID_REQUEST_DEV_DEP_MSG_IN requests and transfers from the bulk-IN + * endpoint. I welcome suggestions from libusb experts. + */ +static asynStatus +asynOctetFlush(void *pvt, asynUser *pasynUser) +{ + drvPvt *pdpvt = (drvPvt *)pvt; + + errlogPrintf("OctetFlush:\n"); + pdpvt->bufCount = 0; + pdpvt->bufp = 0; + return asynSuccess; +} + +static asynStatus +asynOctetSetInputEos(void *pvt, asynUser *pasynUser, const char *eos, int eoslen) +{ + drvPvt *pdpvt = (drvPvt *)pvt; + + if (eoslen == 0) { + pdpvt->termChar = -1; + return asynSuccess; + } + else if (eoslen == 1) { + pdpvt->termChar = *eos & 0xff; + return asynSuccess; + } + else{ + pdpvt->termChar = -1; + epicsSnprintf(pasynUser->errorMessage, pasynUser->errorMessageSize, + "Device does not support multiple terminating characters %s %d", + eos, eoslen + ); + return asynError; + } +} + +static asynStatus +asynOctetGetInputEos(void *pvt, asynUser *pasynUser, char *eos, int eossize, int *eoslen) +{ + *eoslen = 0; + return asynSuccess; +} + +static asynStatus +asynOctetSetOutputEos(void *pvt, asynUser *pasynUser, const char *eos, int eoslen) +{ + return asynError; +} + +static asynStatus +asynOctetGetOutputEos(void *pvt, asynUser *pasynUser, char *eos, int eossize, int *eoslen) +{ + return asynError; +} + +static asynOctet octetMethods = { + .write = asynOctetWrite, + .read = asynOctetRead, + .flush = asynOctetFlush, + .setInputEos = asynOctetSetInputEos, + .getInputEos = asynOctetGetInputEos, + .setOutputEos = asynOctetSetOutputEos, + .getOutputEos = asynOctetGetOutputEos, +}; + +/* + * drvUser methods + */ +static asynStatus +asynDrvUserCreate(void *pvt, asynUser *pasynUser, + const char *drvInfo, const char **pptypeName, size_t *psize) +{ + drvPvt *pdpvt = (drvPvt *)pvt; + + errlogPrintf("DrvUserCreate drvinfo:%s\n", + drvInfo); + + if (epicsStrCaseCmp(drvInfo, "SRQ") == 0) { + pasynUser->reason = ASYN_REASON_SRQ; + pdpvt->enableInterruptSRQ = true; + if (pdpvt->isConnected) + startInterruptThread(pdpvt); + } + else if (epicsStrCaseCmp(drvInfo, "REN") == 0) { + pasynUser->reason = ASYN_REASON_REN; + } + else if (epicsStrCaseCmp(drvInfo, "STB") == 0) { + pasynUser->reason = ASYN_REASON_STB; + } + return asynSuccess; +} + +static asynStatus +asynDrvUserGetType(void *drvPvt, asynUser *pasynUser, + const char **pptypeName, size_t *psize) +{ + return asynSuccess; +} + +static asynStatus +asynDrvUserDestroy(void *drvPvt, asynUser *pasynUser) +{ + return asynSuccess; +} + +static asynDrvUser drvUserMethods = { + .create = asynDrvUserCreate, + .getType = asynDrvUserGetType, + .destroy = asynDrvUserDestroy, +}; + +/* + * Device configuration + */ +void +HiSLIPConfigure(const char *portName, + const char *hostInfo, + const int messagesize, + int priority) +{ + drvPvt *pdpvt; + asynStatus status; + int flags=0; + + /* + * Set up local storage + */ + pdpvt = (drvPvt *)callocMustSucceed(1, sizeof(drvPvt), portName); + pdpvt->portName = epicsStrDup(portName); + pdpvt->interruptThreadName = (char *) callocMustSucceed(1, + strlen(portName)+5, + portName); + epicsSnprintf(pdpvt->interruptThreadName, + sizeof pdpvt->interruptThreadName, + "%sIntr", portName); + if (priority == 0) priority = epicsThreadPriorityMedium; + + pdpvt->hostname=(char *)callocMustSucceed(1, + strlen(hostInfo)+1, + "HiSLIPConfigure"); + strcpy(pdpvt->hostname, hostInfo); + + pdpvt->bufSize=messagesize; + + pdpvt->termChar = 0; + pdpvt->interruptTidMutex = epicsMutexMustCreate(); + pdpvt->pleaseTerminate = epicsEventMustCreate(epicsEventEmpty); + pdpvt->didTerminate = epicsEventMustCreate(epicsEventEmpty); + pdpvt->statusByteMessageQueue = epicsMessageQueueCreate(1, 1); + + if (!pdpvt->statusByteMessageQueue) { + errlogPrintf("Can't create message queue!\n"); + return; + } + + /* + * Create our port + */ + errlogPrintf("* registerPort to asynManager \n"); + status = pasynManager->registerPort(pdpvt->portName, + ASYN_CANBLOCK, + (flags & 0x1) == 0, + priority, 0); + if(status != asynSuccess) { + errlogPrintf("registerPort failed portname %s\n", + pdpvt->portName); + return; + } + + /* + * Register ASYN interfaces + */ + errlogPrintf("* Register ASYN interfaces \n"); + + pdpvt->asynCommon.interfaceType = asynCommonType; + pdpvt->asynCommon.pinterface = &commonMethods; + pdpvt->asynCommon.drvPvt = pdpvt; + + status = pasynManager->registerInterface(pdpvt->portName, &pdpvt->asynCommon); + + if (status != asynSuccess) { + errlogPrintf("registerInterface failed\n"); + return; + } + + errlogPrintf("* Register ASYN Octet interfaces \n"); + pdpvt->asynOctet.interfaceType = asynOctetType; + pdpvt->asynOctet.pinterface = &octetMethods; + pdpvt->asynOctet.drvPvt = pdpvt; + status = pasynOctetBase->initialize(pdpvt->portName, + &pdpvt->asynOctet, + 0, + 0, + 0); + if (status != asynSuccess) { + errlogPrintf("pasynOctetBase->initialize failed\n"); + return; + } + + /* + * Always register an interrupt source, just in case we use SRQs + */ + errlogPrintf("* Register Interrupt source \n"); + pasynManager->registerInterruptSource(pdpvt->portName, + &pdpvt->asynOctet, + &pdpvt->asynOctetInterruptPvt); + + pdpvt->asynDrvUser.interfaceType = asynDrvUserType; + pdpvt->asynDrvUser.pinterface = &drvUserMethods; + pdpvt->asynDrvUser.drvPvt = pdpvt; + status = pasynManager->registerInterface(pdpvt->portName, + &pdpvt->asynDrvUser); + if (status != asynSuccess) { + errlogPrintf("Can't register drvUser\n"); + return; + } +} + +/* + * IOC shell command registration + */ +static const iocshArg HiSLIPConfigureArg0 = {"port name", + iocshArgString}; +static const iocshArg HiSLIPConfigureArg1 = {"HiSLIP host address", + iocshArgString}; +static const iocshArg HiSLIPConfigureArg2 = {"MessageSize", + iocshArgInt}; +static const iocshArg HiSLIPConfigureArg3 = {"priority", + iocshArgInt}; +static const iocshArg *HiSLIPConfigureArgs[] = { + &HiSLIPConfigureArg0 + , &HiSLIPConfigureArg1 + , &HiSLIPConfigureArg2 + , &HiSLIPConfigureArg3 +}; + +static const iocshFuncDef HiSLIPConfigureFuncDef = + {"HiSLIPConfigure", 4, HiSLIPConfigureArgs}; + +static void HiSLIPConfigureCallFunc(const iocshArgBuf *args) +{ + HiSLIPConfigure (args[0].sval, + args[1].sval, + args[2].ival, + args[3].ival); +} + +/* + * This routine is called before multitasking has started, so there's + * no race condition in the test/set of firstTime. + */ +static void HiSLIPRegisterCommands (void) +{ + static int firstTime = 1; + if (firstTime) { + firstTime = 0; + iocshRegister(&HiSLIPConfigureFuncDef, HiSLIPConfigureCallFunc); + } +} +epicsExportRegistrar(HiSLIPRegisterCommands); diff --git a/asyn/drvAsynHiSLIP/drvAsynHiSLIP.dbd b/asyn/drvAsynHiSLIP/drvAsynHiSLIP.dbd new file mode 100644 index 000000000..c3bd03f38 --- /dev/null +++ b/asyn/drvAsynHiSLIP/drvAsynHiSLIP.dbd @@ -0,0 +1 @@ +registrar(HiSLIPRegisterCommands) diff --git a/asyn/drvAsynHiSLIP/drvAsynHiSLIP.h b/asyn/drvAsynHiSLIP/drvAsynHiSLIP.h new file mode 100644 index 000000000..59bd49911 --- /dev/null +++ b/asyn/drvAsynHiSLIP/drvAsynHiSLIP.h @@ -0,0 +1,48 @@ +/* + * ASYN support for HiSLIP + * + + *************************************************************************** + * Copyright (c) 2020 N. Yamamoto + * based on AsynUSBTMC supoort by + * Copyright (c) 2013 W. Eric Norum * + * This file is distributed subject to a Software License Agreement found * + * in the file LICENSE that is included with this distribution. * + *************************************************************************** + */ +//-*- coding:utf-8 -*- +#define NDEBUG 1 +#define DEBUG 1 + +#include + +#include +#include +#include +#ifdef __linux__ +# include // network endian is "be". +#endif +#include +//#include + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "HiSLIPMessage.h" diff --git a/asyn/vxi11/drvVxi11.c b/asyn/vxi11/drvVxi11.c index 28beb7e8f..c9439390d 100644 --- a/asyn/vxi11/drvVxi11.c +++ b/asyn/vxi11/drvVxi11.c @@ -583,7 +583,7 @@ static enum clnt_stat clientIoCall(vxiPort * pvxiPort,asynUser *pasynUser, if(stat!=RPC_SUCCESS) { asynPrint(pasynUser,ASYN_TRACE_ERROR, "%s vxi11 clientIoCall errno %s clnt_stat %d\n", - pvxiPort->portName,strerror(errno),stat); + pvxiPort->portName,strerror(errno), stat); if(stat!=RPC_TIMEDOUT) vxiDisconnectPort(pvxiPort); } return stat; @@ -726,6 +726,9 @@ static void vxiCreateIrqChannel(vxiPort *pvxiPort,asynUser *pasynUser) xdr_free((const xdrproc_t) xdr_Device_Error, (char *) &devErr); } else { vxiSrqEnable(pvxiPort,1); + asynPrint(pasynUser, ASYN_TRACE_FLOW, + "%s vxiCreateIrqChannel %s (create_intr_chan) created\n", + pvxiPort->portName, vxiError(devErr.error)); xdr_free((const xdrproc_t) xdr_Device_Error, (char *) &devErr); return; } @@ -1051,7 +1054,12 @@ static void vxiReport(void *drvPvt,FILE *fd,int details) fprintf(fd," ip address:%s\n", nameBuf); fprintf(fd," vxi name:%s", pvxiPort->vxiName); fprintf(fd," ctrlAddr:%d",pvxiPort->ctrlAddr); - fprintf(fd," maxRecvSize:%lu", pvxiPort->maxRecvSize); + fprintf(fd," maxRecvSize:%lu\n", pvxiPort->maxRecvSize); + fprintf(fd," isSingleLink:%s isGpibLink:%s hasSRQ:%s\n", + ((pvxiPort->isSingleLink) ? "yes" : "no"), + ((pvxiPort->isGpibLink) ? "yes" : "no"), + ((pvxiPort->hasSRQ) ? "yes" : "no") + ); fprintf(fd," isSingleLink:%s isGpibLink:%s\n", ((pvxiPort->isSingleLink) ? "yes" : "no"), ((pvxiPort->isGpibLink) ? "yes" : "no")); diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE index 538cd55ce..ea2b8af8c 100644 --- a/configure/CONFIG_SITE +++ b/configure/CONFIG_SITE @@ -34,7 +34,7 @@ CHECK_RELEASE = YES # If you have the local Linux GPIB support installed (http://sourceforge.net/projects/linux-gpib/) # then set LINUX_GPIB=YES -LINUX_GPIB=NO +#LINUX_GPIB=NO # If you have libusb-1.0 revision 16 or newer and want the USB TMC support set DRV_USBTMC=YES #DRV_USBTMC=YES @@ -53,6 +53,7 @@ DRV_FTDI_USE_LIBFTDI1=NO # If you want to build asyn so the only dependency on EPICS base is libCom then set the following flag #EPICS_LIBCOM_ONLY=YES +EPICS_LIBCOM_ONLY=NO # Some linux systems moved RPC related symbols to libtirpc # To enable linking against this library, uncomment the following line diff --git a/configure/CONFIG_SITE.darwin-x86.Common b/configure/CONFIG_SITE.darwin-x86.Common new file mode 100644 index 000000000..2494e8e48 --- /dev/null +++ b/configure/CONFIG_SITE.darwin-x86.Common @@ -0,0 +1,14 @@ +# If you have libusb-1.0 and libftdi, and want FTDI support, set DRV_FTDI=YES +#DRV_FTDI=YES + +# If your system has libftdi1, set the following to YES. If it has libftdi, set it to NO +#DRV_FTDI_USE_LIBFTDI1=YES + + +ifeq (linux-x86_64, $(T_A)) + DRV_HISLIP=YES +else + ifeq (darwin-x86, $(T_A)) + DRV_HISLIP=YES + endif +endif diff --git a/configure/CONFIG_SITE.linux-x86_64.Common b/configure/CONFIG_SITE.linux-x86_64.Common index e24205eaf..ee2a01494 100644 --- a/configure/CONFIG_SITE.linux-x86_64.Common +++ b/configure/CONFIG_SITE.linux-x86_64.Common @@ -3,3 +3,15 @@ # If your system has libftdi1, set the following to YES. If it has libftdi, set it to NO #DRV_FTDI_USE_LIBFTDI1=YES + + +# asyn/asyn/drvHiSLIP support +# ifeq (linux-x86_64, $(T_A)) +# DRV_HISLIP=YES +# else +# ifeq (darwin-x86, $(T_A)) +# DRV_HISLIP=YES +# endif +# endif + + diff --git a/configure/RELEASE b/configure/RELEASE index cf8155d79..5598ab6a2 100644 --- a/configure/RELEASE +++ b/configure/RELEASE @@ -1,22 +1,25 @@ #RELEASE Location of external products +MODULES=/opt/epics/R7/modules +SNCSEQ=$(realpath $(MODULES)/seq) +ASYN =$(realpath $(MODULES)/asyn) +STREAM=$(realpath $(MODULES)/StreamDevice/stream) -SUPPORT=/corvette/home/epics/devel +#SUPPORT=/corvette/home/epics/devel # IPAC is only necessary if support for Greensprings IP488 is required # IPAC release V2-7 or later is required. -IPAC=$(SUPPORT)/ipac-2-15 +#IPAC=$(SUPPORT)/ipac-2-15 # SEQ is required for testIPServer -SNCSEQ=$(SUPPORT)/seq-2-2-5 +#SNCSEQ=$(SUPPORT)/seq-2-2-5 +$SNCSEQ=/opt/epics/modules/soft/seq/seq-2.2.7 -## For sCalcout support in asynOctet - applications include asynCalc.dbd -#CALC=$(SUPPORT)/calc-3-7-3 - -# If CALC was built with SSCAN support then SSCAN must be defined for testEpicsApp +## for sCalcout support in asynOctet - applications include asynCalc.dbd +#CALC=$(SUPPORT)/calc-3-7-7 #SSCAN=$(SUPPORT)/sscan-2-11-3 # EPICS_BASE 3.14.6 or later is required -EPICS_BASE=/corvette/usr/local/epics-devel/base-7.0.4 +EPICS_BASE=/opt/epics/R7/base -include $(TOP)/../RELEASE.local -include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local