From 24e8ed57ca89c778e72d928d3afdc83fdadf8a8e Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 10 Jul 2018 10:16:41 -0500 Subject: [PATCH 01/21] Modifications (many hard-coded) to work on Windows. Can connect to the controller, but not read anything via USB. Can't connect to the controller via serial. May be a problem with the serial card. --- LinkamApp/src/Linkam_T96.cpp | 29 +++-- LinkamApp/src/Makefile | 10 +- LinkamSupport/Makefile | 17 +++ LinkamSupport/dllHeader.h | 104 ++++++++++++++++++ LinkamSupport/os/win32-x86/CommsWrapper.dll | Bin 0 -> 52736 bytes LinkamSupport/os/win32-x86/CommsWrapper.lib | Bin 0 -> 10660 bytes .../os/win32-x86/LinkamCommsLibrary.dll | Bin 0 -> 111104 bytes LinkamSupport/os/win32-x86/Multimedia.dll | Bin 0 -> 24576 bytes LinkamSupport/os/windows-x64/CommsWrapper.dll | Bin 0 -> 59392 bytes LinkamSupport/os/windows-x64/CommsWrapper.lib | Bin 0 -> 10450 bytes .../os/windows-x64/LinkamCommsLibrary.dll | Bin 0 -> 111104 bytes LinkamSupport/os/windows-x64/Multimedia.dll | Bin 0 -> 24576 bytes configure/RELEASE | 4 +- iocs/linkamIOC/configure/RELEASE | 3 +- .../iocBoot/ioclinkam/Linkam_T96.cmd | 5 +- iocs/linkamIOC/iocBoot/ioclinkam/Makefile | 3 +- .../linkamIOC/iocBoot/ioclinkam/start_ioc.bat | 7 ++ iocs/linkamIOC/linkamApp/src/Makefile | 16 ++- 18 files changed, 176 insertions(+), 22 deletions(-) create mode 100644 LinkamSupport/dllHeader.h create mode 100644 LinkamSupport/os/win32-x86/CommsWrapper.dll create mode 100644 LinkamSupport/os/win32-x86/CommsWrapper.lib create mode 100644 LinkamSupport/os/win32-x86/LinkamCommsLibrary.dll create mode 100644 LinkamSupport/os/win32-x86/Multimedia.dll create mode 100644 LinkamSupport/os/windows-x64/CommsWrapper.dll create mode 100644 LinkamSupport/os/windows-x64/CommsWrapper.lib create mode 100644 LinkamSupport/os/windows-x64/LinkamCommsLibrary.dll create mode 100644 LinkamSupport/os/windows-x64/Multimedia.dll create mode 100644 iocs/linkamIOC/iocBoot/ioclinkam/start_ioc.bat diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index 9fb4429..42c0c0a 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -1,6 +1,9 @@ #include -#include "dllImports.h" - +//#ifdef WINDOWS +#include "dllHeader.h" +//#else +// #include "dllImports.h" +//#endif #include #include #include @@ -28,9 +31,9 @@ Linkam::Linkam(const char *portName, const char *serialPort) : asynPortDriver(po createParam(heaterPowerInValueString, asynParamFloat64, &heaterPowerInValue_); createParam(heatingOutValueString, asynParamInt32, &heatingOutValue_); - // Must be in the directory with the DLL when this line is executed - LoadMonoLibrary("LinkamCommsDll.dll"); - + // Must be in the directory with the DLL when this line is executed + //LoadMonoLibrary("LinkamCommsDll.dll"); + // Call to internal library method, will return wrapper version. wrapperVersion_ = GetWrapperVersion(); printf("Using Wrapper Version %s\n", wrapperVersion_); @@ -71,10 +74,12 @@ asynStatus Linkam::connect(asynUser *pasynUser) * 1, UInt32 u32Commtype: Connection protocol (0: USB, 1: Serial) * 3); UInt32 u32CommPort: Set to the COMM port that the controller is connected to. (e.g. COMM port 3) */ - // OpenComms only works for Windows-style numbering of COM ports - //commStatus_ = OpenComms(true, 1, 0); this->lock(); - commStatus_ = OpenCommsFromDevice(true, serialPort_); + // OpenComms only works for Windows-style numbering of COM ports + commStatus_ = OpenComms(true, 1, 1); + // USB on windows + //commStatus_ = OpenComms(true, 0, 6); + //commStatus_ = OpenCommsFromDevice(true, serialPort_); this->unlock(); /* We found the controller and everything is OK. Signal to asynManager that we are connected. */ @@ -97,10 +102,12 @@ asynStatus Linkam::disconnect(asynUser *pasynUser) asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Disconnecting...\n", driverName, functionName); - // OpenComms only works for Windows-style numbering of COM ports - //commStatus_ = OpenComms(false, 1, 0); this->lock(); - commStatus_ = OpenCommsFromDevice(false, serialPort_); + //commStatus_ = OpenCommsFromDevice(false, serialPort_); + // OpenComms only works for Windows-style numbering of COM ports + commStatus_ = OpenComms(false, 1, 1); + // USB on Windows + //commStatus_ = OpenComms(false, 1, 6); this->unlock(); /* We found the controller and everything is OK. Signal to asynManager that we are connected. */ diff --git a/LinkamApp/src/Makefile b/LinkamApp/src/Makefile index e61ad8d..81466ee 100644 --- a/LinkamApp/src/Makefile +++ b/LinkamApp/src/Makefile @@ -5,12 +5,13 @@ include $(TOP)/configure/CONFIG # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= -USR_CXXFLAGS=-I/usr/include/mono-2.0 +#USR_CXXFLAGS=-I/usr/include/mono-2.0 +#USR_CXXFLAGS=-clr:safe #================================================== # build a support library -LIBRARY_IOC_Linux += Linkam +LIBRARY_IOC += Linkam # xxxRecord.h will be created from xxxRecord.dbd #DBDINC += xxxRecord @@ -18,8 +19,11 @@ LIBRARY_IOC_Linux += Linkam DBD += LinkamSupport.dbd # specify all source files to be compiled and added to the library -Linkam_SRCS_Linux += Linkam_T96.cpp +Linkam_SRCS += Linkam_T96.cpp Linkam_LIBS_Linux += Wrapper +ifeq (win, $(findstring win, $(T_A))) +Linkam_LIBS += CommsWrapper +endif Linkam_Libs += asyn Linkam_LIBS += $(EPICS_BASE_IOC_LIBS) diff --git a/LinkamSupport/Makefile b/LinkamSupport/Makefile index 09c7a3d..8b71693 100644 --- a/LinkamSupport/Makefile +++ b/LinkamSupport/Makefile @@ -7,6 +7,7 @@ include $(TOP)/configure/CONFIG # Note, the following files were manually copied from the _____ directory. INC += dllExports.h INC += dllImports.h +INC += dllHeader.h ifeq (linux-x86_64, $(findstring linux-x86_64, $(T_A))) LIB_INSTALLS += ../os/linux-x86_64/libWrapper.a @@ -15,6 +16,22 @@ BIN_INSTALLS += ../os/linux-x86_64/libWrapper.so BIN_INSTALLS += ../os/linux-x86_64/LinkamCommsDll.dll endif +ifeq (win32-x86, $(findstring win32-x86, $(T_A))) +LIB_INSTALLS += ../os/win32-x86/CommsWrapper.lib +LIB_INSTALLS += ../os/win32-x86/CommsWrapper.dll +BIN_INSTALLS += ../os/win32-x86/CommsWrapper.dll +BIN_INSTALLS += ../os/win32-x86/LinkamCommsLibrary.dll +BIN_INSTALLS += ../os/win32-x86/Multimedia.dll +endif + +ifeq (windows-x64, $(findstring windows-x64, $(T_A))) +LIB_INSTALLS += ../os/windows-x64/CommsWrapper.lib +LIB_INSTALLS += ../os/windows-x64/CommsWrapper.dll +BIN_INSTALLS += ../os/windows-x64/CommsWrapper.dll +BIN_INSTALLS += ../os/windows-x64/LinkamCommsLibrary.dll +BIN_INSTALLS += ../os/windows-x64/Multimedia.dll +endif + #============================= include $(TOP)/configure/RULES diff --git a/LinkamSupport/dllHeader.h b/LinkamSupport/dllHeader.h new file mode 100644 index 0000000..b8373ef --- /dev/null +++ b/LinkamSupport/dllHeader.h @@ -0,0 +1,104 @@ +//Built on 11/04/2018 at 8:26:23.75 by UjasSidapara@LINKAM.LOCAL on SN1651-B +// CommsWrapper.dll SHA:E23D139EA70508CD11041786C3751D213558DCA4A2FAFFC606B4B16438FEA539 +//----------------------------------------------------------------------- +// +// Copyright (c) Linkam Scientific Ltd. All rights reserved. +// +// 17-Jan-2018 +// US (support@linkam.co.uk) +//-1.1.1 +//----------------------------------------------------------------------- +/// This file contains the dllimports/ dllexports for the wrapper. + +// Dependencies: +// - C runtime Library v120 +// -.net framework version 4.0 or newer +// - LinkamCommsLibrary.dll (managed) +#pragma once +#ifndef dllHeader_h + + #define dllHeader_h + // if DLLEXPORTS is defined, define as dllexports, + // otherwise declare dllimport prototypes. + #ifdef DLLEXPORTS + #define DLLAPI __declspec(dllexport) + #else + #define DLLAPI __declspec(dllimport) + #endif + #define DLLCALL __cdecl // Calling convention is __cdecl +#endif +// callbacks: +// typedef return_type ( calling convention *func_pointer) (other parameters) +typedef void(__stdcall *eventCallback)(void); // stdcall used for WINAPI compatibility. +typedef void(__stdcall *eventNewValueCallback)(unsigned long long); //UInt64 + +extern "C" // No name mangling +{ + // Open or close connection to controller + // (arg1: true=Connect, false=Disconnect arg2: 0=USB, 1=RS232, arg3: Comm port number) + DLLAPI unsigned int DLLCALL OpenComms(bool, unsigned int, unsigned int); // wraps Comms.OpenComms() + DLLAPI unsigned int DLLCALL OpenCommsFromDevice(bool bConnect, char* strSerialDevice); + + + // Methods in alphabetical order + DLLAPI bool DLLCALL ApplySampleCals(bool); // wraps Comms.ApplySampleCals() + // Toggle between uncalibrated and sample calibrated temperatures. Refer to example in html docs. + DLLAPI bool DLLCALL CalibrateTstDistance(void); + + DLLAPI bool DLLCALL CssApplyValues(void); + DLLAPI unsigned char DLLCALL CssCheckValues(void); + DLLAPI bool DLLCALL CssGotoReference(void); + DLLAPI bool DLLCALL CssSensorCal(unsigned int); + DLLAPI bool DLLCALL CssStartJogGap(bool); + DLLAPI bool DLLCALL CssStartJogRot(bool); + + DLLAPI int DLLCALL GetCommsPeriod(); + DLLAPI unsigned long long DLLCALL GetControllerConfig(void); // Controller configuration: i.e. boards connected + DLLAPI unsigned int DLLCALL GetControllerError(void); // Controller error status + DLLAPI char* DLLCALL GetControllerName(void); + DLLAPI char* DLLCALL GetControllerSerial(void); + DLLAPI char* DLLCALL GetDllVersion(void); // Version number of ManagedDLL + DLLAPI char* DLLCALL GetWrapperVersion(void); // Version number of Wrapper + + DLLAPI float DLLCALL GetMaxValue(unsigned int); + DLLAPI float DLLCALL GetMinValue(unsigned int); + DLLAPI float DLLCALL GetResolution(unsigned int); + DLLAPI unsigned long long DLLCALL GetStageConfig(void); // Unsigned 64-Bit Integer + DLLAPI char* DLLCALL GetStageName(void); + DLLAPI char* DLLCALL GetStageSerial(void); + DLLAPI unsigned long long DLLCALL GetStatus(void); // Unsigned 64-Bit Integer + DLLAPI float DLLCALL GetValue(unsigned int); + + DLLAPI bool DLLCALL SaveSampleCals(void); + + DLLAPI bool DLLCALL SetCommsPeriod(int); + DLLAPI bool DLLCALL SetLnpMode(bool); + DLLAPI bool DLLCALL SetLnpSpeed(unsigned int); + //DLLAPI bool DLLCALL SetSimulationMode(unsigned int); Deprecated as of v1.0.7/ SDK 2.3.4.1 + DLLAPI bool DLLCALL SetValue(unsigned int, float); + + DLLAPI bool DLLCALL StartHeating(bool); + DLLAPI bool DLLCALL StartMotors(bool, unsigned int); + DLLAPI bool DLLCALL StartVacuum(bool); + DLLAPI bool DLLCALL StartHumidity(bool); + DLLAPI bool DLLCALL StartHumidityDesiccantConditioning(bool); + + DLLAPI bool DLLCALL SetTstMode(bool); + DLLAPI bool DLLCALL TstZeroForce(void); + DLLAPI bool DLLCALL TstZeroPosition(void); + + // Comms Events + /*********************************************/ + /* Event and callback methods */ + /* See "helloLinkam" example in apiDocs */ + /*********************************************/ + // Start callbacks + DLLAPI void DLLCALL StartEvents(); + // Stop callbacks + DLLAPI void DLLCALL StopEvents(); + + // Set or Register callbacks. See typedefs above for declaration of *eventCallback and *eventNewValueCallback. + DLLAPI void DLLCALL SetControllerConnectedCallback(eventCallback); + DLLAPI void DLLCALL SetNewValueCallback(eventNewValueCallback); + DLLAPI void DLLCALL SetControllerDisconnectedCallback(eventCallback); +} diff --git a/LinkamSupport/os/win32-x86/CommsWrapper.dll b/LinkamSupport/os/win32-x86/CommsWrapper.dll new file mode 100644 index 0000000000000000000000000000000000000000..6289fd200b0b92c5147633762d2b89caa05d6e94 GIT binary patch literal 52736 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�EFO9Z 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 Y5%415MZk-I7XdEt<8 literal 0 HcmV?d00001 diff --git a/configure/RELEASE b/configure/RELEASE index 3c9991a..1a37fd6 100644 --- a/configure/RELEASE +++ b/configure/RELEASE @@ -24,10 +24,10 @@ TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top # If using the sequencer, point SNCSEQ at its top directory: #SNCSEQ=$(EPICS_BASE)/../modules/soft/seq -ASYN=/home/KPETERSN/Current/9idc_Linkam/support/asyn +ASYN=D:/epics/linkamDevel/support/asyn # EPICS_BASE usually appears last so other apps can override stuff: -EPICS_BASE=/APSshare/epics/base-3.14.12.5 +EPICS_BASE=D:/epics/base-3.15.5 # Set RULES here if you want to take build rules from somewhere # other than EPICS_BASE: diff --git a/iocs/linkamIOC/configure/RELEASE b/iocs/linkamIOC/configure/RELEASE index 07c0921..cbc6d56 100644 --- a/iocs/linkamIOC/configure/RELEASE +++ b/iocs/linkamIOC/configure/RELEASE @@ -25,11 +25,12 @@ TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top #SNCSEQ=$(EPICS_BASE)/../modules/soft/seq # +#LINKAM=D:/epics/linkamDevel/support/Linkam LINKAM=$(TOP)/../.. -include $(LINKAM)/configure/RELEASE # EPICS_BASE usually appears last so other apps can override stuff: -EPICS_BASE=/APSshare/epics/base-3.14.12.5 +#!EPICS_BASE= # Set RULES here if you want to take build rules from somewhere # other than EPICS_BASE: diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd index c0b53f4..97d8075 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd +++ b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd @@ -5,4 +5,7 @@ dbLoadRecords("$(ASYN)/db/asynRecord.db", "P=$(PREFIX),R=asyn,PORT=$(PORT),ADDR= ## Run Linkam C# code cd "${TOP}/bin/${ARCH}" -LinkamConfig("$(PORT)", "/dev/ttyUSB0") +# Linux +#!LinkamConfig("$(PORT)", "/dev/ttyUSB0") +# Windows +LinkamConfig("$(PORT)", "0") diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/Makefile b/iocs/linkamIOC/iocBoot/ioclinkam/Makefile index c68aac1..3380783 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/Makefile +++ b/iocs/linkamIOC/iocBoot/ioclinkam/Makefile @@ -1,6 +1,7 @@ TOP = ../.. include $(TOP)/configure/CONFIG #!ARCH = linux-x86_64 -ARCH = linux-x86_64-debug +#!ARCH = linux-x86_64-debug +ARCH = windows-x64-static TARGETS = envPaths include $(TOP)/configure/RULES.ioc diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/start_ioc.bat b/iocs/linkamIOC/iocBoot/ioclinkam/start_ioc.bat new file mode 100644 index 0000000..96a1837 --- /dev/null +++ b/iocs/linkamIOC/iocBoot/ioclinkam/start_ioc.bat @@ -0,0 +1,7 @@ +@echo off + +set EPICS_HOST_ARCH=windows-x64-static + +..\..\bin\%EPICS_HOST_ARCH%\linkam.exe st.cmd + +pause diff --git a/iocs/linkamIOC/linkamApp/src/Makefile b/iocs/linkamIOC/linkamApp/src/Makefile index b487bed..28cd247 100644 --- a/iocs/linkamIOC/linkamApp/src/Makefile +++ b/iocs/linkamIOC/linkamApp/src/Makefile @@ -8,13 +8,20 @@ include $(TOP)/configure/CONFIG #============================= # Build the IOC application -PROD_IOC_Linux = linkam +PROD_IOC = linkam # linkam.dbd will be created and installed DBD += linkam.dbd # +ifeq (linux-x86_64, $(findstring linux-x86_64, $(T_A))) BIN_INSTALLS += ${LINKAM}/bin/${EPICS_HOST_ARCH}/LinkamCommsDll.dll BIN_INSTALLS += ${LINKAM}/bin/${EPICS_HOST_ARCH}/libWrapper.so +endif +ifeq (win, $(findstring win, $(T_A))) +BIN_INSTALLS += ${LINKAM}/bin/${EPICS_HOST_ARCH}/CommsWrapper.dll +BIN_INSTALLS += ${LINKAM}/bin/${EPICS_HOST_ARCH}/LinkamCommsLibrary.dll +BIN_INSTALLS += ${LINKAM}/bin/${EPICS_HOST_ARCH}/Multimedia.dll +endif # linkam.dbd will be made up from these files: linkam_DBD += base.dbd @@ -25,10 +32,13 @@ linkam_DBD += LinkamSupport.dbd # Add all the support libraries needed by this IOC PROD_SYS_LIBS_Linux += mono-2.0 -linkam_LIBS_Linux += Linkam +linkam_LIBS += Linkam # The following line isn't necessary, but it makes the depenency explicit linkam_LIBS_Linux += Wrapper -linkam_LIBS_Linux += asyn +ifeq (win, $(findstring win, $(T_A))) +linkam_LIBS += CommsWrapper +endif +linkam_LIBS += asyn # linkam_registerRecordDeviceDriver.cpp derives from linkam.dbd linkam_SRCS += linkam_registerRecordDeviceDriver.cpp -- GitLab From 1665f255d0b9bd403359576651568609f14650c3 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 10 Jul 2018 10:17:25 -0500 Subject: [PATCH 02/21] Added readme --- LinkamSupport/README.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LinkamSupport/README.txt diff --git a/LinkamSupport/README.txt b/LinkamSupport/README.txt new file mode 100644 index 0000000..90d1380 --- /dev/null +++ b/LinkamSupport/README.txt @@ -0,0 +1,7 @@ +dllExports.h + used for building the C wrapper +dllImports.h + used for building the EPICS support on Linux +dllHeader.h + used for building the EPICS support on Windows + -- GitLab From 83f412dce1b21c52fce093e0e1f0e00d21b501f4 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 31 Jul 2018 14:54:45 -0500 Subject: [PATCH 03/21] Initialize PVs --- LinkamApp/Db/Linkam_T96.db | 10 ++++++---- LinkamApp/Db/Linkam_T96_settings.req | 5 +++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 LinkamApp/Db/Linkam_T96_settings.req diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index 6de8813..f523484 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -15,12 +15,13 @@ record(ao, "$(P)$(T):rampLimit") { field(DTYP, "asynFloat64") field(OUT, "@asyn($(PORT),$(ADDR))RAMP_LIMIT_OUT_VALUE") - #field(PINI, "YES") + field(PINI, "YES") field(LINR, "NO CONVERSION") field(PREC, "4") field(EGU, "Celsius") field(SCAN, "Passive") - field(VAL, "22.0") + field(VAL, "20.0") + field(FLNK, "$(P)$(T):rampLimit_RBV") } record(ai, "$(P)$(T):rampLimit_RBV") @@ -38,12 +39,13 @@ record(ao, "$(P)$(T):rampRate") { field(DTYP, "asynFloat64") field(OUT, "@asyn($(PORT),$(ADDR))RAMP_RATE_OUT_VALUE") - #field(PINI, "YES") + field(PINI, "YES") field(LINR, "NO CONVERSION") field(PREC, "4") field(EGU, "Deg C / min") field(SCAN, "Passive") field(VAL, "20.0") + field(FLNK, "$(P)$(T):rampRate_RBV") } record(ai, "$(P)$(T):rampRate_RBV") @@ -72,7 +74,7 @@ record(bo, "$(P)$(T):heating") { field(DTYP, "asynInt32") field(OUT, "@asyn($(PORT),$(ADDR))HEATING_OUT_VALUE") - #field(PINI, "YES") + field(PINI, "YES") field(ZNAM, "Off") field(ONAM, "On") field(VAL, "0") diff --git a/LinkamApp/Db/Linkam_T96_settings.req b/LinkamApp/Db/Linkam_T96_settings.req new file mode 100644 index 0000000..c5e7691 --- /dev/null +++ b/LinkamApp/Db/Linkam_T96_settings.req @@ -0,0 +1,5 @@ +$(P)$(T):rampLimit +$(P)$(T):rampRate +$(P)$(T):heating +$(P)$(T):temperature_RBV.SCAN +$(P)$(T):heaterPower_RBV.SCAN \ No newline at end of file -- GitLab From 3c171fcbc9769755cec84c9c89dfa3a3689f9ba2 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 31 Jul 2018 15:43:32 -0500 Subject: [PATCH 04/21] Install databases locally to reduce what needs to be deployed for Windows IOCs. --- iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd | 4 ++-- iocs/linkamIOC/linkamApp/Db/Makefile | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd index 97d8075..b80e95a 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd +++ b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd @@ -1,7 +1,7 @@ # -dbLoadRecords("$(LINKAM)/db/Linkam_T96.db", "P=$(PREFIX),T=tc1,PORT=$(PORT),ADDR=0") +dbLoadRecords("$(TOP)/db/Linkam_T96.db", "P=$(PREFIX),T=tc1,PORT=$(PORT),ADDR=0") # -dbLoadRecords("$(ASYN)/db/asynRecord.db", "P=$(PREFIX),R=asyn,PORT=$(PORT),ADDR=0,OMAX=255,IMAX=255") +dbLoadRecords("$(TOP)/db/asynRecord.db", "P=$(PREFIX),R=asyn,PORT=$(PORT),ADDR=0,OMAX=255,IMAX=255") ## Run Linkam C# code cd "${TOP}/bin/${ARCH}" diff --git a/iocs/linkamIOC/linkamApp/Db/Makefile b/iocs/linkamIOC/linkamApp/Db/Makefile index 983981d..4b7505a 100644 --- a/iocs/linkamIOC/linkamApp/Db/Makefile +++ b/iocs/linkamIOC/linkamApp/Db/Makefile @@ -12,6 +12,9 @@ include $(TOP)/configure/CONFIG # databases, templates, substitutions like this #DB += xxx.db +DB += $(LINKAM)/db/Linkam_T96.db +DB += $(ASYN)/db/asynRecord.db + #---------------------------------------------------- # If .db template is not named *.template add # _template = -- GitLab From e5b0f3ab0127ad474579ebff703631dc39a23c7c Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Mon, 6 Aug 2018 12:19:30 -0500 Subject: [PATCH 05/21] Allow commType and commPort to be specified in LinkamConfig call. --- LinkamApp/src/Linkam_T96.cpp | 40 ++++++++++++------- LinkamApp/src/Linkam_T96.h | 5 ++- .../iocBoot/ioclinkam/Linkam_T96.cmd | 12 ++++-- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index 42c0c0a..bf1a921 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -14,15 +14,18 @@ // Is this needed? using namespace std; -Linkam::Linkam(const char *portName, const char *serialPort) : asynPortDriver(portName, MAX_CONTROLLERS, +Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt32 commPort) : asynPortDriver(portName, MAX_CONTROLLERS, asynInt32Mask | asynFloat64Mask | asynDrvUserMask, asynInt32Mask | asynFloat64Mask, ASYN_MULTIDEVICE | ASYN_CANBLOCK, 1, /* ASYN_CANBLOCK=0, ASYN_MULTIDEVICE=1, autoConnect=1 */ 0, 0) /* Default priority and stack size */ { - serialPort_ = epicsStrDup(serialPort); static const char *functionName = "Linkam"; + // Add validation in the future + commType_ = commType; + commPort_ = commPort; + createParam(temperatureInValueString, asynParamFloat64, &temperatureInValue_); createParam(rampLimitOutValueString, asynParamFloat64, &rampLimitOutValue_); createParam(rampLimitInValueString, asynParamFloat64, &rampLimitInValue_); @@ -76,10 +79,14 @@ asynStatus Linkam::connect(asynUser *pasynUser) */ this->lock(); // OpenComms only works for Windows-style numbering of COM ports - commStatus_ = OpenComms(true, 1, 1); - // USB on windows + //commStatus_ = OpenComms(true, 1, 1); + // USB on windows //commStatus_ = OpenComms(true, 0, 6); + // A different call would be needed for Linux //commStatus_ = OpenCommsFromDevice(true, serialPort_); + // + commStatus_ = OpenComms(true, commType_, commPort_); + this->unlock(); /* We found the controller and everything is OK. Signal to asynManager that we are connected. */ @@ -105,9 +112,12 @@ asynStatus Linkam::disconnect(asynUser *pasynUser) this->lock(); //commStatus_ = OpenCommsFromDevice(false, serialPort_); // OpenComms only works for Windows-style numbering of COM ports - commStatus_ = OpenComms(false, 1, 1); - // USB on Windows + //commStatus_ = OpenComms(false, 1, 1); + // USB on Windows //commStatus_ = OpenComms(false, 1, 6); + // + commStatus_ = OpenComms(false, commType_, commPort_); + this->unlock(); /* We found the controller and everything is OK. Signal to asynManager that we are connected. */ @@ -366,28 +376,30 @@ asynStatus Linkam::setHeating(epicsInt32 value) void Linkam::report(FILE *fp, int details) { asynPortDriver::report(fp, details); - fprintf(fp, "* Port: %s, %s, commStatus=%d\n", this->portName, serialPort_, commStatus_); + fprintf(fp, "* Port: %s, commType=%d, commPort=%d, commStatus=%d\n", this->portName, commType_, commPort_, commStatus_); if (details >= 1) fprintf(fp, " interesting value = "); fprintf(fp, "\n"); } -extern "C" int LinkamConfig(const char *portName, const char *serialPort) +extern "C" int LinkamConfig(const char *portName, const epicsUInt32 commType, const epicsUInt32 commPort) { - Linkam *pLinkam = new Linkam(portName, serialPort); + Linkam *pLinkam = new Linkam(portName, commType, commPort); pLinkam = NULL; /* This is just to avoid compiler warnings */ return(asynSuccess); } static const iocshArg linkamArg0 = { "Port name", iocshArgString}; -static const iocshArg linkamArg1 = { "Serial port", iocshArgString}; -static const iocshArg * const linkamArgs[2] = {&linkamArg0, - &linkamArg1}; -static const iocshFuncDef linkamFuncDef = {"LinkamConfig", 2, linkamArgs}; +static const iocshArg linkamArg1 = { "Comm Type", iocshArgInt}; +static const iocshArg linkamArg2 = { "Comm port", iocshArgInt}; +static const iocshArg * const linkamArgs[3] = {&linkamArg0, + &linkamArg1, + &linkamArg2}; +static const iocshFuncDef linkamFuncDef = {"LinkamConfig", 3, linkamArgs}; static void linkamCallFunc(const iocshArgBuf *args) { - LinkamConfig(args[0].sval, args[1].sval); + LinkamConfig(args[0].sval, args[1].ival, args[2].ival); } void drvLinkamRegister(void) diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index 60e46ba..3cc1be6 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -32,7 +32,7 @@ static const char *driverName = "Linkam"; */ class Linkam : public asynPortDriver { public: - Linkam(const char *portName, const char *serialPort); + Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt32 commPort); /* These are the methods that we override from asynPortDriver */ virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); @@ -63,7 +63,8 @@ private: asynStatus setHeating(epicsInt32 value); void report(FILE *fp, int details); - char* serialPort_; + epicsUInt32 commType_; + epicsUInt32 commPort_; int commStatus_; float temperatureRbv_; char* wrapperVersion_; diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd index b80e95a..612397e 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd +++ b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd @@ -5,7 +5,11 @@ dbLoadRecords("$(TOP)/db/asynRecord.db", "P=$(PREFIX),R=asyn,PORT=$(PORT),ADDR=0 ## Run Linkam C# code cd "${TOP}/bin/${ARCH}" -# Linux -#!LinkamConfig("$(PORT)", "/dev/ttyUSB0") -# Windows -LinkamConfig("$(PORT)", "0") +# LinkamConfig( +# portName Desired asyn port name (created by driver) +# commType 0 = USB, 1 = RS232 +# commPort 0 = COM0, 1 = COM1, ... +# RS232, COM1 +LinkamConfig("$(PORT)", 1, 1) +# USB, device #6 +#!LinkamConfig("$(PORT)", 0, 6) -- GitLab From 76be441315d9343a15307833a9d7e3ae664f48fe Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Mon, 6 Aug 2018 14:04:06 -0500 Subject: [PATCH 06/21] Changed prefix and asyn record name. --- iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd | 2 +- iocs/linkamIOC/iocBoot/ioclinkam/st.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd index 612397e..5fa61ea 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd +++ b/iocs/linkamIOC/iocBoot/ioclinkam/Linkam_T96.cmd @@ -1,7 +1,7 @@ # dbLoadRecords("$(TOP)/db/Linkam_T96.db", "P=$(PREFIX),T=tc1,PORT=$(PORT),ADDR=0") # -dbLoadRecords("$(TOP)/db/asynRecord.db", "P=$(PREFIX),R=asyn,PORT=$(PORT),ADDR=0,OMAX=255,IMAX=255") +dbLoadRecords("$(TOP)/db/asynRecord.db", "P=$(PREFIX),R=tc1:asyn,PORT=$(PORT),ADDR=0,OMAX=255,IMAX=255") ## Run Linkam C# code cd "${TOP}/bin/${ARCH}" diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd b/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd index 55aa255..1a131bc 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd +++ b/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd @@ -5,7 +5,7 @@ < envPaths -epicsEnvSet("PREFIX", "kmp:") +epicsEnvSet("PREFIX", "ioc:") epicsEnvSet("PORT", "linkam1") ## Register all support components -- GitLab From 1b4ed6a28bdde8878133e346d781241146bcae9b Mon Sep 17 00:00:00 2001 From: kpetersn Date: Mon, 6 Aug 2018 14:08:13 -0500 Subject: [PATCH 07/21] Improved Linkam_T96.adl --- LinkamApp/op/adl/Linkam_T96.adl | 325 ++++++++++++++++++++++++-------- 1 file changed, 242 insertions(+), 83 deletions(-) diff --git a/LinkamApp/op/adl/Linkam_T96.adl b/LinkamApp/op/adl/Linkam_T96.adl index 161a4bf..c4bdea6 100644 --- a/LinkamApp/op/adl/Linkam_T96.adl +++ b/LinkamApp/op/adl/Linkam_T96.adl @@ -1,14 +1,14 @@ file { - name="/home/oxygen40/KPETERSN/Current/Linkam/Linkam/LinkamApp/op/adl/Linkam_T96.adl" - version=030111 + name="/home/KPETERSN/Current/9idc_Linkam/support/Linkam/LinkamApp/op/adl/Linkam_T96.adl" + version=030104 } display { object { - x=10 - y=37 - width=400 - height=280 + x=2272 + y=64 + width=560 + height=250 } clr=14 bclr=4 @@ -87,117 +87,276 @@ display { 1a7309, } } -"text update" { - object { - x=50 - y=141 - width=150 - height=40 - } - monitor { - chan="$(P)$(T):temperature_RBV" - clr=14 - bclr=4 - } - limits { - } -} -menu { +text { object { - x=227 - y=143 - width=120 - height=35 + x=15 + y=10 + width=530 + height=30 } - control { - chan="$(P)$(T):temperature_RBV.SCAN" + "basic attribute" { clr=14 - bclr=4 } + textix="Linkam T96" + align="horiz. centered" } composite { object { - x=138 - y=210 - width=120 - height=42 + x=15 + y=55 + width=530 + height=145 } "composite name"="" children { text { object { - x=153 - y=210 - width=90 - height=20 + x=15 + y=55 + width=200 + height=25 } "basic attribute" { - clr=62 + clr=14 } - "dynamic attribute" { - vis="if not zero" - chan="$(P)$(R).CNCT" + textix="Temperature" + } + text { + object { + x=15 + y=85 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Ramp Limit" + } + text { + object { + x=15 + y=115 + width=200 + height=25 + } + "basic attribute" { + clr=14 } - textix="Connected" - align="horiz. right" + textix="Ramp Rate (C/min)" } text { object { - x=138 - y=210 - width=120 - height=20 + x=15 + y=175 + width=200 + height=25 } "basic attribute" { - clr=20 + clr=14 } - "dynamic attribute" { - vis="if zero" - chan="$(P)$(R).CNCT" + textix="Heating" + } + "text update" { + object { + x=220 + y=55 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):temperature_RBV" + clr=14 + bclr=4 + } + limits { + } + } + "text update" { + object { + x=385 + y=85 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):rampLimit_RBV" + clr=14 + bclr=4 + } + limits { + } + } + "text update" { + object { + x=385 + y=115 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):rampRate_RBV" + clr=14 + bclr=4 + } + limits { + } + } + "text entry" { + object { + x=220 + y=85 + width=160 + height=25 + } + control { + chan="$(P)$(T):rampLimit" + clr=14 + bclr=4 + } + limits { + } + } + "text entry" { + object { + x=220 + y=115 + width=160 + height=25 + } + control { + chan="$(P)$(T):rampRate" + clr=14 + bclr=4 + } + limits { } - textix="Disconnected" - align="horiz. right" } menu { object { - x=138 - y=232 - width=120 - height=20 + x=385 + y=55 + width=160 + height=25 } control { - chan="$(P)$(R).CNCT" + chan="$(P)$(T):temperature_RBV.SCAN" clr=14 - bclr=51 + bclr=4 + } + } + text { + object { + x=15 + y=145 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Heater Power" + } + "text update" { + object { + x=220 + y=145 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):heaterPower_RBV" + clr=14 + bclr=4 + } + limits { + } + } + menu { + object { + x=385 + y=145 + width=160 + height=25 + } + control { + chan="$(P)$(T):heaterPower_RBV.SCAN" + clr=14 + bclr=4 + } + } + "text update" { + object { + x=385 + y=175 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):heating" + clr=14 + bclr=4 + } + format="string" + limits { + } + } + composite { + object { + x=220 + y=175 + width=160 + height=25 + } + "composite name"="" + children { + "message button" { + object { + x=220 + y=175 + width=75 + height=25 + } + control { + chan="$(P)$(T):heating" + clr=0 + bclr=63 + } + label="On" + release_msg="1" + } + "message button" { + object { + x=305 + y=175 + width=75 + height=25 + } + control { + chan="$(P)$(T):heating" + clr=0 + bclr=22 + } + label="Off" + release_msg="0" + } } } } } -text { - object { - x=69 - y=12 - width=249 - height=52 - } - "basic attribute" { - clr=14 - } - textix="Linkam T96" - align="horiz. centered" -} -"text update" { +"related display" { object { - x=51 - y=100 - width=300 - height=24 - } - monitor { - chan="$(P)$(T):temperature_RBV.NAME" - clr=14 - bclr=4 + x=445 + y=215 + width=100 + height=25 } - limits { + display[0] { + label="asyn record" + name="asynRecord.adl" + args="P=$(P),R=$(R)" } + clr=14 + bclr=4 + label="-More" } -- GitLab From 487c2315b2b3abcf9e01f47f880f5de6737d7c68 Mon Sep 17 00:00:00 2001 From: kpetersn Date: Mon, 6 Aug 2018 14:09:34 -0500 Subject: [PATCH 08/21] Added Linkam_T96.ui --- LinkamApp/op/adl/Linkam_T96.ui | 930 +++++++++++++++++++++++++++++++++ 1 file changed, 930 insertions(+) create mode 100644 LinkamApp/op/adl/Linkam_T96.ui diff --git a/LinkamApp/op/adl/Linkam_T96.ui b/LinkamApp/op/adl/Linkam_T96.ui new file mode 100644 index 0000000..f209f1c --- /dev/null +++ b/LinkamApp/op/adl/Linkam_T96.ui @@ -0,0 +1,930 @@ + + +MainWindow + + + + 2272 + 64 + 560 + 250 + + + + + +QWidget#centralWidget {background: rgba(187, 187, 187, 255);} +QPushButton::menu-indicator {image: url(none.png); width: 0} + +caTable { + font: 10pt; + background: cornsilk; + alternate-background-color: wheat; +} + +caLineEdit { + border-radius: 1px; + background: lightyellow; + color: black; + } + +caTextEntry { + color: rgb(127, 0, 63); + background-color: cornsilk; + selection-color: #0a214c; + selection-background-color: wheat; + border: 1px groove black; + border-radius: 1px; + padding: 1px; +} + +caTextEntry:focus { + padding: 0px; + border: 2px groove darkred; + border-radius: 1px; +} + +QPushButton { + border-color: #00b; + border-radius: 2px; + padding: 3px; + border-width: 1px; + + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(224, 239, 255, 255), + stop:0.5 rgba(199, 215, 230, 255), + stop:1 rgba(184, 214, 236, 255)); +} +QPushButton:hover { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(201, 226, 255, 255), + stop:0.5 rgba(177, 204, 230, 255), + stop:1 rgba(163, 205, 236, 255)); +} +QPushButton:pressed { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(174, 219, 255, 255), + stop:0.5 rgba(165, 199, 230, 255), + stop:1 rgba(134, 188, 236, 255)); +} + +QPushButton:disabled { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 rgba(174, 219, 255, 255), + stop:0.5 rgba(165, 199, 230, 255), + stop:1 rgba(134, 188, 236, 255)); +} + +caChoice { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0 #E1E1E1, stop: 0.4 #DDDDDD, + stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3); +} + +caChoice > QPushButton { + text-align: left; + padding: 1px; +} + +/* when font specified, no font sizing is done any more, font: 10pt; is not bad. You could Enable this when you converted from .adl files +caRelatedDisplay > QPushButton { +font: 10pt; +} + +caShellCommand > QPushButton { +font: 10pt; +} +*/ + +caSlider::groove:horizontal { +border: 1px solid #bbb; +background: lightgrey; +height: 20px; +border-radius: 4px; +} + +caSlider::handle:horizontal { +background: red; +border: 1px solid #777; +width: 13px; +margin-top: -2px; +margin-bottom: -2px; +border-radius: 2px; +} + + + + + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Linkam T96 + + + ESimpleLabel::WidthAndHeight + + + Qt::AlignAbsolute|Qt::AlignHCenter|Qt::AlignVCenter + + + + 15 + 10 + 530 + 30 + + + + + + + 15 + 55 + 532 + 147 + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Temperature + + + ESimpleLabel::WidthAndHeight + + + + 0 + 0 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Ramp Limit + + + ESimpleLabel::WidthAndHeight + + + + 0 + 30 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Ramp Rate (C/min) + + + ESimpleLabel::WidthAndHeight + + + + 0 + 60 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Heating + + + ESimpleLabel::WidthAndHeight + + + + 0 + 120 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 205 + 0 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):temperature_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 370 + 30 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampLimit_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 370 + 60 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampRate_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 205 + 30 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampLimit + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + caLineEdit::Static + + + decimal + + + + + + 205 + 60 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampRate + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + caLineEdit::Static + + + decimal + + + + + + 370 + 0 + 160 + 25 + + + + $(P)$(T):temperature_RBV.SCAN + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Heater Power + + + ESimpleLabel::WidthAndHeight + + + + 0 + 90 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 205 + 90 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):heaterPower_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 370 + 90 + 160 + 25 + + + + $(P)$(T):heaterPower_RBV.SCAN + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + + + + + 370 + 120 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):heating + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + string + + + caLineEdit::Static + + + + + + 205 + 120 + 162 + 27 + + + + + + 0 + 0 + 75 + 25 + + + + EPushButton::WidthAndHeight + + + $(P)$(T):heating + + + + 255 + 255 + 255 + + + + + 40 + 147 + 21 + + + + On + + + 1 + + + caMessageButton::Static + + + + + + 85 + 0 + 75 + 25 + + + + EPushButton::WidthAndHeight + + + $(P)$(T):heating + + + + 255 + 255 + 255 + + + + + 190 + 25 + 11 + + + + Off + + + 0 + + + caMessageButton::Static + + + + + + + + 445 + 215 + 100 + 25 + + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + -More + + + Menu + + + asyn record + + + asynRecord.adl + + + P=$(P),R=$(R) + + + false + + + caLabel_0 + caLabel_1 + caLabel_2 + caLabel_3 + caLabel_4 + caLabel_5 + caFrame_1 + caFrame_0 + caLineEdit_0 + caLineEdit_1 + caLineEdit_2 + caTextEntry_0 + caTextEntry_1 + caMenu_0 + caLineEdit_3 + caMenu_1 + caLineEdit_4 + caMessageButton_0 + caMessageButton_1 + caRelatedDisplay_0 + + + \ No newline at end of file -- GitLab From 7c72d958f024700d5bc00af6ced33653cd92e9de Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Thu, 6 Sep 2018 14:23:59 -0500 Subject: [PATCH 09/21] Added controller and stage queries for troubleshooting. They don't work as expected yet. --- LinkamApp/Db/Linkam_T96.db | 36 +++++++++++ LinkamApp/src/Linkam_T96.cpp | 122 ++++++++++++++++++++++++++++++++++- LinkamApp/src/Linkam_T96.h | 19 +++++- 3 files changed, 175 insertions(+), 2 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index f523484..dc069b3 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -79,3 +79,39 @@ record(bo, "$(P)$(T):heating") field(ONAM, "On") field(VAL, "0") } + +record(longin, "$(P)$(T):controllerConfig_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_CONFIG_IN_VALUE") + field(PINI, "YES") + field(EGU, "N/A") + field(SCAN, "Passive") +} + +record(longin, "$(P)$(T):controllerError_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_CONFIG_IN_VALUE") + field(PINI, "YES") + field(EGU, "N/A") + field(SCAN, "Passive") +} + +record(longin, "$(P)$(T):controllerStatus_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_CONFIG_IN_VALUE") + field(PINI, "YES") + field(EGU, "N/A") + field(SCAN, "Passive") +} + +record(longin, "$(P)$(T):stageConfig_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_CONFIG_IN_VALUE") + field(PINI, "YES") + field(EGU, "N/A") + field(SCAN, "Passive") +} \ No newline at end of file diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index bf1a921..f326f42 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -33,6 +33,10 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt createParam(rampRateInValueString, asynParamFloat64, &rampRateInValue_); createParam(heaterPowerInValueString, asynParamFloat64, &heaterPowerInValue_); createParam(heatingOutValueString, asynParamInt32, &heatingOutValue_); + createParam(controllerConfigInValueString, asynParamInt32, &controllerConfigInValue_); + createParam(controllerErrorInValueString, asynParamInt32, &controllerErrorInValue_); + createParam(controllerStatusInValueString, asynParamInt32, &controllerStatusInValue_); + createParam(stageConfigInValueString, asynParamInt32, &stageConfigInValue_); // Must be in the directory with the DLL when this line is executed //LoadMonoLibrary("LinkamCommsDll.dll"); @@ -372,13 +376,129 @@ asynStatus Linkam::setHeating(epicsInt32 value) return (status) ? asynSuccess : asynError; } +asynStatus Linkam::readInt32(asynUser *pasynUser, epicsInt32 *value) +{ + int function = pasynUser->reason; + asynStatus status = asynSuccess; + static const char *functionName = "readInt32"; + + asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, function = %d\n", + driverName, functionName, this->portName, function); + + // Get the current value; it can be reverted later if commands fail + getIntegerParam(function, value); + + if (function == controllerConfigInValue_) { + + status = readControllerConfig(&controllerConfig_); + + } else if (function == controllerErrorInValue_) { + + status = readControllerError(&controllerError_); + + } else if (function == controllerStatusInValue_) { + + status = readControllerStatus(&controllerStatus_); + + } else if (function == stageConfigInValue_) { + + status = readStageConfig(&stageConfig_); + + } + + // Increment the value so we can see that records are processing + if (*value > 100000) + *value = 0; + else + *value += 1; + + callParamCallbacks(); + if (status == 0) { + asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, wrote %d\n", + driverName, functionName, this->portName, value); + } else { + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR writing %d, status=%d\n", + driverName, functionName, this->portName, value, status); + } + + return (status==0) ? asynSuccess : asynError; +} + +asynStatus Linkam::readControllerConfig(epicsUInt64 *value) +{ + static const char *functionName = "readControllerConfig"; + + this->lock(); + *value = GetControllerConfig(); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %llx\n", + driverName, functionName, this->portName, *value); + + return asynSuccess; +} + +asynStatus Linkam::readControllerError(epicsUInt32 *value) +{ + static const char *functionName = "readControllerError"; + + this->lock(); + *value = GetControllerError(); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %lx\n", + driverName, functionName, this->portName, *value); + + return asynSuccess; +} + +asynStatus Linkam::readControllerStatus(epicsUInt64 *value) +{ + static const char *functionName = "readControllerStatus"; + + this->lock(); + *value = GetStatus(); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %llx\n", + driverName, functionName, this->portName, *value); + + return asynSuccess; +} + +asynStatus Linkam::readStageConfig(epicsUInt64 *value) +{ + static const char *functionName = "readStageConfig"; + + this->lock(); + *value = GetStageConfig(); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %llx\n", + driverName, functionName, this->portName, *value); + + return asynSuccess; +} + void Linkam::report(FILE *fp, int details) { asynPortDriver::report(fp, details); fprintf(fp, "* Port: %s, commType=%d, commPort=%d, commStatus=%d\n", this->portName, commType_, commPort_, commStatus_); if (details >= 1) - fprintf(fp, " interesting value = "); + { + fprintf(fp, " controller config = %llx\n", controllerConfig_); + fprintf(fp, " controller error = %lx\n", controllerError_); + fprintf(fp, " controller status = %llx\n", controllerStatus_); + fprintf(fp, " stage config = %llx\n", stageConfig_); + } fprintf(fp, "\n"); } diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index 3cc1be6..eac5751 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -26,6 +26,10 @@ static const char *driverName = "Linkam"; #define rampRateInValueString "RAMP_RATE_IN_VALUE" /* asynFloat64 r/o */ #define heaterPowerInValueString "HEATER_POWER_IN_VALUE" /* asynFloat64 r/o */ #define heatingOutValueString "HEATING_OUT_VALUE" /* asynInt32 r/w */ +#define controllerConfigInValueString "CONTROLLER_CONFIG_IN_VALUE" /* asynInt32 r/o */ +#define controllerErrorInValueString "CONTROLLER_ERROR_IN_VALUE" /* asynInt32 r/o */ +#define controllerStatusInValueString "CONTROLLER_STATUS_IN_VALUE" /* asynInt32 r/o */ +#define stageConfigInValueString "STAGE_CONFIG_IN_VALUE" /* asynInt32 r/o */ /* * Class definition for the Linkam class @@ -36,6 +40,7 @@ public: /* These are the methods that we override from asynPortDriver */ virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); + virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value); virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value); virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value); virtual asynStatus disconnect(asynUser *pasynUser); @@ -50,8 +55,12 @@ protected: int rampRateInValue_; int heaterPowerInValue_; int heatingOutValue_; + int controllerConfigInValue_; + int controllerErrorInValue_; + int controllerStatusInValue_; + int stageConfigInValue_; #define FIRST_LINKAM_PARAM temperatureInValue_; - #define LAST_LINKAM_PARAM heatingOutValue_; + #define LAST_LINKAM_PARAM stageConfigInValue_; private: asynStatus readTemperature(epicsFloat64 *value); @@ -61,6 +70,10 @@ private: asynStatus setRampRate(epicsFloat64 value); asynStatus readHeaterPower(epicsFloat64 *value); asynStatus setHeating(epicsInt32 value); + asynStatus readControllerConfig(epicsUInt64 *value); + asynStatus readControllerError(epicsUInt32 *value); + asynStatus readControllerStatus(epicsUInt64 *value); + asynStatus readStageConfig(epicsUInt64 *value); void report(FILE *fp, int details); epicsUInt32 commType_; @@ -69,6 +82,10 @@ private: float temperatureRbv_; char* wrapperVersion_; char* libraryVersion_; + epicsUInt64 controllerConfig_; + epicsUInt32 controllerError_; + epicsUInt64 controllerStatus_; + epicsUInt64 stageConfig_; }; #define NUM_PARAMS ((int)(&LAST_LINKAM_PARAM - &FIRST_LINKAM_PARAM + 1)) -- GitLab From cf3fd7538c51e4526f7a7196795d415c001b7cad Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Mon, 10 Sep 2018 13:54:35 -0500 Subject: [PATCH 10/21] Don't pass pointers that aren't needed. Corrected parameter strings in db. --- LinkamApp/Db/Linkam_T96.db | 6 +++--- LinkamApp/src/Linkam_T96.cpp | 40 ++++++++++++++++++------------------ LinkamApp/src/Linkam_T96.h | 8 ++++---- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index dc069b3..fddced9 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -92,7 +92,7 @@ record(longin, "$(P)$(T):controllerConfig_RBV") record(longin, "$(P)$(T):controllerError_RBV") { field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_CONFIG_IN_VALUE") + field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_ERROR_IN_VALUE") field(PINI, "YES") field(EGU, "N/A") field(SCAN, "Passive") @@ -101,7 +101,7 @@ record(longin, "$(P)$(T):controllerError_RBV") record(longin, "$(P)$(T):controllerStatus_RBV") { field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_CONFIG_IN_VALUE") + field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_STATUS_IN_VALUE") field(PINI, "YES") field(EGU, "N/A") field(SCAN, "Passive") @@ -110,7 +110,7 @@ record(longin, "$(P)$(T):controllerStatus_RBV") record(longin, "$(P)$(T):stageConfig_RBV") { field(DTYP, "asynInt32") - field(INP, "@asyn($(PORT),$(ADDR))CONTROLLER_CONFIG_IN_VALUE") + field(INP, "@asyn($(PORT),$(ADDR))STAGE_CONFIG_IN_VALUE") field(PINI, "YES") field(EGU, "N/A") field(SCAN, "Passive") diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index f326f42..5d8e65c 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -391,19 +391,19 @@ asynStatus Linkam::readInt32(asynUser *pasynUser, epicsInt32 *value) if (function == controllerConfigInValue_) { - status = readControllerConfig(&controllerConfig_); + status = readControllerConfig(); } else if (function == controllerErrorInValue_) { - status = readControllerError(&controllerError_); + status = readControllerError(); } else if (function == controllerStatusInValue_) { - status = readControllerStatus(&controllerStatus_); + status = readControllerStatus(); } else if (function == stageConfigInValue_) { - status = readStageConfig(&stageConfig_); + status = readStageConfig(); } @@ -427,62 +427,62 @@ asynStatus Linkam::readInt32(asynUser *pasynUser, epicsInt32 *value) return (status==0) ? asynSuccess : asynError; } -asynStatus Linkam::readControllerConfig(epicsUInt64 *value) +asynStatus Linkam::readControllerConfig() { static const char *functionName = "readControllerConfig"; this->lock(); - *value = GetControllerConfig(); + this->controllerConfig_ = GetControllerConfig(); this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %llx\n", - driverName, functionName, this->portName, *value); + "%s:%s, port %s, controllerConfig_ = %llx\n", + driverName, functionName, this->portName, this->controllerConfig_); return asynSuccess; } -asynStatus Linkam::readControllerError(epicsUInt32 *value) +asynStatus Linkam::readControllerError() { static const char *functionName = "readControllerError"; this->lock(); - *value = GetControllerError(); + this->controllerError_ = GetControllerError(); this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %lx\n", - driverName, functionName, this->portName, *value); + "%s:%s, port %s, controllerError_ = %lx\n", + driverName, functionName, this->portName, this->controllerError_); return asynSuccess; } -asynStatus Linkam::readControllerStatus(epicsUInt64 *value) +asynStatus Linkam::readControllerStatus() { static const char *functionName = "readControllerStatus"; this->lock(); - *value = GetStatus(); + this->controllerStatus_ = GetStatus(); this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %llx\n", - driverName, functionName, this->portName, *value); + "%s:%s, port %s, controllerStatus_ = %llx\n", + driverName, functionName, this->portName, this->controllerStatus_); return asynSuccess; } -asynStatus Linkam::readStageConfig(epicsUInt64 *value) +asynStatus Linkam::readStageConfig() { static const char *functionName = "readStageConfig"; this->lock(); - *value = GetStageConfig(); + this->stageConfig_ = GetStageConfig(); this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %llx\n", - driverName, functionName, this->portName, *value); + "%s:%s, port %s, stageConfig_ = %llx\n", + driverName, functionName, this->portName, this->stageConfig_); return asynSuccess; } diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index eac5751..687f163 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -70,10 +70,10 @@ private: asynStatus setRampRate(epicsFloat64 value); asynStatus readHeaterPower(epicsFloat64 *value); asynStatus setHeating(epicsInt32 value); - asynStatus readControllerConfig(epicsUInt64 *value); - asynStatus readControllerError(epicsUInt32 *value); - asynStatus readControllerStatus(epicsUInt64 *value); - asynStatus readStageConfig(epicsUInt64 *value); + asynStatus readControllerConfig(); + asynStatus readControllerError(); + asynStatus readControllerStatus(); + asynStatus readStageConfig(); void report(FILE *fp, int details); epicsUInt32 commType_; -- GitLab From ad3b00ee33c0d6bd5f86f62cd413430dcb51ce7d Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Mon, 10 Sep 2018 15:49:28 -0500 Subject: [PATCH 11/21] Added LN pump mode and speed control. --- LinkamApp/Db/Linkam_T96.db | 24 +++++++++++ LinkamApp/src/Linkam_T96.cpp | 82 +++++++++++++++++++++++++++++++++--- LinkamApp/src/Linkam_T96.h | 34 +++++++++++++++ 3 files changed, 134 insertions(+), 6 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index fddced9..1a7e6f6 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -80,6 +80,30 @@ record(bo, "$(P)$(T):heating") field(VAL, "0") } +record(bo, "$(P)$(T):lnpMode") +{ + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),$(ADDR))LNP_MODE_OUT_VALUE") + field(PINI, "YES") + field(ZNAM, "Manual") + field(ONAM, "Auto") + field(VAL, "1") +} + +record(longout, "$(P)$(T):lnpSpeed") +{ + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),$(ADDR))LNP_SPEED_OUT_VALUE") + field(PINI, "YES") + field(OMSL, "closed_loop") + field(DRVH, "100") + field(DRVL, "0") + field(VAL, "0") + field(HOPR, "100") + field(LOPR, "0") + field(EGU, "%") +} + record(longin, "$(P)$(T):controllerConfig_RBV") { field(DTYP, "asynInt32") diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index 5d8e65c..8cbad46 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -33,6 +33,7 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt createParam(rampRateInValueString, asynParamFloat64, &rampRateInValue_); createParam(heaterPowerInValueString, asynParamFloat64, &heaterPowerInValue_); createParam(heatingOutValueString, asynParamInt32, &heatingOutValue_); + createParam(lnpModeOutValueString, asynParamInt32, &lnpModeOutValue_); createParam(controllerConfigInValueString, asynParamInt32, &controllerConfigInValue_); createParam(controllerErrorInValueString, asynParamInt32, &controllerErrorInValue_); createParam(controllerStatusInValueString, asynParamInt32, &controllerStatusInValue_); @@ -53,6 +54,12 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt // Force the device to connect now connect(this->pasynUserSelf); + // + readControllerConfig(); + + // + readStageConfig(); + //epicsThreadSleep(5.0); } @@ -344,7 +351,17 @@ asynStatus Linkam::writeInt32(asynUser *pasynUser, epicsInt32 value) // Enable/disable heating status = setHeating(value); - } + } else if (function == lnpModeOutValue_) { + + // Set Manual/Auto LNP mode + status = setLnpMode(value); + + } else if (function == lnpSpeedOutValue_) { + + // Set LN pump speed (0-100%) + status = setLnpSpeed(value); + + } callParamCallbacks(); if (status == 0) { @@ -376,6 +393,40 @@ asynStatus Linkam::setHeating(epicsInt32 value) return (status) ? asynSuccess : asynError; } +asynStatus Linkam::setLnpMode(epicsInt32 value) +{ + bool status; + static const char *functionName = "setLnpMode"; + + this->lock(); + // Manual = 0 = false ; Auto = 1 = true + status = SetLnpMode((value==1) ? true : false); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %d\n", + driverName, functionName, this->portName, value); + + return (status) ? asynSuccess : asynError; +} + +asynStatus Linkam::setLnpSpeed(epicsInt32 value) +{ + bool status; + static const char *functionName = "setLnpSpeed"; + + this->lock(); + // 0-100% + status = SetLnpMode(value); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %d\n", + driverName, functionName, this->portName, value); + + return (status) ? asynSuccess : asynError; +} + asynStatus Linkam::readInt32(asynUser *pasynUser, epicsInt32 *value) { int function = pasynUser->reason; @@ -433,11 +484,17 @@ asynStatus Linkam::readControllerConfig() this->lock(); this->controllerConfig_ = GetControllerConfig(); - this->unlock(); + + if (controllerConfig_ & ( (epicsUInt64)1 << (int)u64LnpReady ) ) + controllerLnpReady_ = 1; + else + controllerLnpReady_ = 0; + + this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, controllerConfig_ = %llx\n", - driverName, functionName, this->portName, this->controllerConfig_); + "%s:%s, port %s, controllerConfig_ = %llx; controllerLnpReady_ = %d\n", + driverName, functionName, this->portName, this->controllerConfig_, this->controllerLnpReady_); return asynSuccess; } @@ -480,9 +537,19 @@ asynStatus Linkam::readStageConfig() this->stageConfig_ = GetStageConfig(); this->unlock(); + if (stageConfig_ & ( (epicsUInt64)1 << (int)u64SupportsLNPMan ) ) + stageLnpManual_ = 1; + else + stageLnpManual_ = 0; + + if (stageConfig_ & ( (epicsUInt64)1 << (int)u64SupportsLNPAuto ) ) + stageLnpAuto_ = 1; + else + stageLnpAuto_ = 0; + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, stageConfig_ = %llx\n", - driverName, functionName, this->portName, this->stageConfig_); + "%s:%s, port %s, stageConfig_ = %llx, stageLnpManual_ = %d, stageLnpAuto_ = %d\n", + driverName, functionName, this->portName, this->stageConfig_, stageLnpManual_, stageLnpAuto_); return asynSuccess; } @@ -495,9 +562,12 @@ void Linkam::report(FILE *fp, int details) if (details >= 1) { fprintf(fp, " controller config = %llx\n", controllerConfig_); + fprintf(fp, "\tLNP Ready = %d\n", controllerLnpReady_); fprintf(fp, " controller error = %lx\n", controllerError_); fprintf(fp, " controller status = %llx\n", controllerStatus_); fprintf(fp, " stage config = %llx\n", stageConfig_); + fprintf(fp, "\tLNP Manual = %d\n", stageLnpManual_); + fprintf(fp, "\tLNP Auto = %d\n", stageLnpAuto_); } fprintf(fp, "\n"); } diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index 687f163..02195af 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -16,6 +16,31 @@ static const char *driverName = "Linkam"; #define u32Heater1LimitRW 2 #define u32Heater1PowerR 3 +// eSTAGECONFIG Enumeration +#define u64StandardStage 0 +#define u64HighTempStage 1 +#define u64PeltierStage 2 +#define u64SupportsLNPMan 21 +#define u64SupportsLNPAuto 22 +#define u64SupportsHeater1Present 26 +#define u64Heater1SupportsTemperatureControl 27 +#define u64SupportsVacuum 48 +#define u64SupportsHumidity 52 + +// eCONTROLLERCONFIG Enumeration +#define u64SupportsHeater 0 +#define u64LnpReady 36 + +// eSTATUS Enumeration +#define u64ControllerError 0 +#define u64Heater1RampSetpoint 1 +#define u64Heater1Started 2 +#define u64VacuumSetPoint 5 +#define u64VacuumControlStarted 6 +#define u64HumidityRampSetpoint 9 +#define u64HumidityControlStarted 10 +#define u64LnpCoolingStarted 11 +#define u64LnpCoolingAuto 12 /* These are the drvInfo strings that are used to identify the parameters. * They are used by asyn clients, including standard asyn device support */ @@ -26,6 +51,8 @@ static const char *driverName = "Linkam"; #define rampRateInValueString "RAMP_RATE_IN_VALUE" /* asynFloat64 r/o */ #define heaterPowerInValueString "HEATER_POWER_IN_VALUE" /* asynFloat64 r/o */ #define heatingOutValueString "HEATING_OUT_VALUE" /* asynInt32 r/w */ +#define lnpModeOutValueString "LNP_MODE_OUT_VALUE" /* asynInt32 r/w */ +#define lnpSpeedOutValueString "LNP_SPEED_OUT_VALUE" /* asynInt32 r/w */ #define controllerConfigInValueString "CONTROLLER_CONFIG_IN_VALUE" /* asynInt32 r/o */ #define controllerErrorInValueString "CONTROLLER_ERROR_IN_VALUE" /* asynInt32 r/o */ #define controllerStatusInValueString "CONTROLLER_STATUS_IN_VALUE" /* asynInt32 r/o */ @@ -55,6 +82,8 @@ protected: int rampRateInValue_; int heaterPowerInValue_; int heatingOutValue_; + int lnpModeOutValue_; + int lnpSpeedOutValue_; int controllerConfigInValue_; int controllerErrorInValue_; int controllerStatusInValue_; @@ -70,6 +99,8 @@ private: asynStatus setRampRate(epicsFloat64 value); asynStatus readHeaterPower(epicsFloat64 *value); asynStatus setHeating(epicsInt32 value); + asynStatus setLnpMode(epicsInt32 value); + asynStatus setLnpSpeed(epicsInt32 value); asynStatus readControllerConfig(); asynStatus readControllerError(); asynStatus readControllerStatus(); @@ -86,6 +117,9 @@ private: epicsUInt32 controllerError_; epicsUInt64 controllerStatus_; epicsUInt64 stageConfig_; + short controllerLnpReady_; + short stageLnpAuto_; + short stageLnpManual_; }; #define NUM_PARAMS ((int)(&LAST_LINKAM_PARAM - &FIRST_LINKAM_PARAM + 1)) -- GitLab From 18e8f4e75d82e028c364ef3fd511dafd3b4d0aee Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Mon, 10 Sep 2018 16:13:38 -0500 Subject: [PATCH 12/21] Added LNP controls --- LinkamApp/op/adl/Linkam_T96.adl | 497 ++++++----- LinkamApp/op/adl/Linkam_T96.ui | 1470 +++++++++++++++++-------------- 2 files changed, 1077 insertions(+), 890 deletions(-) diff --git a/LinkamApp/op/adl/Linkam_T96.adl b/LinkamApp/op/adl/Linkam_T96.adl index c4bdea6..6ee9c94 100644 --- a/LinkamApp/op/adl/Linkam_T96.adl +++ b/LinkamApp/op/adl/Linkam_T96.adl @@ -1,14 +1,14 @@ file { - name="/home/KPETERSN/Current/9idc_Linkam/support/Linkam/LinkamApp/op/adl/Linkam_T96.adl" - version=030104 + name="/home/oxygen40/KPETERSN/Current/9idc_Linkam/Linkam_T96.adl" + version=030111 } display { object { - x=2272 - y=64 + x=378 + y=148 width=560 - height=250 + height=310 } clr=14 bclr=4 @@ -100,254 +100,243 @@ text { textix="Linkam T96" align="horiz. centered" } -composite { +text { object { x=15 y=55 - width=530 - height=145 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Temperature" +} +text { + object { + x=15 + y=85 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Ramp Limit" +} +text { + object { + x=15 + y=115 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Ramp Rate (C/min)" +} +text { + object { + x=15 + y=175 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Heating" +} +"text update" { + object { + x=220 + y=55 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):temperature_RBV" + clr=14 + bclr=4 + } + limits { + } +} +"text update" { + object { + x=385 + y=85 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):rampLimit_RBV" + clr=14 + bclr=4 + } + limits { + } +} +"text update" { + object { + x=385 + y=115 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):rampRate_RBV" + clr=14 + bclr=4 + } + limits { + } +} +"text entry" { + object { + x=220 + y=85 + width=160 + height=25 + } + control { + chan="$(P)$(T):rampLimit" + clr=14 + bclr=4 + } + limits { + } +} +"text entry" { + object { + x=220 + y=115 + width=160 + height=25 + } + control { + chan="$(P)$(T):rampRate" + clr=14 + bclr=4 + } + limits { + } +} +menu { + object { + x=385 + y=55 + width=160 + height=25 + } + control { + chan="$(P)$(T):temperature_RBV.SCAN" + clr=14 + bclr=4 + } +} +text { + object { + x=15 + y=145 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Heater Power" +} +"text update" { + object { + x=220 + y=145 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):heaterPower_RBV" + clr=14 + bclr=4 + } + limits { + } +} +menu { + object { + x=385 + y=145 + width=160 + height=25 + } + control { + chan="$(P)$(T):heaterPower_RBV.SCAN" + clr=14 + bclr=4 + } +} +"text update" { + object { + x=385 + y=175 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):heating" + clr=14 + bclr=4 + } + format="string" + limits { + } +} +composite { + object { + x=220 + y=175 + width=160 + height=25 } "composite name"="" children { - text { - object { - x=15 - y=55 - width=200 - height=25 - } - "basic attribute" { - clr=14 - } - textix="Temperature" - } - text { - object { - x=15 - y=85 - width=200 - height=25 - } - "basic attribute" { - clr=14 - } - textix="Ramp Limit" - } - text { - object { - x=15 - y=115 - width=200 - height=25 - } - "basic attribute" { - clr=14 - } - textix="Ramp Rate (C/min)" - } - text { - object { - x=15 - y=175 - width=200 - height=25 - } - "basic attribute" { - clr=14 - } - textix="Heating" - } - "text update" { + "message button" { object { x=220 - y=55 - width=160 - height=25 - } - monitor { - chan="$(P)$(T):temperature_RBV" - clr=14 - bclr=4 - } - limits { - } - } - "text update" { - object { - x=385 - y=85 - width=160 - height=25 - } - monitor { - chan="$(P)$(T):rampLimit_RBV" - clr=14 - bclr=4 - } - limits { - } - } - "text update" { - object { - x=385 - y=115 - width=160 - height=25 - } - monitor { - chan="$(P)$(T):rampRate_RBV" - clr=14 - bclr=4 - } - limits { - } - } - "text entry" { - object { - x=220 - y=85 - width=160 - height=25 - } - control { - chan="$(P)$(T):rampLimit" - clr=14 - bclr=4 - } - limits { - } - } - "text entry" { - object { - x=220 - y=115 - width=160 - height=25 - } - control { - chan="$(P)$(T):rampRate" - clr=14 - bclr=4 - } - limits { - } - } - menu { - object { - x=385 - y=55 - width=160 - height=25 - } - control { - chan="$(P)$(T):temperature_RBV.SCAN" - clr=14 - bclr=4 - } - } - text { - object { - x=15 - y=145 - width=200 - height=25 - } - "basic attribute" { - clr=14 - } - textix="Heater Power" - } - "text update" { - object { - x=220 - y=145 - width=160 - height=25 - } - monitor { - chan="$(P)$(T):heaterPower_RBV" - clr=14 - bclr=4 - } - limits { - } - } - menu { - object { - x=385 - y=145 - width=160 - height=25 - } - control { - chan="$(P)$(T):heaterPower_RBV.SCAN" - clr=14 - bclr=4 - } - } - "text update" { - object { - x=385 y=175 - width=160 + width=75 height=25 } - monitor { + control { chan="$(P)$(T):heating" - clr=14 - bclr=4 - } - format="string" - limits { + clr=0 + bclr=63 } + label="On" + release_msg="1" } - composite { + "message button" { object { - x=220 + x=305 y=175 - width=160 + width=75 height=25 } - "composite name"="" - children { - "message button" { - object { - x=220 - y=175 - width=75 - height=25 - } - control { - chan="$(P)$(T):heating" - clr=0 - bclr=63 - } - label="On" - release_msg="1" - } - "message button" { - object { - x=305 - y=175 - width=75 - height=25 - } - control { - chan="$(P)$(T):heating" - clr=0 - bclr=22 - } - label="Off" - release_msg="0" - } + control { + chan="$(P)$(T):heating" + clr=0 + bclr=22 } + label="Off" + release_msg="0" } } } "related display" { object { x=445 - y=215 + y=275 width=100 height=25 } @@ -360,3 +349,55 @@ composite { bclr=4 label="-More" } +text { + object { + x=15 + y=205 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="LN Pump Mode" +} +text { + object { + x=15 + y=235 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="LN Pump Speed (%)" +} +menu { + object { + x=385 + y=205 + width=160 + height=25 + } + control { + chan="$(P)$(T):lnpMode.VAL" + clr=14 + bclr=4 + } +} +"text entry" { + object { + x=220 + y=235 + width=160 + height=25 + } + control { + chan="$(P)$(T):lnpSpeed.VAL" + clr=14 + bclr=4 + } + limits { + } +} diff --git a/LinkamApp/op/adl/Linkam_T96.ui b/LinkamApp/op/adl/Linkam_T96.ui index f209f1c..fe5e33d 100644 --- a/LinkamApp/op/adl/Linkam_T96.ui +++ b/LinkamApp/op/adl/Linkam_T96.ui @@ -4,10 +4,10 @@ - 2272 - 64 + 378 + 148 560 - 250 + 310 @@ -153,721 +153,711 @@ border-radius: 2px; - + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Temperature + + + ESimpleLabel::WidthAndHeight + 15 55 - 532 - 147 + 200 + 25 - - - QFrame::NoFrame - - - - 0 - 0 - 0 - - - - - 0 - 0 - 0 - - - - Temperature - - - ESimpleLabel::WidthAndHeight - - - - 0 - 0 - 200 - 25 - - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - - - QFrame::NoFrame - - - - 0 - 0 - 0 - - - - - 0 - 0 - 0 - - - - Ramp Limit - - - ESimpleLabel::WidthAndHeight - - - - 0 - 30 - 200 - 25 - - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - - - QFrame::NoFrame - - - - 0 - 0 - 0 - - - - - 0 - 0 - 0 - - - - Ramp Rate (C/min) - - - ESimpleLabel::WidthAndHeight - - - - 0 - 60 - 200 - 25 - - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - - - QFrame::NoFrame - - - - 0 - 0 - 0 - - - - - 0 - 0 - 0 - - - - Heating - - - ESimpleLabel::WidthAndHeight - - - - 0 - 120 - 200 - 25 - - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - - - - 205 - 0 - 160 - 25 - - - - caLineEdit::WidthAndHeight - - - $(P)$(T):temperature_RBV - - - - 0 - 0 - 0 - - - - - 187 - 187 - 187 - - - - caLineEdit::Channel - - - caLineEdit::Channel - - - caLineEdit::Channel - - - 0.0 - - - 1.0 - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - decimal - - - caLineEdit::Static - - - - - - 370 - 30 - 160 - 25 - - - - caLineEdit::WidthAndHeight - - - $(P)$(T):rampLimit_RBV - - - - 0 - 0 - 0 - - - - - 187 - 187 - 187 - - - - caLineEdit::Channel - - - caLineEdit::Channel - - - caLineEdit::Channel - - - 0.0 - - - 1.0 - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - decimal - - - caLineEdit::Static - - - - - - 370 - 60 - 160 - 25 - - - - caLineEdit::WidthAndHeight - - - $(P)$(T):rampRate_RBV - - - - 0 - 0 - 0 - - - - - 187 - 187 - 187 - - - - caLineEdit::Channel - - - caLineEdit::Channel - - - caLineEdit::Channel - - - 0.0 - - - 1.0 - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - decimal - - - caLineEdit::Static - - - - - - 205 - 30 - 160 - 25 - - - - caLineEdit::WidthAndHeight - - - $(P)$(T):rampLimit - - - - 0 - 0 - 0 - - - - - 187 - 187 - 187 - - - - caLineEdit::Channel - - - caLineEdit::Channel - - - caLineEdit::Channel - - - 0.0 - - - 1.0 - - - caLineEdit::Static - - - decimal - - - - - - 205 - 60 - 160 - 25 - - - - caLineEdit::WidthAndHeight - - - $(P)$(T):rampRate - - - - 0 - 0 - 0 - - - - - 187 - 187 - 187 - - - - caLineEdit::Channel - - - caLineEdit::Channel - - - caLineEdit::Channel - - - 0.0 - - - 1.0 - - - caLineEdit::Static - - - decimal - - - - - - 370 - 0 - 160 - 25 - - - - $(P)$(T):temperature_RBV.SCAN - - - - 0 - 0 - 0 - - - - - 187 - 187 - 187 - - - - caMenu::Static - - - - - QFrame::NoFrame - - - - 0 - 0 - 0 - - - - - 0 - 0 - 0 - - - - Heater Power - - - ESimpleLabel::WidthAndHeight - + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Ramp Limit + + + ESimpleLabel::WidthAndHeight + + + + 15 + 85 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Ramp Rate (C/min) + + + ESimpleLabel::WidthAndHeight + + + + 15 + 115 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Heating + + + ESimpleLabel::WidthAndHeight + + + + 15 + 175 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 220 + 55 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):temperature_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 385 + 85 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampLimit_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 385 + 115 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampRate_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 220 + 85 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampLimit + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + caLineEdit::Static + + + decimal + + + + + + 220 + 115 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):rampRate + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + caLineEdit::Static + + + decimal + + + + + + 385 + 55 + 160 + 25 + + + + $(P)$(T):temperature_RBV.SCAN + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Heater Power + + + ESimpleLabel::WidthAndHeight + + + + 15 + 145 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 220 + 145 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):heaterPower_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 385 + 145 + 160 + 25 + + + + $(P)$(T):heaterPower_RBV.SCAN + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + + + + + 385 + 175 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):heating + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + string + + + caLineEdit::Static + + + + + + 220 + 175 + 162 + 27 + + + 0 - 90 - 200 - 25 - - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - - - - 205 - 90 - 160 + 0 + 75 25 - caLineEdit::WidthAndHeight + EPushButton::WidthAndHeight - $(P)$(T):heaterPower_RBV + $(P)$(T):heating - 0 - 0 - 0 + 255 + 255 + 255 - 187 - 187 - 187 + 40 + 147 + 21 - - caLineEdit::Channel - - - caLineEdit::Channel - - - caLineEdit::Channel - - - 0.0 - - - 1.0 - - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - decimal - - - caLineEdit::Static - - - - - - 370 - 90 - 160 - 25 - - - - $(P)$(T):heaterPower_RBV.SCAN - - - - 0 - 0 - 0 - + + On - - - 187 - 187 - 187 - + + 1 - caMenu::Static + caMessageButton::Static - + - 370 - 120 - 160 + 85 + 0 + 75 25 - caLineEdit::WidthAndHeight + EPushButton::WidthAndHeight $(P)$(T):heating - 0 - 0 - 0 + 255 + 255 + 255 - 187 - 187 - 187 + 190 + 25 + 11 - - caLineEdit::Channel - - - caLineEdit::Channel - - - caLineEdit::Channel - - - 0.0 - - - 1.0 + + Off - - Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - - - string + + 0 - caLineEdit::Static - - - - - - 205 - 120 - 162 - 27 - + caMessageButton::Static - - - - 0 - 0 - 75 - 25 - - - - EPushButton::WidthAndHeight - - - $(P)$(T):heating - - - - 255 - 255 - 255 - - - - - 40 - 147 - 21 - - - - On - - - 1 - - - caMessageButton::Static - - - - - - 85 - 0 - 75 - 25 - - - - EPushButton::WidthAndHeight - - - $(P)$(T):heating - - - - 255 - 255 - 255 - - - - - 190 - 25 - 11 - - - - Off - - - 0 - - - caMessageButton::Static - - 445 - 215 + 275 100 25 @@ -905,14 +895,168 @@ border-radius: 2px; false + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + LN Pump Mode + + + ESimpleLabel::WidthAndHeight + + + + 15 + 205 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + LN Pump Speed (%) + + + ESimpleLabel::WidthAndHeight + + + + 15 + 235 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 385 + 205 + 160 + 25 + + + + $(P)$(T):lnpMode.VAL + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + + + + + 220 + 235 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):lnpSpeed.VAL + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + caLineEdit::Static + + + decimal + + caLabel_0 caLabel_1 caLabel_2 caLabel_3 caLabel_4 caLabel_5 - caFrame_1 caFrame_0 + caLabel_6 + caLabel_7 caLineEdit_0 caLineEdit_1 caLineEdit_2 @@ -925,6 +1069,8 @@ border-radius: 2px; caMessageButton_0 caMessageButton_1 caRelatedDisplay_0 + caMenu_2 + caTextEntry_2 \ No newline at end of file -- GitLab From 605fb10b56ea415e3415d23b0d60e83df9f7a250 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Mon, 10 Sep 2018 17:01:20 -0500 Subject: [PATCH 13/21] Added checks for controller and stage config before sending LNP commands. --- LinkamApp/Db/Linkam_T96.db | 14 +++---- LinkamApp/src/Linkam_T96.cpp | 77 ++++++++++++++++++++++++++++++++---- LinkamApp/src/Linkam_T96.h | 4 +- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index 1a7e6f6..13a1607 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -77,7 +77,7 @@ record(bo, "$(P)$(T):heating") field(PINI, "YES") field(ZNAM, "Off") field(ONAM, "On") - field(VAL, "0") + field(VAL, "Off") } record(bo, "$(P)$(T):lnpMode") @@ -87,7 +87,7 @@ record(bo, "$(P)$(T):lnpMode") field(PINI, "YES") field(ZNAM, "Manual") field(ONAM, "Auto") - field(VAL, "1") + field(VAL, "Auto") } record(longout, "$(P)$(T):lnpSpeed") @@ -96,11 +96,11 @@ record(longout, "$(P)$(T):lnpSpeed") field(OUT, "@asyn($(PORT),$(ADDR))LNP_SPEED_OUT_VALUE") field(PINI, "YES") field(OMSL, "closed_loop") - field(DRVH, "100") - field(DRVL, "0") - field(VAL, "0") - field(HOPR, "100") - field(LOPR, "0") + field(DRVH, 100) + field(DRVL, 0) + field(VAL, 0) + field(HOPR, 100) + field(LOPR, 0) field(EGU, "%") } diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index 8cbad46..dc16141 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -34,6 +34,7 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt createParam(heaterPowerInValueString, asynParamFloat64, &heaterPowerInValue_); createParam(heatingOutValueString, asynParamInt32, &heatingOutValue_); createParam(lnpModeOutValueString, asynParamInt32, &lnpModeOutValue_); + createParam(lnpSpeedOutValueString, asynParamInt32, &lnpSpeedOutValue_); createParam(controllerConfigInValueString, asynParamInt32, &controllerConfigInValue_); createParam(controllerErrorInValueString, asynParamInt32, &controllerErrorInValue_); createParam(controllerStatusInValueString, asynParamInt32, &controllerStatusInValue_); @@ -354,12 +355,12 @@ asynStatus Linkam::writeInt32(asynUser *pasynUser, epicsInt32 value) } else if (function == lnpModeOutValue_) { // Set Manual/Auto LNP mode - status = setLnpMode(value); + status = setLnpMode(pasynUser, value); } else if (function == lnpSpeedOutValue_) { // Set LN pump speed (0-100%) - status = setLnpSpeed(value); + status = setLnpSpeed(pasynUser, value); } @@ -393,14 +394,52 @@ asynStatus Linkam::setHeating(epicsInt32 value) return (status) ? asynSuccess : asynError; } -asynStatus Linkam::setLnpMode(epicsInt32 value) +asynStatus Linkam::setLnpMode(asynUser *pasynUser, epicsInt32 value) { bool status; static const char *functionName = "setLnpMode"; this->lock(); + + if (controllerLnpReady_) + { + if (value == 1) + { + // User requested Auto mode + if (stageLnpAuto_ == 1) + { + status = SetLnpMode(true); + } else { + status = false; + + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting LNP Mode: stageLnpAuto_ = %d\n", + driverName, functionName, this->portName, stageLnpAuto_); + } + } else { + // User requested Manual mode + if (stageLnpManual_ == 1) + { + status = SetLnpMode(false); + } else { + status = false; + + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting LNP Mode: stageLnpManual_ = %d\n", + driverName, functionName, this->portName, stageLnpManual_); + } + } + } else { + status = false; + + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting LNP Mode: controllerLnpReady_ = %d\n", + driverName, functionName, this->portName, controllerLnpReady_); + } + // Manual = 0 = false ; Auto = 1 = true - status = SetLnpMode((value==1) ? true : false); + //status = SetLnpMode((value==1) ? true : false); + this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, @@ -410,15 +449,38 @@ asynStatus Linkam::setLnpMode(epicsInt32 value) return (status) ? asynSuccess : asynError; } -asynStatus Linkam::setLnpSpeed(epicsInt32 value) +asynStatus Linkam::setLnpSpeed(asynUser *pasynUser, epicsInt32 value) { bool status; static const char *functionName = "setLnpSpeed"; this->lock(); + + if (controllerLnpReady_) + { + // 0-100% + if (stageLnpManual_ == 1) + { + status = SetLnpSpeed(value); + } else { + status = false; + + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting LNP Speed: stageLnpManual_ = %d\n", + driverName, functionName, this->portName, stageLnpManual_); + } + } else { + status = false; + + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting LNP Speed: controllerLnpReady_ = %d\n", + driverName, functionName, this->portName, controllerLnpReady_); + } + // 0-100% - status = SetLnpMode(value); - this->unlock(); + //status = SetLnpMode(value); + + this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s, port %s, value = %d\n", @@ -427,6 +489,7 @@ asynStatus Linkam::setLnpSpeed(epicsInt32 value) return (status) ? asynSuccess : asynError; } + asynStatus Linkam::readInt32(asynUser *pasynUser, epicsInt32 *value) { int function = pasynUser->reason; diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index 02195af..9884e90 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -99,8 +99,8 @@ private: asynStatus setRampRate(epicsFloat64 value); asynStatus readHeaterPower(epicsFloat64 *value); asynStatus setHeating(epicsInt32 value); - asynStatus setLnpMode(epicsInt32 value); - asynStatus setLnpSpeed(epicsInt32 value); + asynStatus setLnpMode(asynUser *pasynUser, epicsInt32 value); + asynStatus setLnpSpeed(asynUser *pasynUser, epicsInt32 value); asynStatus readControllerConfig(); asynStatus readControllerError(); asynStatus readControllerStatus(); -- GitLab From f11201689335aad291bf5002d08b8d5d4e5b4a90 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 11 Sep 2018 10:33:41 -0500 Subject: [PATCH 14/21] Added lnpSpeed_RBV --- LinkamApp/Db/Linkam_T96.db | 15 +++++ LinkamApp/Db/Linkam_T96_settings.req | 3 +- LinkamApp/op/adl/Linkam_T96.adl | 41 ++++++++++-- LinkamApp/op/adl/Linkam_T96.ui | 98 ++++++++++++++++++++++++++-- LinkamApp/src/Linkam_T96.cpp | 48 ++++++++++---- LinkamApp/src/Linkam_T96.h | 16 +++-- 6 files changed, 188 insertions(+), 33 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index 13a1607..beaecfb 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -66,6 +66,8 @@ record(ai, "$(P)$(T):heaterPower_RBV") field(PINI, "YES") field(LINR, "NO CONVERSION") field(PREC, "4") + field(HOPR, 100.0) + field(LOPR, 0.0) field(EGU, "Watts") field(SCAN, "Passive") } @@ -104,6 +106,19 @@ record(longout, "$(P)$(T):lnpSpeed") field(EGU, "%") } +record(ai, "$(P)$(T):lnpSpeed_RBV") +{ + field(DTYP, "asynFloat64") + field(INP, "@asyn($(PORT),$(ADDR))LNP_SPEED_IN_VALUE") + field(PINI, "YES") + field(LINR, "NO CONVERSION") + field(PREC, "4") + field(HOPR, 100) + field(LOPR, 0) + field(EGU, "%") + field(SCAN, "Passive") +} + record(longin, "$(P)$(T):controllerConfig_RBV") { field(DTYP, "asynInt32") diff --git a/LinkamApp/Db/Linkam_T96_settings.req b/LinkamApp/Db/Linkam_T96_settings.req index c5e7691..83d8e55 100644 --- a/LinkamApp/Db/Linkam_T96_settings.req +++ b/LinkamApp/Db/Linkam_T96_settings.req @@ -2,4 +2,5 @@ $(P)$(T):rampLimit $(P)$(T):rampRate $(P)$(T):heating $(P)$(T):temperature_RBV.SCAN -$(P)$(T):heaterPower_RBV.SCAN \ No newline at end of file +$(P)$(T):heaterPower_RBV.SCAN +$(P)$(T):lnpSpeed_RBV.SCAN diff --git a/LinkamApp/op/adl/Linkam_T96.adl b/LinkamApp/op/adl/Linkam_T96.adl index 6ee9c94..dd651b2 100644 --- a/LinkamApp/op/adl/Linkam_T96.adl +++ b/LinkamApp/op/adl/Linkam_T96.adl @@ -5,10 +5,10 @@ file { } display { object { - x=378 - y=148 + x=2310 + y=92 width=560 - height=310 + height=340 } clr=14 bclr=4 @@ -336,7 +336,7 @@ composite { "related display" { object { x=445 - y=275 + y=305 width=100 height=25 } @@ -364,7 +364,7 @@ text { text { object { x=15 - y=235 + y=250 width=200 height=25 } @@ -389,7 +389,7 @@ menu { "text entry" { object { x=220 - y=235 + y=265 width=160 height=25 } @@ -401,3 +401,32 @@ menu { limits { } } +"text update" { + object { + x=220 + y=235 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):lnpSpeed_RBV" + clr=14 + bclr=4 + } + format="string" + limits { + } +} +menu { + object { + x=385 + y=235 + width=160 + height=25 + } + control { + chan="$(P)$(T):lnpSpeed_RBV.SCAN" + clr=14 + bclr=4 + } +} diff --git a/LinkamApp/op/adl/Linkam_T96.ui b/LinkamApp/op/adl/Linkam_T96.ui index fe5e33d..855793a 100644 --- a/LinkamApp/op/adl/Linkam_T96.ui +++ b/LinkamApp/op/adl/Linkam_T96.ui @@ -4,10 +4,10 @@ - 378 - 148 + 2310 + 92 560 - 310 + 340 @@ -857,7 +857,7 @@ border-radius: 2px; 445 - 275 + 305 100 25 @@ -958,7 +958,7 @@ border-radius: 2px; 15 - 235 + 250 200 25 @@ -1001,7 +1001,7 @@ border-radius: 2px; 220 - 235 + 265 160 25 @@ -1048,6 +1048,90 @@ border-radius: 2px; decimal + + + + 220 + 235 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):lnpSpeed_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + string + + + caLineEdit::Static + + + + + + 385 + 235 + 160 + 25 + + + + $(P)$(T):lnpSpeed_RBV.SCAN + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + caLabel_0 caLabel_1 caLabel_2 @@ -1071,6 +1155,8 @@ border-radius: 2px; caRelatedDisplay_0 caMenu_2 caTextEntry_2 + caLineEdit_5 + caMenu_3 \ No newline at end of file diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index dc16141..d8608bb 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -157,24 +157,29 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) if (function == temperatureInValue_) { - // Read the temperature from the controller + // Read the temperature from the controller (Celsius) status = readTemperature(value); } else if (function == rampLimitInValue_) { - // Read the ramp limit from the controller + // Read the ramp limit from the controller (Celsius) status = readRampLimit(value); } else if (function == rampRateInValue_) { - // Read the ramp rate from the controller + // Read the ramp rate from the controller (C/min) status = readRampRate(value); } else if (function == heaterPowerInValue_) { - // Read the temperature from the controller + // Read the temperature from the controller (Watts) status = readHeaterPower(value); + } else if (function == lnpSpeedInValue_) { + + // Read the LN Pump speed from the controller (0-100%) + status = readLnpSpeed(value); + } else { status = asynPortDriver::readFloat64(pasynUser,value); } @@ -245,7 +250,22 @@ asynStatus Linkam::readHeaterPower(epicsFloat64 *value) asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s, port %s, value = %lf\n", - driverName, functionName, this->portName, temperatureRbv_); + driverName, functionName, this->portName, *value); + + return asynSuccess; +} + +asynStatus Linkam::readLnpSpeed(epicsFloat64 *value) +{ + static const char *functionName = "readLnpSpeed"; + + this->lock(); + *value = GetValue(u32Heater1LnpSpeedR); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %lf\n", + driverName, functionName, this->portName, *value); return asynSuccess; } @@ -355,12 +375,12 @@ asynStatus Linkam::writeInt32(asynUser *pasynUser, epicsInt32 value) } else if (function == lnpModeOutValue_) { // Set Manual/Auto LNP mode - status = setLnpMode(pasynUser, value); + status = setLnpMode(value); } else if (function == lnpSpeedOutValue_) { // Set LN pump speed (0-100%) - status = setLnpSpeed(pasynUser, value); + status = setLnpSpeed(value); } @@ -394,7 +414,7 @@ asynStatus Linkam::setHeating(epicsInt32 value) return (status) ? asynSuccess : asynError; } -asynStatus Linkam::setLnpMode(asynUser *pasynUser, epicsInt32 value) +asynStatus Linkam::setLnpMode(epicsInt32 value) { bool status; static const char *functionName = "setLnpMode"; @@ -412,7 +432,7 @@ asynStatus Linkam::setLnpMode(asynUser *pasynUser, epicsInt32 value) } else { status = false; - asynPrint(pasynUser, ASYN_TRACE_ERROR, + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s, port %s, ERROR setting LNP Mode: stageLnpAuto_ = %d\n", driverName, functionName, this->portName, stageLnpAuto_); } @@ -424,7 +444,7 @@ asynStatus Linkam::setLnpMode(asynUser *pasynUser, epicsInt32 value) } else { status = false; - asynPrint(pasynUser, ASYN_TRACE_ERROR, + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s, port %s, ERROR setting LNP Mode: stageLnpManual_ = %d\n", driverName, functionName, this->portName, stageLnpManual_); } @@ -432,7 +452,7 @@ asynStatus Linkam::setLnpMode(asynUser *pasynUser, epicsInt32 value) } else { status = false; - asynPrint(pasynUser, ASYN_TRACE_ERROR, + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s, port %s, ERROR setting LNP Mode: controllerLnpReady_ = %d\n", driverName, functionName, this->portName, controllerLnpReady_); } @@ -449,7 +469,7 @@ asynStatus Linkam::setLnpMode(asynUser *pasynUser, epicsInt32 value) return (status) ? asynSuccess : asynError; } -asynStatus Linkam::setLnpSpeed(asynUser *pasynUser, epicsInt32 value) +asynStatus Linkam::setLnpSpeed(epicsInt32 value) { bool status; static const char *functionName = "setLnpSpeed"; @@ -465,14 +485,14 @@ asynStatus Linkam::setLnpSpeed(asynUser *pasynUser, epicsInt32 value) } else { status = false; - asynPrint(pasynUser, ASYN_TRACE_ERROR, + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s, port %s, ERROR setting LNP Speed: stageLnpManual_ = %d\n", driverName, functionName, this->portName, stageLnpManual_); } } else { status = false; - asynPrint(pasynUser, ASYN_TRACE_ERROR, + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s, port %s, ERROR setting LNP Speed: controllerLnpReady_ = %d\n", driverName, functionName, this->portName, controllerLnpReady_); } diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index 9884e90..cfddc42 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -11,10 +11,11 @@ static const char *driverName = "Linkam"; #define MAX_CONTROLLERS 1 // eVALUETYPE Enumeration -#define u32Heater1TempR 0 -#define u32Heater1RateRW 1 -#define u32Heater1LimitRW 2 -#define u32Heater1PowerR 3 +#define u32Heater1TempR 0 +#define u32Heater1RateRW 1 +#define u32Heater1LimitRW 2 +#define u32Heater1PowerR 3 +#define u32Heater1LnpSpeedR 4 // eSTAGECONFIG Enumeration #define u64StandardStage 0 @@ -53,6 +54,7 @@ static const char *driverName = "Linkam"; #define heatingOutValueString "HEATING_OUT_VALUE" /* asynInt32 r/w */ #define lnpModeOutValueString "LNP_MODE_OUT_VALUE" /* asynInt32 r/w */ #define lnpSpeedOutValueString "LNP_SPEED_OUT_VALUE" /* asynInt32 r/w */ +#define lnpSpeedInValueString "LNP_SPEED_IN_VALUE" /* asynInt32 r/o */ #define controllerConfigInValueString "CONTROLLER_CONFIG_IN_VALUE" /* asynInt32 r/o */ #define controllerErrorInValueString "CONTROLLER_ERROR_IN_VALUE" /* asynInt32 r/o */ #define controllerStatusInValueString "CONTROLLER_STATUS_IN_VALUE" /* asynInt32 r/o */ @@ -84,6 +86,7 @@ protected: int heatingOutValue_; int lnpModeOutValue_; int lnpSpeedOutValue_; + int lnpSpeedInValue_; int controllerConfigInValue_; int controllerErrorInValue_; int controllerStatusInValue_; @@ -99,8 +102,9 @@ private: asynStatus setRampRate(epicsFloat64 value); asynStatus readHeaterPower(epicsFloat64 *value); asynStatus setHeating(epicsInt32 value); - asynStatus setLnpMode(asynUser *pasynUser, epicsInt32 value); - asynStatus setLnpSpeed(asynUser *pasynUser, epicsInt32 value); + asynStatus setLnpMode(epicsInt32 value); + asynStatus setLnpSpeed(epicsInt32 value); + asynStatus readLnpSpeed(epicsFloat64 *value); asynStatus readControllerConfig(); asynStatus readControllerError(); asynStatus readControllerStatus(); -- GitLab From f0d456d0acd060e388175110410b4e652e22833a Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 11 Sep 2018 11:48:51 -0500 Subject: [PATCH 15/21] Minor improvements to T96 support. --- LinkamApp/Db/Linkam_T96.db | 4 ++-- LinkamApp/Db/Linkam_T96_settings.req | 2 ++ LinkamApp/src/Linkam_T96.cpp | 1 + LinkamApp/src/Linkam_T96.h | 2 +- iocs/linkamIOC/iocBoot/ioclinkam/st.cmd | 11 ++++++++--- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index beaecfb..fb97c3d 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -79,7 +79,7 @@ record(bo, "$(P)$(T):heating") field(PINI, "YES") field(ZNAM, "Off") field(ONAM, "On") - field(VAL, "Off") + field(VAL, 0) } record(bo, "$(P)$(T):lnpMode") @@ -89,7 +89,7 @@ record(bo, "$(P)$(T):lnpMode") field(PINI, "YES") field(ZNAM, "Manual") field(ONAM, "Auto") - field(VAL, "Auto") + field(VAL, 1) } record(longout, "$(P)$(T):lnpSpeed") diff --git a/LinkamApp/Db/Linkam_T96_settings.req b/LinkamApp/Db/Linkam_T96_settings.req index 83d8e55..6eab6e4 100644 --- a/LinkamApp/Db/Linkam_T96_settings.req +++ b/LinkamApp/Db/Linkam_T96_settings.req @@ -1,6 +1,8 @@ $(P)$(T):rampLimit $(P)$(T):rampRate $(P)$(T):heating +$(P)$(T):lnpMode +$(P)$(T):lnpSpeed $(P)$(T):temperature_RBV.SCAN $(P)$(T):heaterPower_RBV.SCAN $(P)$(T):lnpSpeed_RBV.SCAN diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index d8608bb..e2eb071 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -35,6 +35,7 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt createParam(heatingOutValueString, asynParamInt32, &heatingOutValue_); createParam(lnpModeOutValueString, asynParamInt32, &lnpModeOutValue_); createParam(lnpSpeedOutValueString, asynParamInt32, &lnpSpeedOutValue_); + createParam(lnpSpeedInValueString, asynParamFloat64, &lnpSpeedInValue_); createParam(controllerConfigInValueString, asynParamInt32, &controllerConfigInValue_); createParam(controllerErrorInValueString, asynParamInt32, &controllerErrorInValue_); createParam(controllerStatusInValueString, asynParamInt32, &controllerStatusInValue_); diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index cfddc42..11c595e 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -54,7 +54,7 @@ static const char *driverName = "Linkam"; #define heatingOutValueString "HEATING_OUT_VALUE" /* asynInt32 r/w */ #define lnpModeOutValueString "LNP_MODE_OUT_VALUE" /* asynInt32 r/w */ #define lnpSpeedOutValueString "LNP_SPEED_OUT_VALUE" /* asynInt32 r/w */ -#define lnpSpeedInValueString "LNP_SPEED_IN_VALUE" /* asynInt32 r/o */ +#define lnpSpeedInValueString "LNP_SPEED_IN_VALUE" /* asynFloat64 r/o */ #define controllerConfigInValueString "CONTROLLER_CONFIG_IN_VALUE" /* asynInt32 r/o */ #define controllerErrorInValueString "CONTROLLER_ERROR_IN_VALUE" /* asynInt32 r/o */ #define controllerStatusInValueString "CONTROLLER_STATUS_IN_VALUE" /* asynInt32 r/o */ diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd b/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd index 1a131bc..9f017df 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd +++ b/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd @@ -21,8 +21,13 @@ linkam_registerRecordDeviceDriver pdbbase cd "${TOP}/iocBoot/${IOC}" iocInit -## Start any sequence programs -#seq sncxxx,"user=kpetersnHost" +# Turn off heating by default +#!dbpf "$(PREFIX)tc1:heating" "0" +# Make Auto mode the default +#!dbpf "$(PREFIX)tc1:lnpMode" "1" # Ugly hack to allow disconnecting for manual control via the screen -dbpf "$(PREFIX)asyn.AUCT" "noAutoConnect" +#!dbpf "$(PREFIX)asyn.AUCT" "noAutoConnect" + +## Boot is complete +date -- GitLab From 4da358893f664f13ba163c475fc9f13792243a36 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 11 Sep 2018 14:38:57 -0500 Subject: [PATCH 16/21] Added vacuum controls --- LinkamApp/Db/Linkam_T96.db | 45 +++++++ LinkamApp/Db/Linkam_T96_settings.req | 3 + LinkamApp/src/Linkam_T96.cpp | 170 +++++++++++++++++++++++++-- LinkamApp/src/Linkam_T96.h | 25 ++++ 4 files changed, 234 insertions(+), 9 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index fb97c3d..4080f73 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -119,6 +119,51 @@ record(ai, "$(P)$(T):lnpSpeed_RBV") field(SCAN, "Passive") } +record(bo, "$(P)$(T):vacuum") +{ + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),$(ADDR))VACUUM_OUT_VALUE") + field(PINI, "YES") + field(ZNAM, "Off") + field(ONAM, "On") + field(VAL, 0) +} + +record(ao, "$(P)$(T):vacuumLimit") +{ + field(DTYP, "asynFloat64") + field(OUT, "@asyn($(PORT),$(ADDR))VACUUM_LIMIT_OUT_VALUE") + field(PINI, "YES") + field(LINR, "NO CONVERSION") + field(PREC, "4") + field(EGU, "mBar") + field(SCAN, "Passive") + field(VAL, "1013.25") + field(FLNK, "$(P)$(T):vacuumLimit_RBV") +} + +record(ai, "$(P)$(T):vacuumLimit_RBV") +{ + field(DTYP, "asynFloat64") + field(INP, "@asyn($(PORT),$(ADDR))VACUUM_LIMIT_IN_VALUE") + field(PINI, "YES") + field(LINR, "NO CONVERSION") + field(PREC, "4") + field(EGU, "mBar") + field(SCAN, "Passive") +} + +record(ai, "$(P)$(T):pressure_RBV") +{ + field(DTYP, "asynFloat64") + field(INP, "@asyn($(PORT),$(ADDR))PRESSURE_IN_VALUE") + field(PINI, "YES") + field(LINR, "NO CONVERSION") + field(PREC, "4") + field(EGU, "mBar") + field(SCAN, "Passive") +} + record(longin, "$(P)$(T):controllerConfig_RBV") { field(DTYP, "asynInt32") diff --git a/LinkamApp/Db/Linkam_T96_settings.req b/LinkamApp/Db/Linkam_T96_settings.req index 6eab6e4..d995ad5 100644 --- a/LinkamApp/Db/Linkam_T96_settings.req +++ b/LinkamApp/Db/Linkam_T96_settings.req @@ -3,6 +3,9 @@ $(P)$(T):rampRate $(P)$(T):heating $(P)$(T):lnpMode $(P)$(T):lnpSpeed +$(P)$(T):vacuum +$(P)$(T):vacuumLimit $(P)$(T):temperature_RBV.SCAN $(P)$(T):heaterPower_RBV.SCAN $(P)$(T):lnpSpeed_RBV.SCAN +$(P)$(T):pressure_RBV.SCAN diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index e2eb071..eba0e4d 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -26,6 +26,7 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt commType_ = commType; commPort_ = commPort; +// createParam(temperatureInValueString, asynParamFloat64, &temperatureInValue_); createParam(rampLimitOutValueString, asynParamFloat64, &rampLimitOutValue_); createParam(rampLimitInValueString, asynParamFloat64, &rampLimitInValue_); @@ -33,13 +34,20 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt createParam(rampRateInValueString, asynParamFloat64, &rampRateInValue_); createParam(heaterPowerInValueString, asynParamFloat64, &heaterPowerInValue_); createParam(heatingOutValueString, asynParamInt32, &heatingOutValue_); +// createParam(lnpModeOutValueString, asynParamInt32, &lnpModeOutValue_); createParam(lnpSpeedOutValueString, asynParamInt32, &lnpSpeedOutValue_); createParam(lnpSpeedInValueString, asynParamFloat64, &lnpSpeedInValue_); - createParam(controllerConfigInValueString, asynParamInt32, &controllerConfigInValue_); +// + createParam(vacuumOutValueString, asynParamInt32, &vacuumOutValue_); + createParam(vacuumLimitOutValueString, asynParamFloat64, &vacuumLimitOutValue_); + createParam(vacuumLimitInValueString, asynParamFloat64, &vacuumLimitInValue_); + createParam(pressureInValueString, asynParamFloat64, &pressureInValue_); +// + createParam(controllerConfigInValueString, asynParamInt32, &controllerConfigInValue_); createParam(controllerErrorInValueString, asynParamInt32, &controllerErrorInValue_); - createParam(controllerStatusInValueString, asynParamInt32, &controllerStatusInValue_); - createParam(stageConfigInValueString, asynParamInt32, &stageConfigInValue_); + createParam(controllerStatusInValueString, asynParamInt32, &controllerStatusInValue_); + createParam(stageConfigInValueString, asynParamInt32, &stageConfigInValue_); // Must be in the directory with the DLL when this line is executed //LoadMonoLibrary("LinkamCommsDll.dll"); @@ -181,6 +189,16 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) // Read the LN Pump speed from the controller (0-100%) status = readLnpSpeed(value); + } else if (function == vacuumLimitInValue_) { + + // Read the vacuum limit from the controller (mBar) + status = readVacuumLimit(value); + + } else if (function == pressureInValue_) { + + // Read the vacuum pressure from the controller (mBar) + status = readPressure(value); + } else { status = asynPortDriver::readFloat64(pasynUser,value); } @@ -271,6 +289,36 @@ asynStatus Linkam::readLnpSpeed(epicsFloat64 *value) return asynSuccess; } +asynStatus Linkam::readVacuumLimit(epicsFloat64 *value) +{ + static const char *functionName = "readVacuumLimit"; + + this->lock(); + *value = GetValue(u32VacuumLimitRW); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %lf\n", + driverName, functionName, this->portName, *value); + + return asynSuccess; +} + +asynStatus Linkam::readPressure(epicsFloat64 *value) +{ + static const char *functionName = "readPressure"; + + this->lock(); + *value = GetValue(u32VacuumR); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %lf\n", + driverName, functionName, this->portName, *value); + + return asynSuccess; +} + asynStatus Linkam::writeFloat64(asynUser *pasynUser, epicsFloat64 value) { @@ -287,14 +335,19 @@ asynStatus Linkam::writeFloat64(asynUser *pasynUser, epicsFloat64 value) if (function == rampLimitOutValue_) { - // Read the temperature from the controller + // set the desired temperature status = setRampLimit(value); } else if (function == rampRateOutValue_) { - // Read the temperature from the controller + // set the desired ramp rate status = setRampRate(value); + } else if (function == vacuumLimitOutValue_) { + + // set the desired pressure + status = setVacuumLimit(value); + } else { status = asynPortDriver::writeFloat64(pasynUser,value); } @@ -326,8 +379,7 @@ asynStatus Linkam::setRampLimit(epicsFloat64 value) * Returns true if set successfully, false otherwise. */ this->lock(); - //status = SetValue(u32Heater1LimitRW, (float) value); - status = SetValue(2, (float) value); + status = SetValue(u32Heater1LimitRW, (float) value); this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, @@ -343,8 +395,48 @@ asynStatus Linkam::setRampRate(epicsFloat64 value) static const char *functionName = "setRampRate"; this->lock(); - //status = SetValue(u32Heater1RateRW, (float) value); - status = SetValue(1, (float) value); + status = SetValue(u32Heater1RateRW, (float) value); + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %lf\n", + driverName, functionName, this->portName, value); + + return (status) ? asynSuccess : asynError; +} + +asynStatus Linkam::setVacuumLimit(epicsFloat64 value) +{ + bool status; + static const char *functionName = "setVacuumLimit"; + + this->lock(); + + if (controllerVacuumReady_) + { + if (stageVacuum_) + { + // Vacuum control is supported and ready + status = SetValue(u32VacuumLimitRW, (float) value); + } + else + { + status = false; + + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting vacuum limit: stageVacuum_ = %d\n", + driverName, functionName, this->portName, stageVacuum_); + } + } + else + { + status = false; + + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting vacuum limit: controllerVacuumReady_ = %d\n", + driverName, functionName, this->portName, controllerVacuumReady_); + } + this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, @@ -383,7 +475,13 @@ asynStatus Linkam::writeInt32(asynUser *pasynUser, epicsInt32 value) // Set LN pump speed (0-100%) status = setLnpSpeed(value); + } else if (function == vacuumOutValue_) { + + // Set vacuum on/off + status = setVacuum(value); + } + callParamCallbacks(); if (status == 0) { @@ -510,6 +608,48 @@ asynStatus Linkam::setLnpSpeed(epicsInt32 value) return (status) ? asynSuccess : asynError; } +asynStatus Linkam::setVacuum(epicsInt32 value) +{ + bool status; + static const char *functionName = "setVacuum"; + + this->lock(); + + // Off = 0 = false ; On = 1 = true + if (controllerVacuumReady_) + { + if (stageVacuum_) + { + // Vacuum control is supported and ready + status = StartVacuum((value) ? true : false); + } + else + { + status = false; + + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting vacuum: stageVacuum_ = %d\n", + driverName, functionName, this->portName, stageVacuum_); + } + } + else + { + status = false; + + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s, port %s, ERROR setting vacuum: controllerVacuumReady_ = %d\n", + driverName, functionName, this->portName, controllerVacuumReady_); + } + + this->unlock(); + + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s:%s, port %s, value = %d\n", + driverName, functionName, this->portName, value); + + return (status) ? asynSuccess : asynError; +} + asynStatus Linkam::readInt32(asynUser *pasynUser, epicsInt32 *value) { @@ -574,6 +714,11 @@ asynStatus Linkam::readControllerConfig() else controllerLnpReady_ = 0; + if (controllerConfig_ & ( (epicsUInt64)1 << (int)u64VacuumReady ) ) + controllerVacuumReady_ = 1; + else + controllerVacuumReady_ = 0; + this->unlock(); asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, @@ -631,6 +776,11 @@ asynStatus Linkam::readStageConfig() else stageLnpAuto_ = 0; + if (stageConfig_ & ( (epicsUInt64)1 << (int)u64SupportsVacuum ) ) + stageVacuum_ = 1; + else + stageVacuum_ = 0; + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s, port %s, stageConfig_ = %llx, stageLnpManual_ = %d, stageLnpAuto_ = %d\n", driverName, functionName, this->portName, this->stageConfig_, stageLnpManual_, stageLnpAuto_); @@ -647,11 +797,13 @@ void Linkam::report(FILE *fp, int details) { fprintf(fp, " controller config = %llx\n", controllerConfig_); fprintf(fp, "\tLNP Ready = %d\n", controllerLnpReady_); + fprintf(fp, "\tVac Ready = %d\n", controllerVacuumReady_); fprintf(fp, " controller error = %lx\n", controllerError_); fprintf(fp, " controller status = %llx\n", controllerStatus_); fprintf(fp, " stage config = %llx\n", stageConfig_); fprintf(fp, "\tLNP Manual = %d\n", stageLnpManual_); fprintf(fp, "\tLNP Auto = %d\n", stageLnpAuto_); + fprintf(fp, "\tVacuum = %d\n", stageVacuum_); } fprintf(fp, "\n"); } diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index 11c595e..bcae444 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -16,6 +16,8 @@ static const char *driverName = "Linkam"; #define u32Heater1LimitRW 2 #define u32Heater1PowerR 3 #define u32Heater1LnpSpeedR 4 +#define u32VacuumR 12 +#define u32VacuumLimitRW 13 // eSTAGECONFIG Enumeration #define u64StandardStage 0 @@ -30,6 +32,7 @@ static const char *driverName = "Linkam"; // eCONTROLLERCONFIG Enumeration #define u64SupportsHeater 0 +#define u64VacuumReady 10 #define u64LnpReady 36 // eSTATUS Enumeration @@ -52,9 +55,16 @@ static const char *driverName = "Linkam"; #define rampRateInValueString "RAMP_RATE_IN_VALUE" /* asynFloat64 r/o */ #define heaterPowerInValueString "HEATER_POWER_IN_VALUE" /* asynFloat64 r/o */ #define heatingOutValueString "HEATING_OUT_VALUE" /* asynInt32 r/w */ +// #define lnpModeOutValueString "LNP_MODE_OUT_VALUE" /* asynInt32 r/w */ #define lnpSpeedOutValueString "LNP_SPEED_OUT_VALUE" /* asynInt32 r/w */ #define lnpSpeedInValueString "LNP_SPEED_IN_VALUE" /* asynFloat64 r/o */ +// +#define vacuumOutValueString "VACUUM_OUT_VALUE" /* asynInt32 r/w */ +#define vacuumLimitOutValueString "VACUUM_LIMIT_OUT_VALUE" /* asynFloat64 r/w */ +#define vacuumLimitInValueString "VACUUM_LIMIT_IN_VALUE" /* asynFloat64 r/o */ +#define pressureInValueString "PRESSURE_IN_VALUE" /* asynFloat64 r/o */ +// #define controllerConfigInValueString "CONTROLLER_CONFIG_IN_VALUE" /* asynInt32 r/o */ #define controllerErrorInValueString "CONTROLLER_ERROR_IN_VALUE" /* asynInt32 r/o */ #define controllerStatusInValueString "CONTROLLER_STATUS_IN_VALUE" /* asynInt32 r/o */ @@ -87,6 +97,10 @@ protected: int lnpModeOutValue_; int lnpSpeedOutValue_; int lnpSpeedInValue_; + int vacuumOutValue_; + int vacuumLimitOutValue_; + int vacuumLimitInValue_; + int pressureInValue_; int controllerConfigInValue_; int controllerErrorInValue_; int controllerStatusInValue_; @@ -95,6 +109,7 @@ protected: #define LAST_LINKAM_PARAM stageConfigInValue_; private: + // asynStatus readTemperature(epicsFloat64 *value); asynStatus readRampLimit(epicsFloat64 *value); asynStatus readRampRate(epicsFloat64 *value); @@ -102,9 +117,16 @@ private: asynStatus setRampRate(epicsFloat64 value); asynStatus readHeaterPower(epicsFloat64 *value); asynStatus setHeating(epicsInt32 value); + // asynStatus setLnpMode(epicsInt32 value); asynStatus setLnpSpeed(epicsInt32 value); asynStatus readLnpSpeed(epicsFloat64 *value); + // + asynStatus setVacuum(epicsInt32 value); + asynStatus setVacuumLimit(epicsFloat64 value); + asynStatus readVacuumLimit(epicsFloat64 *value); + asynStatus readPressure(epicsFloat64 *value); + // asynStatus readControllerConfig(); asynStatus readControllerError(); asynStatus readControllerStatus(); @@ -124,6 +146,9 @@ private: short controllerLnpReady_; short stageLnpAuto_; short stageLnpManual_; + short controllerVacuumReady_; + short stageVacuum_; + }; #define NUM_PARAMS ((int)(&LAST_LINKAM_PARAM - &FIRST_LINKAM_PARAM + 1)) -- GitLab From 77ddec62e721a634187731027c244abba6180b7f Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 11 Sep 2018 15:06:15 -0500 Subject: [PATCH 17/21] Added vacuum controls --- LinkamApp/op/adl/Linkam_T96.adl | 115 ++++++++++- LinkamApp/op/adl/Linkam_T96.ui | 343 +++++++++++++++++++++++++++++++- 2 files changed, 450 insertions(+), 8 deletions(-) diff --git a/LinkamApp/op/adl/Linkam_T96.adl b/LinkamApp/op/adl/Linkam_T96.adl index dd651b2..215b135 100644 --- a/LinkamApp/op/adl/Linkam_T96.adl +++ b/LinkamApp/op/adl/Linkam_T96.adl @@ -5,10 +5,10 @@ file { } display { object { - x=2310 - y=92 + x=2300 + y=84 width=560 - height=340 + height=430 } clr=14 bclr=4 @@ -336,7 +336,7 @@ composite { "related display" { object { x=445 - y=305 + y=395 width=100 height=25 } @@ -430,3 +430,110 @@ menu { bclr=4 } } +text { + object { + x=15 + y=295 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Pressure (mBar)" +} +"text update" { + object { + x=220 + y=295 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):pressure_RBV" + clr=14 + bclr=4 + } + limits { + } +} +menu { + object { + x=385 + y=295 + width=160 + height=25 + } + control { + chan="$(P)$(T):pressure_RBV.SCAN" + clr=14 + bclr=4 + } +} +text { + object { + x=15 + y=325 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Vac Limit (mBar)" +} +"text update" { + object { + x=385 + y=325 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):vacuumLimit_RBV" + clr=14 + bclr=4 + } + limits { + } +} +"text entry" { + object { + x=220 + y=325 + width=160 + height=25 + } + control { + chan="$(P)$(T):vacuumLimit" + clr=14 + bclr=4 + } + limits { + } +} +text { + object { + x=15 + y=355 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Vacuum" +} +menu { + object { + x=383 + y=355 + width=160 + height=25 + } + control { + chan="$(P)$(T):vacuum.VAL" + clr=14 + bclr=4 + } +} diff --git a/LinkamApp/op/adl/Linkam_T96.ui b/LinkamApp/op/adl/Linkam_T96.ui index 855793a..8903226 100644 --- a/LinkamApp/op/adl/Linkam_T96.ui +++ b/LinkamApp/op/adl/Linkam_T96.ui @@ -4,10 +4,10 @@ - 2310 - 92 + 2300 + 84 560 - 340 + 430 @@ -857,7 +857,7 @@ border-radius: 2px; 445 - 305 + 395 100 25 @@ -1132,6 +1132,333 @@ border-radius: 2px; caMenu::Static + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Pressure (mBar) + + + ESimpleLabel::WidthAndHeight + + + + 15 + 295 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 220 + 295 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):pressure_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 385 + 295 + 160 + 25 + + + + $(P)$(T):pressure_RBV.SCAN + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Vac Limit (mBar) + + + ESimpleLabel::WidthAndHeight + + + + 15 + 325 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 385 + 325 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):vacuumLimit_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + decimal + + + caLineEdit::Static + + + + + + 220 + 325 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):vacuumLimit + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + caLineEdit::Static + + + decimal + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Vacuum + + + ESimpleLabel::WidthAndHeight + + + + 15 + 355 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 383 + 355 + 160 + 25 + + + + $(P)$(T):vacuum.VAL + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caMenu::Static + + caLabel_0 caLabel_1 caLabel_2 @@ -1141,6 +1468,9 @@ border-radius: 2px; caFrame_0 caLabel_6 caLabel_7 + caLabel_8 + caLabel_9 + caLabel_10 caLineEdit_0 caLineEdit_1 caLineEdit_2 @@ -1157,6 +1487,11 @@ border-radius: 2px; caTextEntry_2 caLineEdit_5 caMenu_3 + caLineEdit_6 + caMenu_4 + caLineEdit_7 + caTextEntry_3 + caMenu_5 \ No newline at end of file -- GitLab From eb6c6a6bd2e259e789c1441ea3b304288c0f3ff0 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Tue, 11 Sep 2018 16:13:49 -0500 Subject: [PATCH 18/21] Don't PINI things that should be selectively initialized --- LinkamApp/Db/Linkam_T96.db | 10 +++++----- iocs/linkamIOC/iocBoot/ioclinkam/st.cmd | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index 4080f73..a9fc4e8 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -86,7 +86,7 @@ record(bo, "$(P)$(T):lnpMode") { field(DTYP, "asynInt32") field(OUT, "@asyn($(PORT),$(ADDR))LNP_MODE_OUT_VALUE") - field(PINI, "YES") + #!field(PINI, "YES") field(ZNAM, "Manual") field(ONAM, "Auto") field(VAL, 1) @@ -96,7 +96,7 @@ record(longout, "$(P)$(T):lnpSpeed") { field(DTYP, "asynInt32") field(OUT, "@asyn($(PORT),$(ADDR))LNP_SPEED_OUT_VALUE") - field(PINI, "YES") + #!field(PINI, "YES") field(OMSL, "closed_loop") field(DRVH, 100) field(DRVL, 0) @@ -123,7 +123,7 @@ record(bo, "$(P)$(T):vacuum") { field(DTYP, "asynInt32") field(OUT, "@asyn($(PORT),$(ADDR))VACUUM_OUT_VALUE") - field(PINI, "YES") + #!field(PINI, "YES") field(ZNAM, "Off") field(ONAM, "On") field(VAL, 0) @@ -133,12 +133,12 @@ record(ao, "$(P)$(T):vacuumLimit") { field(DTYP, "asynFloat64") field(OUT, "@asyn($(PORT),$(ADDR))VACUUM_LIMIT_OUT_VALUE") - field(PINI, "YES") + #!field(PINI, "YES") field(LINR, "NO CONVERSION") field(PREC, "4") field(EGU, "mBar") field(SCAN, "Passive") - field(VAL, "1013.25") + field(VAL, "10.1325") field(FLNK, "$(P)$(T):vacuumLimit_RBV") } diff --git a/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd b/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd index 9f017df..4bfaa4a 100644 --- a/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd +++ b/iocs/linkamIOC/iocBoot/ioclinkam/st.cmd @@ -23,7 +23,9 @@ iocInit # Turn off heating by default #!dbpf "$(PREFIX)tc1:heating" "0" -# Make Auto mode the default +# Turn off vacuum by default +#!dbpf "$(PREFIX)tc1:vacuum" "0" +# Make LNP Auto mode the default #!dbpf "$(PREFIX)tc1:lnpMode" "1" # Ugly hack to allow disconnecting for manual control via the screen -- GitLab From 256b8dab897c6994153d3b1fb6006a98da9797df Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Wed, 12 Sep 2018 11:01:50 -0500 Subject: [PATCH 19/21] Added comments to give more separation to the asyn interface sections. --- LinkamApp/src/Linkam_T96.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index eba0e4d..18bc218 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -153,7 +153,11 @@ asynStatus Linkam::disconnect(asynUser *pasynUser) return asynSuccess; } - +/* + * + * readFloat64 + * + */ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) { int function = pasynUser->reason; @@ -319,7 +323,11 @@ asynStatus Linkam::readPressure(epicsFloat64 *value) return asynSuccess; } - +/* + * + * writeFloat64 + * + */ asynStatus Linkam::writeFloat64(asynUser *pasynUser, epicsFloat64 value) { int function = pasynUser->reason; @@ -447,6 +455,11 @@ asynStatus Linkam::setVacuumLimit(epicsFloat64 value) } +/* + * + * writeInt32 + * + */ asynStatus Linkam::writeInt32(asynUser *pasynUser, epicsInt32 value) { int function = pasynUser->reason; @@ -651,6 +664,11 @@ asynStatus Linkam::setVacuum(epicsInt32 value) } +/* + * + * readInt32 + * + */ asynStatus Linkam::readInt32(asynUser *pasynUser, epicsInt32 *value) { int function = pasynUser->reason; -- GitLab From 98614910fbfbafa13c8dd65c069bb6f3aaabaa83 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Wed, 12 Sep 2018 16:01:00 -0500 Subject: [PATCH 20/21] Added a pollerThread. Moved querying of the temperature, heater power, LN pump speed, and vacuum to the poller. Also made the poller query the status. Added PVs for relevant status bits. --- LinkamApp/Db/Linkam_T96.db | 65 +++- LinkamApp/op/adl/Linkam_T96.adl | 217 ++++++++++--- LinkamApp/op/adl/Linkam_T96.ui | 541 ++++++++++++++++++++++++++------ LinkamApp/src/Linkam_T96.cpp | 141 +++++++-- LinkamApp/src/Linkam_T96.h | 26 +- 5 files changed, 813 insertions(+), 177 deletions(-) diff --git a/LinkamApp/Db/Linkam_T96.db b/LinkamApp/Db/Linkam_T96.db index a9fc4e8..d3f0172 100644 --- a/LinkamApp/Db/Linkam_T96.db +++ b/LinkamApp/Db/Linkam_T96.db @@ -198,4 +198,67 @@ record(longin, "$(P)$(T):stageConfig_RBV") field(PINI, "YES") field(EGU, "N/A") field(SCAN, "Passive") -} \ No newline at end of file +} + +record(bi, "$(P)$(T):statusError_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))STATUS_CONTROLLER_ERROR") + field(ZNAM, "Ok") + field(ONAM, "Error") + field(SCAN, "I/O Intr") +} + +record(bi, "$(P)$(T):rampAtLimit_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))STATUS_RAMP_SETPOINT") + field(ZNAM, "No") + field(ONAM, "Yes") + field(SCAN, "I/O Intr") +} + +record(bi, "$(P)$(T):heating_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))STATUS_RAMP_STARTED") + field(ZNAM, "Off") + field(ONAM, "On") + field(SCAN, "I/O Intr") +} + +record(bi, "$(P)$(T):vacuumAtLimit_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))STATUS_VACUUM_SETPOINT") + field(ZNAM, "No") + field(ONAM, "Yes") + field(SCAN, "I/O Intr") +} + +record(bi, "$(P)$(T):vacuumStatus_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))STATUS_VACUUM_STARTED") + field(ZNAM, "Off") + field(ONAM, "On") + field(SCAN, "I/O Intr") +} + +record(bi, "$(P)$(T):lnpStatus_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))STATUS_LNP_COOLING_STARTED") + field(ZNAM, "Off") + field(ONAM, "On") + field(SCAN, "I/O Intr") +} + +record(bi, "$(P)$(T):lnpMode_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR))STATUS_LNP_COOLING_AUTO") + field(ZNAM, "Manual") + field(ONAM, "Auto") + field(SCAN, "I/O Intr") +} diff --git a/LinkamApp/op/adl/Linkam_T96.adl b/LinkamApp/op/adl/Linkam_T96.adl index 215b135..8282fda 100644 --- a/LinkamApp/op/adl/Linkam_T96.adl +++ b/LinkamApp/op/adl/Linkam_T96.adl @@ -5,8 +5,8 @@ file { } display { object { - x=2300 - y=84 + x=668 + y=129 width=560 height=430 } @@ -104,7 +104,7 @@ text { object { x=15 y=55 - width=200 + width=190 height=25 } "basic attribute" { @@ -160,6 +160,7 @@ text { clr=14 bclr=4 } + align="horiz. centered" limits { } } @@ -175,6 +176,7 @@ text { clr=14 bclr=4 } + align="horiz. centered" limits { } } @@ -190,6 +192,7 @@ text { clr=14 bclr=4 } + align="horiz. centered" limits { } } @@ -260,6 +263,7 @@ text { clr=14 bclr=4 } + align="horiz. centered" limits { } } @@ -284,55 +288,15 @@ menu { height=25 } monitor { - chan="$(P)$(T):heating" + chan="$(P)$(T):heating_RBV" clr=14 bclr=4 } + align="horiz. centered" format="string" limits { } } -composite { - object { - x=220 - y=175 - width=160 - height=25 - } - "composite name"="" - children { - "message button" { - object { - x=220 - y=175 - width=75 - height=25 - } - control { - chan="$(P)$(T):heating" - clr=0 - bclr=63 - } - label="On" - release_msg="1" - } - "message button" { - object { - x=305 - y=175 - width=75 - height=25 - } - control { - chan="$(P)$(T):heating" - clr=0 - bclr=22 - } - label="Off" - release_msg="0" - } - } -} "related display" { object { x=445 @@ -375,7 +339,7 @@ text { } menu { object { - x=385 + x=220 y=205 width=160 height=25 @@ -413,7 +377,7 @@ menu { clr=14 bclr=4 } - format="string" + align="horiz. centered" limits { } } @@ -434,7 +398,7 @@ text { object { x=15 y=295 - width=200 + width=190 height=25 } "basic attribute" { @@ -454,6 +418,7 @@ text { clr=14 bclr=4 } + align="horiz. centered" limits { } } @@ -494,6 +459,7 @@ text { clr=14 bclr=4 } + align="horiz. centered" limits { } } @@ -526,7 +492,7 @@ text { } menu { object { - x=383 + x=220 y=355 width=160 height=25 @@ -537,3 +503,156 @@ menu { bclr=4 } } +composite { + object { + x=220 + y=175 + width=160 + height=25 + } + "composite name"="" + children { + "message button" { + object { + x=220 + y=175 + width=75 + height=25 + } + control { + chan="$(P)$(T):heating" + clr=0 + bclr=63 + } + label="On" + release_msg="1" + } + "message button" { + object { + x=305 + y=175 + width=75 + height=25 + } + control { + chan="$(P)$(T):heating" + clr=0 + bclr=22 + } + label="Off" + release_msg="0" + } + } +} +"text update" { + object { + x=385 + y=205 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):lnpMode_RBV" + clr=14 + bclr=4 + } + align="horiz. centered" + format="string" + limits { + } +} +rectangle { + object { + x=210 + y=55 + width=8 + height=25 + } + "basic attribute" { + clr=17 + } + "dynamic attribute" { + vis="if not zero" + calc="A" + chan="$(P)$(T):rampAtLimit_RBV" + } +} +"text update" { + object { + x=385 + y=265 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):lnpStatus_RBV" + clr=14 + bclr=4 + } + align="horiz. centered" + format="string" + limits { + } +} +"text update" { + object { + x=385 + y=355 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):vacuumStatus_RBV" + clr=14 + bclr=4 + } + align="horiz. centered" + format="string" + limits { + } +} +rectangle { + object { + x=210 + y=295 + width=8 + height=25 + } + "basic attribute" { + clr=17 + } + "dynamic attribute" { + vis="if not zero" + calc="A" + chan="$(P)$(T):vacuumAtLimit_RBV" + } +} +text { + object { + x=15 + y=385 + width=200 + height=25 + } + "basic attribute" { + clr=14 + } + textix="Controller Error" +} +"text update" { + object { + x=220 + y=385 + width=160 + height=25 + } + monitor { + chan="$(P)$(T):statusError_RBV" + clr=14 + bclr=4 + } + align="horiz. centered" + format="string" + limits { + } +} diff --git a/LinkamApp/op/adl/Linkam_T96.ui b/LinkamApp/op/adl/Linkam_T96.ui index 8903226..c9e06bb 100644 --- a/LinkamApp/op/adl/Linkam_T96.ui +++ b/LinkamApp/op/adl/Linkam_T96.ui @@ -4,8 +4,8 @@ - 2300 - 84 + 668 + 129 560 430 @@ -181,7 +181,7 @@ border-radius: 2px; 15 55 - 200 + 190 25 @@ -724,7 +724,7 @@ border-radius: 2px; caLineEdit::WidthAndHeight - $(P)$(T):heating + $(P)$(T):heating_RBV @@ -765,94 +765,6 @@ border-radius: 2px; caLineEdit::Static - - - - 220 - 175 - 162 - 27 - - - - - - 0 - 0 - 75 - 25 - - - - EPushButton::WidthAndHeight - - - $(P)$(T):heating - - - - 255 - 255 - 255 - - - - - 40 - 147 - 21 - - - - On - - - 1 - - - caMessageButton::Static - - - - - - 85 - 0 - 75 - 25 - - - - EPushButton::WidthAndHeight - - - $(P)$(T):heating - - - - 255 - 255 - 255 - - - - - 190 - 25 - 11 - - - - Off - - - 0 - - - caMessageButton::Static - - - @@ -970,7 +882,7 @@ border-radius: 2px; - 385 + 220 205 160 25 @@ -1096,7 +1008,7 @@ border-radius: 2px; Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter - string + decimal caLineEdit::Static @@ -1160,7 +1072,7 @@ border-radius: 2px; 15 295 - 200 + 190 25 @@ -1432,7 +1344,7 @@ border-radius: 2px; - 383 + 220 355 160 25 @@ -1459,18 +1371,445 @@ border-radius: 2px; caMenu::Static + + + + 220 + 175 + 162 + 27 + + + + + + 0 + 0 + 75 + 25 + + + + EPushButton::WidthAndHeight + + + $(P)$(T):heating + + + + 255 + 255 + 255 + + + + + 40 + 147 + 21 + + + + On + + + 1 + + + caMessageButton::Static + + + + + + 85 + 0 + 75 + 25 + + + + EPushButton::WidthAndHeight + + + $(P)$(T):heating + + + + 255 + 255 + 255 + + + + + 190 + 25 + 11 + + + + Off + + + 0 + + + caMessageButton::Static + + + + + + + 385 + 205 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):lnpMode_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + string + + + caLineEdit::Static + + + + + caGraphics::Rectangle + + + + 210 + 55 + 8 + 25 + + + + + 51 + 153 + 0 + + + + Filled + + + + 51 + 153 + 0 + + + + Solid + + + caGraphics::IfNotZero + + + A + + + $(P)$(T):rampAtLimit_RBV + + + + + + 385 + 265 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):lnpStatus_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + string + + + caLineEdit::Static + + + + + + 385 + 355 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):vacuumStatus_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + string + + + caLineEdit::Static + + + + + caGraphics::Rectangle + + + + 210 + 295 + 8 + 25 + + + + + 51 + 153 + 0 + + + + Filled + + + + 51 + 153 + 0 + + + + Solid + + + caGraphics::IfNotZero + + + A + + + $(P)$(T):vacuumAtLimit_RBV + + + + + QFrame::NoFrame + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + Controller Error + + + ESimpleLabel::WidthAndHeight + + + + 15 + 385 + 200 + 25 + + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 220 + 385 + 160 + 25 + + + + caLineEdit::WidthAndHeight + + + $(P)$(T):statusError_RBV + + + + 0 + 0 + 0 + + + + + 187 + 187 + 187 + + + + caLineEdit::Channel + + + caLineEdit::Channel + + + caLineEdit::Channel + + + 0.0 + + + 1.0 + + + Qt::AlignAbsolute|Qt::AlignLeft|Qt::AlignVCenter + + + string + + + caLineEdit::Static + + caLabel_0 caLabel_1 caLabel_2 caLabel_3 caLabel_4 caLabel_5 - caFrame_0 caLabel_6 caLabel_7 caLabel_8 caLabel_9 caLabel_10 + caFrame_0 + caRectangle_0 + caRectangle_1 + caLabel_11 caLineEdit_0 caLineEdit_1 caLineEdit_2 @@ -1480,8 +1819,6 @@ border-radius: 2px; caLineEdit_3 caMenu_1 caLineEdit_4 - caMessageButton_0 - caMessageButton_1 caRelatedDisplay_0 caMenu_2 caTextEntry_2 @@ -1492,6 +1829,12 @@ border-radius: 2px; caLineEdit_7 caTextEntry_3 caMenu_5 + caMessageButton_0 + caMessageButton_1 + caLineEdit_8 + caLineEdit_9 + caLineEdit_10 + caLineEdit_11 \ No newline at end of file diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index 18bc218..f67d242 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -14,11 +14,19 @@ // Is this needed? using namespace std; +// This needs to come before the Linkam constructor to avoid compiler errors +static void pollerThreadC(void * pPvt) +{ + Linkam *pLinkam = (Linkam *)pPvt; + pLinkam->pollerThread(); +} + Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt32 commPort) : asynPortDriver(portName, MAX_CONTROLLERS, asynInt32Mask | asynFloat64Mask | asynDrvUserMask, asynInt32Mask | asynFloat64Mask, ASYN_MULTIDEVICE | ASYN_CANBLOCK, 1, /* ASYN_CANBLOCK=0, ASYN_MULTIDEVICE=1, autoConnect=1 */ - 0, 0) /* Default priority and stack size */ + 0, 0), /* Default priority and stack size */ + pollTime_(DEFAULT_POLL_TIME) { static const char *functionName = "Linkam"; @@ -26,7 +34,7 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt commType_ = commType; commPort_ = commPort; -// + // createParam(temperatureInValueString, asynParamFloat64, &temperatureInValue_); createParam(rampLimitOutValueString, asynParamFloat64, &rampLimitOutValue_); createParam(rampLimitInValueString, asynParamFloat64, &rampLimitInValue_); @@ -34,21 +42,29 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt createParam(rampRateInValueString, asynParamFloat64, &rampRateInValue_); createParam(heaterPowerInValueString, asynParamFloat64, &heaterPowerInValue_); createParam(heatingOutValueString, asynParamInt32, &heatingOutValue_); -// + // createParam(lnpModeOutValueString, asynParamInt32, &lnpModeOutValue_); createParam(lnpSpeedOutValueString, asynParamInt32, &lnpSpeedOutValue_); createParam(lnpSpeedInValueString, asynParamFloat64, &lnpSpeedInValue_); -// + // createParam(vacuumOutValueString, asynParamInt32, &vacuumOutValue_); createParam(vacuumLimitOutValueString, asynParamFloat64, &vacuumLimitOutValue_); createParam(vacuumLimitInValueString, asynParamFloat64, &vacuumLimitInValue_); createParam(pressureInValueString, asynParamFloat64, &pressureInValue_); -// + // createParam(controllerConfigInValueString, asynParamInt32, &controllerConfigInValue_); createParam(controllerErrorInValueString, asynParamInt32, &controllerErrorInValue_); createParam(controllerStatusInValueString, asynParamInt32, &controllerStatusInValue_); createParam(stageConfigInValueString, asynParamInt32, &stageConfigInValue_); - + // + createParam(statusControllerErrorString, asynParamInt32, &statusControllerError_); + createParam(statusRampSetpointString, asynParamInt32, &statusRampSetpoint_); + createParam(statusRampStartedString, asynParamInt32, &statusRampStarted_); + createParam(statusVacuumSetpointString, asynParamInt32, &statusVacuumSetpoint_); + createParam(statusVacuumStartedString, asynParamInt32, &statusVacuumStarted_); + createParam(statusLnpCoolingStartedString, asynParamInt32, &statusLnpCoolingStarted_); + createParam(statusLnpCoolingAutoString, asynParamInt32, &statusLnpCoolingAuto_); + // Must be in the directory with the DLL when this line is executed //LoadMonoLibrary("LinkamCommsDll.dll"); @@ -59,8 +75,6 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt libraryVersion_ = GetDllVersion(); printf("Comms Library Version %s\n", libraryVersion_); - // Start poller? For now just initialize the temperature - // Force the device to connect now connect(this->pasynUserSelf); @@ -70,7 +84,20 @@ Linkam::Linkam(const char *portName, const epicsUInt32 commType, const epicsUInt // readStageConfig(); - //epicsThreadSleep(5.0); + /* + * Initialize parameters here that need to be set because init record order is + * not predictable or because the corresponding records are PINI=NO + */ + //setIntegerParam(variable_, 1); + + // Start the poller + epicsThreadCreate("LinkamPoller", + epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackMedium), + (EPICSTHREADFUNC)pollerThreadC, + this); + + //epicsThreadSleep(5.0); } Linkam::~Linkam() @@ -153,6 +180,74 @@ asynStatus Linkam::disconnect(asynUser *pasynUser) return asynSuccess; } +/* + * + * poller + * + */ +void Linkam::pollerThread() +{ + /* This function runs in a separate thread. It waits for the poll time. */ + static const char *functionName = "pollerThread"; + // Other variable declarations + epicsInt32 ival; + epicsFloat64 fval; + + while (1) + { + lock(); + + // Get the controller status + this->controllerStatus_ = GetStatus(); + + /* + * Parse the controller status + */ + ival = ( controllerStatus_ & ((epicsUInt64)1 << (int)u64ControllerError) ) ? 1 : 0; + setIntegerParam(statusControllerError_, ival); + + ival = ( controllerStatus_ & ((epicsUInt64)1 << (int)u64Heater1RampSetpoint) ) ? 1 : 0; + setIntegerParam(statusRampSetpoint_, ival); + + ival = ( controllerStatus_ & ((epicsUInt64)1 << (int)u64Heater1Started) ) ? 1 : 0; + setIntegerParam(statusRampStarted_, ival); + + ival = ( controllerStatus_ & ((epicsUInt64)1 << (int)u64VacuumSetPoint) ) ? 1 : 0; + setIntegerParam(statusVacuumSetpoint_, ival); + + ival = ( controllerStatus_ & ((epicsUInt64)1 << (int)u64VacuumControlStarted) ) ? 1 : 0; + setIntegerParam(statusVacuumStarted_, ival); + + ival = ( controllerStatus_ & ((epicsUInt64)1 << (int)u64LnpCoolingStarted) ) ? 1 : 0; + setIntegerParam(statusLnpCoolingStarted_, ival); + + ival = ( controllerStatus_ & ((epicsUInt64)1 << (int)u64LnpCoolingAuto) ) ? 1 : 0; + setIntegerParam(statusLnpCoolingAuto_, ival); + + // Get the temperature + fval = GetValue(u32Heater1TempR); + setDoubleParam(temperatureInValue_, fval); + + // Get the heater power + fval = GetValue(u32Heater1PowerR); + setDoubleParam(heaterPowerInValue_, fval); + + // Get the LN pump speed + fval = GetValue(u32Heater1LnpSpeedR); + setDoubleParam(lnpSpeedInValue_, fval); + + // Get the pressure + fval = GetValue(u32VacuumR); + setDoubleParam(pressureInValue_, fval); + + callParamCallbacks(); + + unlock(); + epicsThreadSleep(pollTime_); + } +} + + /* * * readFloat64 @@ -168,12 +263,12 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) "%s:%s, port %s, function = %d\n", driverName, functionName, this->portName, function); - if (function == temperatureInValue_) { + /* if (function == temperatureInValue_) { // Read the temperature from the controller (Celsius) status = readTemperature(value); - } else if (function == rampLimitInValue_) { + } else */ if (function == rampLimitInValue_) { // Read the ramp limit from the controller (Celsius) status = readRampLimit(value); @@ -183,7 +278,7 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) // Read the ramp rate from the controller (C/min) status = readRampRate(value); - } else if (function == heaterPowerInValue_) { + } /* else if (function == heaterPowerInValue_) { // Read the temperature from the controller (Watts) status = readHeaterPower(value); @@ -193,17 +288,17 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) // Read the LN Pump speed from the controller (0-100%) status = readLnpSpeed(value); - } else if (function == vacuumLimitInValue_) { + } */ else if (function == vacuumLimitInValue_) { // Read the vacuum limit from the controller (mBar) status = readVacuumLimit(value); - } else if (function == pressureInValue_) { + } /* else if (function == pressureInValue_) { // Read the vacuum pressure from the controller (mBar) status = readPressure(value); - } else { + } */ else { status = asynPortDriver::readFloat64(pasynUser,value); } @@ -215,7 +310,7 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) return (status==0) ? asynSuccess : asynError; } -asynStatus Linkam::readTemperature(epicsFloat64 *value) +/*asynStatus Linkam::readTemperature(epicsFloat64 *value) { static const char *functionName = "readTemperature"; @@ -231,7 +326,7 @@ asynStatus Linkam::readTemperature(epicsFloat64 *value) driverName, functionName, this->portName, *value); return asynSuccess; -} +}*/ asynStatus Linkam::readRampLimit(epicsFloat64 *value) { @@ -263,7 +358,7 @@ asynStatus Linkam::readRampRate(epicsFloat64 *value) return asynSuccess; } -asynStatus Linkam::readHeaterPower(epicsFloat64 *value) +/*asynStatus Linkam::readHeaterPower(epicsFloat64 *value) { static const char *functionName = "readHeaterPower"; @@ -276,9 +371,9 @@ asynStatus Linkam::readHeaterPower(epicsFloat64 *value) driverName, functionName, this->portName, *value); return asynSuccess; -} +}*/ -asynStatus Linkam::readLnpSpeed(epicsFloat64 *value) +/*asynStatus Linkam::readLnpSpeed(epicsFloat64 *value) { static const char *functionName = "readLnpSpeed"; @@ -291,7 +386,7 @@ asynStatus Linkam::readLnpSpeed(epicsFloat64 *value) driverName, functionName, this->portName, *value); return asynSuccess; -} +}*/ asynStatus Linkam::readVacuumLimit(epicsFloat64 *value) { @@ -308,7 +403,7 @@ asynStatus Linkam::readVacuumLimit(epicsFloat64 *value) return asynSuccess; } -asynStatus Linkam::readPressure(epicsFloat64 *value) +/*asynStatus Linkam::readPressure(epicsFloat64 *value) { static const char *functionName = "readPressure"; @@ -321,7 +416,7 @@ asynStatus Linkam::readPressure(epicsFloat64 *value) driverName, functionName, this->portName, *value); return asynSuccess; -} +}*/ /* * diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index bcae444..6d09398 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -9,6 +9,7 @@ static const char *driverName = "Linkam"; #define MAX_CONTROLLERS 1 +#define DEFAULT_POLL_TIME 0.05 // eVALUETYPE Enumeration #define u32Heater1TempR 0 @@ -28,7 +29,6 @@ static const char *driverName = "Linkam"; #define u64SupportsHeater1Present 26 #define u64Heater1SupportsTemperatureControl 27 #define u64SupportsVacuum 48 -#define u64SupportsHumidity 52 // eCONTROLLERCONFIG Enumeration #define u64SupportsHeater 0 @@ -41,8 +41,6 @@ static const char *driverName = "Linkam"; #define u64Heater1Started 2 #define u64VacuumSetPoint 5 #define u64VacuumControlStarted 6 -#define u64HumidityRampSetpoint 9 -#define u64HumidityControlStarted 10 #define u64LnpCoolingStarted 11 #define u64LnpCoolingAuto 12 @@ -69,6 +67,14 @@ static const char *driverName = "Linkam"; #define controllerErrorInValueString "CONTROLLER_ERROR_IN_VALUE" /* asynInt32 r/o */ #define controllerStatusInValueString "CONTROLLER_STATUS_IN_VALUE" /* asynInt32 r/o */ #define stageConfigInValueString "STAGE_CONFIG_IN_VALUE" /* asynInt32 r/o */ +// +#define statusControllerErrorString "STATUS_CONTROLLER_ERROR" /* asynInt32 r/o */ +#define statusRampSetpointString "STATUS_RAMP_SETPOINT" /* asynInt32 r/o */ +#define statusRampStartedString "STATUS_RAMP_STARTED" /* asynInt32 r/o */ +#define statusVacuumSetpointString "STATUS_VACUUM_SETPOINT" /* asynInt32 r/o */ +#define statusVacuumStartedString "STATUS_VACUUM_STARTED" /* asynInt32 r/o */ +#define statusLnpCoolingStartedString "STATUS_LNP_COOLING_STARTED" /* asynInt32 r/o */ +#define statusLnpCoolingAutoString "STATUS_LNP_COOLING_AUTO" /* asynInt32 r/o */ /* * Class definition for the Linkam class @@ -85,6 +91,8 @@ public: virtual asynStatus disconnect(asynUser *pasynUser); virtual asynStatus connect(asynUser *pasynUser); virtual ~Linkam(); + // These should be private but are called from C + virtual void pollerThread(void); protected: int temperatureInValue_; @@ -105,8 +113,15 @@ protected: int controllerErrorInValue_; int controllerStatusInValue_; int stageConfigInValue_; - #define FIRST_LINKAM_PARAM temperatureInValue_; - #define LAST_LINKAM_PARAM stageConfigInValue_; + int statusControllerError_; + int statusRampSetpoint_; + int statusRampStarted_; + int statusVacuumSetpoint_; + int statusVacuumStarted_; + int statusLnpCoolingStarted_; + int statusLnpCoolingAuto_; + #define FIRST_LINKAM_PARAM temperatureInValue_; + #define LAST_LINKAM_PARAM statusLnpCoolingAuto_; private: // @@ -133,6 +148,7 @@ private: asynStatus readStageConfig(); void report(FILE *fp, int details); + double pollTime_; epicsUInt32 commType_; epicsUInt32 commPort_; int commStatus_; -- GitLab From 735dd0a8b17187f758d5920bfbd31ca2ac856464 Mon Sep 17 00:00:00 2001 From: Kevin Peterson Date: Wed, 12 Sep 2018 16:07:50 -0500 Subject: [PATCH 21/21] Removed the commented-out code that is no longer needed now that there is a poller thread. --- LinkamApp/src/Linkam_T96.cpp | 88 ++---------------------------------- LinkamApp/src/Linkam_T96.h | 4 -- 2 files changed, 3 insertions(+), 89 deletions(-) diff --git a/LinkamApp/src/Linkam_T96.cpp b/LinkamApp/src/Linkam_T96.cpp index f67d242..e871e7b 100644 --- a/LinkamApp/src/Linkam_T96.cpp +++ b/LinkamApp/src/Linkam_T96.cpp @@ -263,12 +263,7 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) "%s:%s, port %s, function = %d\n", driverName, functionName, this->portName, function); - /* if (function == temperatureInValue_) { - - // Read the temperature from the controller (Celsius) - status = readTemperature(value); - - } else */ if (function == rampLimitInValue_) { + if (function == rampLimitInValue_) { // Read the ramp limit from the controller (Celsius) status = readRampLimit(value); @@ -278,27 +273,12 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) // Read the ramp rate from the controller (C/min) status = readRampRate(value); - } /* else if (function == heaterPowerInValue_) { - - // Read the temperature from the controller (Watts) - status = readHeaterPower(value); - - } else if (function == lnpSpeedInValue_) { - - // Read the LN Pump speed from the controller (0-100%) - status = readLnpSpeed(value); - - } */ else if (function == vacuumLimitInValue_) { + } else if (function == vacuumLimitInValue_) { // Read the vacuum limit from the controller (mBar) status = readVacuumLimit(value); - } /* else if (function == pressureInValue_) { - - // Read the vacuum pressure from the controller (mBar) - status = readPressure(value); - - } */ else { + } else { status = asynPortDriver::readFloat64(pasynUser,value); } @@ -310,24 +290,6 @@ asynStatus Linkam::readFloat64(asynUser *pasynUser, epicsFloat64 *value) return (status==0) ? asynSuccess : asynError; } -/*asynStatus Linkam::readTemperature(epicsFloat64 *value) -{ - static const char *functionName = "readTemperature"; - - this->lock(); - *value = GetValue(u32Heater1TempR); - this->unlock(); - // There probably isn't a good reason to retain the temperature in a private variable; - // The value is already stored in the parameter library - temperatureRbv_ = *value; - - asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %lf\n", - driverName, functionName, this->portName, *value); - - return asynSuccess; -}*/ - asynStatus Linkam::readRampLimit(epicsFloat64 *value) { static const char *functionName = "readRampLimit"; @@ -358,36 +320,6 @@ asynStatus Linkam::readRampRate(epicsFloat64 *value) return asynSuccess; } -/*asynStatus Linkam::readHeaterPower(epicsFloat64 *value) -{ - static const char *functionName = "readHeaterPower"; - - this->lock(); - *value = GetValue(u32Heater1PowerR); - this->unlock(); - - asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %lf\n", - driverName, functionName, this->portName, *value); - - return asynSuccess; -}*/ - -/*asynStatus Linkam::readLnpSpeed(epicsFloat64 *value) -{ - static const char *functionName = "readLnpSpeed"; - - this->lock(); - *value = GetValue(u32Heater1LnpSpeedR); - this->unlock(); - - asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %lf\n", - driverName, functionName, this->portName, *value); - - return asynSuccess; -}*/ - asynStatus Linkam::readVacuumLimit(epicsFloat64 *value) { static const char *functionName = "readVacuumLimit"; @@ -403,20 +335,6 @@ asynStatus Linkam::readVacuumLimit(epicsFloat64 *value) return asynSuccess; } -/*asynStatus Linkam::readPressure(epicsFloat64 *value) -{ - static const char *functionName = "readPressure"; - - this->lock(); - *value = GetValue(u32VacuumR); - this->unlock(); - - asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, - "%s:%s, port %s, value = %lf\n", - driverName, functionName, this->portName, *value); - - return asynSuccess; -}*/ /* * diff --git a/LinkamApp/src/Linkam_T96.h b/LinkamApp/src/Linkam_T96.h index 6d09398..706f7d1 100644 --- a/LinkamApp/src/Linkam_T96.h +++ b/LinkamApp/src/Linkam_T96.h @@ -125,22 +125,18 @@ protected: private: // - asynStatus readTemperature(epicsFloat64 *value); asynStatus readRampLimit(epicsFloat64 *value); asynStatus readRampRate(epicsFloat64 *value); asynStatus setRampLimit(epicsFloat64 value); asynStatus setRampRate(epicsFloat64 value); - asynStatus readHeaterPower(epicsFloat64 *value); asynStatus setHeating(epicsInt32 value); // asynStatus setLnpMode(epicsInt32 value); asynStatus setLnpSpeed(epicsInt32 value); - asynStatus readLnpSpeed(epicsFloat64 *value); // asynStatus setVacuum(epicsInt32 value); asynStatus setVacuumLimit(epicsFloat64 value); asynStatus readVacuumLimit(epicsFloat64 *value); - asynStatus readPressure(epicsFloat64 *value); // asynStatus readControllerConfig(); asynStatus readControllerError(); -- GitLab