From afec971d71a9d16b16db59247127a6a37840a02c Mon Sep 17 00:00:00 2001
From: marcofilho <marco.filho@ess.eu>
Date: Tue, 7 Jan 2025 23:41:57 +0100
Subject: [PATCH] Add reading temperature. Not directly from hardware.

This reads Temperature from API memory and sets the structure for future
parameters such as BandgapADC, etc...

The timestamp is also read and put into a PV in order to be explicit for
user when was the last time it was read from hardware.
---
 vmmTblApp/Db/vmm.template | 27 ++++++++++++++++
 vmmTblApp/src/vmm_tbl.cpp | 65 ++++++++++++++++++++++++++++++++++++++-
 vmmTblApp/src/vmm_tbl.h   |  3 ++
 3 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/vmmTblApp/Db/vmm.template b/vmmTblApp/Db/vmm.template
index f1d6227..0e9b641 100644
--- a/vmmTblApp/Db/vmm.template
+++ b/vmmTblApp/Db/vmm.template
@@ -60,4 +60,31 @@ record(ai, "$(P)$(R)$(HYB)$(VMM)$(C)ADCVal-R"){
     field(PINI, "YES")
     field(SCAN, "I/O Intr")
     field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))HYB_$(HYB)_$(VMM)_ADCVAL")
+    field(FLNK, "$(P)$(R)$(HYB)$(VMM)$(C)#ADCParams")
+}
+
+record(fanout, "$(P)$(R)$(HYB)$(VMM)$(C)#ADCParams") {
+    field(LNK1, "$(P)$(R)$(HYB)$(VMM)$(C)Temp-R")
+    #Pulser...
+    #Bandgap...
+    #Threshold...
+}
+
+# Is updated only when AnalogMon = 67 and ADCVal is read
+record(ai, "$(P)$(R)$(HYB)$(VMM)$(C)Temp-R"){
+    field(DESC, "Last read temperature")
+    field(DTYP, "asynFloat64")
+    field(PINI, "YES")
+    field(SCAN, "Passive")
+    field(FLNK, "$(P)$(R)$(HYB)$(VMM)$(C)TempTmstp-R")
+    field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))HYB_$(HYB)_$(VMM)_TEMP")
+}
+
+record(waveform, "$(P)$(R)$(HYB)$(VMM)$(C)TempTmstp-R") {
+    field(DESC, "Timestamp from last temperature read")
+    field(DTYP, "asynOctetRead")
+    field(NELM, "256")
+    field(FTVL, "CHAR")
+    field(INP,  "@asyn($(PORT),$(ADDR),$(TIMEOUT))HYB_$(HYB)_$(VMM)_TMP_TMSTP")
+    field(SCAN, "Passive")
 }
\ No newline at end of file
diff --git a/vmmTblApp/src/vmm_tbl.cpp b/vmmTblApp/src/vmm_tbl.cpp
index 17cc41d..76fca14 100644
--- a/vmmTblApp/src/vmm_tbl.cpp
+++ b/vmmTblApp/src/vmm_tbl.cpp
@@ -104,10 +104,49 @@ endOfReadInt32:
   return status;
 }
 
+asynStatus VMMTbl::readFloat64(asynUser *pasynUser, epicsFloat64 *value) {
+  int function = pasynUser->reason;
+  asynStatus status = asynSuccess;
+  GenericParameter val;
+  vmmStatus result = vmmSuccess;
+  int param_index = 0, hyb_index, vmm_index;
+
+  if (function < FIRST_VMM_PARAM) {
+    return asynPortDriver::readFloat64(pasynUser, value);
+  }
+
+  // Search for parameter in adequate parameter vectors. If found, execute correct function for specific hybrid.
+  param_index = VecUtils::getIndex(vmmTemp_, function, hyb_index, vmm_index);
+  if (param_index >= 0) {
+    result = this->pVmmAPI->getTemperature(hyb_index, vmm_index, val);
+    *value = (epicsFloat64)val.value_float;
+    goto endOfReadFloat64;
+  }
+
+endOfReadFloat64:
+  if (param_index < 0) {
+    asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Failed to find parameter %d.\n", driverName, __FUNCTION__,
+              function);
+    *value = 0;
+    return asynError;
+  }
+  if (result != vmmSuccess) {
+    asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: Failed to adequately read parameter: %d.\n", driverName,
+              __FUNCTION__, function);
+    status = asynError;
+  }
+
+  setDoubleParam(function, *value);
+  callParamCallbacks();
+
+  return status;
+}
+
 asynStatus VMMTbl::readOctet(asynUser *pasynUser, char *value, size_t nChars, size_t *nActual, int *eomReason) {
   asynStatus status = asynSuccess;
   vmmStatus result;
   int function = pasynUser->reason;
+  int hyb_index, vmm_index;
   std::string read = "";
 
   /* If this parameter belongs to a base class call its method */
@@ -134,6 +173,20 @@ asynStatus VMMTbl::readOctet(asynUser *pasynUser, char *value, size_t nChars, si
     goto endOfReadOctet;
   }
 
+  param_index = VecUtils::getIndex(vmmTempTmstp_, function, hyb_index, vmm_index);
+  if (param_index >= 0) {
+    GenericParameter val;
+    result = this->pVmmAPI->getTemperature(hyb_index, vmm_index, val);
+
+    // Converting timestamp to string
+    std::time_t time_t_value = std::chrono::system_clock::to_time_t(val.timestamp);
+    std::tm tm_value = *std::localtime(&time_t_value);
+    std::ostringstream oss;
+    oss << std::put_time(&tm_value, "%Y-%m-%d %H:%M:%S");
+    read = oss.str();
+    goto endOfReadOctet;
+  }
+
 endOfReadOctet:
   if (param_index < 0) {
     value[0] = '\0';
@@ -194,7 +247,7 @@ asynStatus VMMTbl::createEpicsParams() {
       createParamAndStoreInVector("HYB_" + std::to_string(hyb) + key, typ, vec);
     }
 
-    std::vector<int> SC, SL, ST, STH, SM, SD, SMX, ADCIDX, ADCVAL;
+    std::vector<int> SC, SL, ST, STH, SM, SD, SMX, ADCIDX, ADCVAL, TEMP, TEMPTMSTP;
 
     for (int vmm = 0; vmm < VMMS_PER_HYBRID; vmm++) {
       param_name.str("");
@@ -232,6 +285,14 @@ asynStatus VMMTbl::createEpicsParams() {
       param_name.str("");
       param_name << "HYB_" << hyb << "_" << vmm << "_ADCVAL";
       createParamAndStoreInVector(param_name.str(), asynParamInt32, &ADCVAL);
+
+      param_name.str("");
+      param_name << "HYB_" << hyb << "_" << vmm << "_TEMP";
+      createParamAndStoreInVector(param_name.str(), asynParamFloat64, &TEMP);
+
+      param_name.str("");
+      param_name << "HYB_" << hyb << "_" << vmm << "_TMP_TMSTP";
+      createParamAndStoreInVector(param_name.str(), asynParamOctet, &TEMPTMSTP);
     }
 
     this->vmmSC_.push_back(SC);
@@ -243,6 +304,8 @@ asynStatus VMMTbl::createEpicsParams() {
     this->vmmSMX_.push_back(SMX);
     this->vmmADCIDX_.push_back(ADCIDX);
     this->vmmADCVAL_.push_back(ADCVAL);
+    this->vmmTemp_.push_back(TEMP);
+    this->vmmTempTmstp_.push_back(TEMPTMSTP);
   }
 
   return asynSuccess;
diff --git a/vmmTblApp/src/vmm_tbl.h b/vmmTblApp/src/vmm_tbl.h
index 2ee6df2..fa7c468 100644
--- a/vmmTblApp/src/vmm_tbl.h
+++ b/vmmTblApp/src/vmm_tbl.h
@@ -22,6 +22,7 @@ class VMMTbl : public asynPortDriver {
   virtual asynStatus writeInt8Array(asynUser *pasynUser, epicsInt8 *value, size_t nElements);
   virtual asynStatus readInt8Array(asynUser *pasynUser, epicsInt8 *value, size_t nElements, size_t *nIn);
   virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
+  virtual asynStatus readFloat64(asynUser *pasynUser, epicsFloat64 *value);
   virtual asynStatus readOctet(asynUser *pasynUser, char *value, size_t maxChars, size_t *nActual, int *eomReason);
   asynStatus createParamAndStoreInVector(std::string paramName, asynParamType typ, std::vector<int> *vectorToStore);
 
@@ -52,6 +53,8 @@ class VMMTbl : public asynPortDriver {
   std::vector<std::vector<int>> vmmSMX_;
   std::vector<std::vector<int>> vmmADCIDX_;
   std::vector<std::vector<int>> vmmADCVAL_;
+  std::vector<std::vector<int>> vmmTemp_;
+  std::vector<std::vector<int>> vmmTempTmstp_;
 
  private:
   static constexpr const char *driverName = "VMMTbl";
-- 
GitLab