From 58054805889beaf05a1789f14985de592ef00e63 Mon Sep 17 00:00:00 2001
From: George Nicolas Kontogiorgos <george.kontogiorgos@ess.eu>
Date: Sat, 2 Sep 2023 01:33:11 +0200
Subject: [PATCH 1/5] Add LICENSE file

---
 LICENSE | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 LICENSE

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0730be6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2023, European Spallation Source ERIC
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- 
GitLab


From 3bbdbb21cfd56818f87d1f6506fc4233d1e3c87a Mon Sep 17 00:00:00 2001
From: George Kontogiorgos <george.kontogiorgos@ess.eu>
Date: Tue, 26 Sep 2023 16:48:41 +0200
Subject: [PATCH 2/5] Fix debug messages behavior related to the server queries

---
 .gitignore                                   | 2 +-
 cmds/st-asyn-dev.cmd                         | 2 +-
 etalonMultiLine2App/src/etalonMultiLine2.cpp | 6 ++----
 3 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore
index 6000f6d..6105fc8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,4 @@ Makefile.E3
 .cvsignore
 O.*
 core.*
-etalon
+etalon*
diff --git a/cmds/st-asyn-dev.cmd b/cmds/st-asyn-dev.cmd
index 7b80576..6017eb9 100644
--- a/cmds/st-asyn-dev.cmd
+++ b/cmds/st-asyn-dev.cmd
@@ -21,7 +21,7 @@ drvAsynIPPortConfigure("$(PORTNAME)", "$(IPADDR):$(IPPORT)")
 #asynSetTraceMask("$(PORTNAME)",-1,0x9) 
 #asynSetTraceIOMask("$(PORTNAME)",-1,0x2)
 
-etalonMultiLine2Config("ETALON", "$(PORTNAME)", 0)
+etalonMultiLine2Config("ETALON", "$(PORTNAME)", 1)
 
 dbLoadRecords(control.db, "P=$(PREFIX),R=:, PORT="ETALON", ADDR=0, SLOW_SCAN=$(SLOW_SCAN), FAST_SCAN=$(FAST_SCAN)")
 dbLoadRecords(data.db, "P=$(PREFIX),R=:, PORT="ETALON", ADDR=0, SLOW_SCAN=$(SLOW_SCAN), FAST_SCAN=$(FAST_SCAN)")
diff --git a/etalonMultiLine2App/src/etalonMultiLine2.cpp b/etalonMultiLine2App/src/etalonMultiLine2.cpp
index 4e2fbae..b199fe1 100644
--- a/etalonMultiLine2App/src/etalonMultiLine2.cpp
+++ b/etalonMultiLine2App/src/etalonMultiLine2.cpp
@@ -210,7 +210,8 @@ asynStatus EtalonMultiline2::serverQuery(const char * query,
                                              reply, MAX_REPLY_SIZE,
                                              TIMEOUT,
                                              &nWrite, &nRead, &eomReason);
-
+    spdlog::debug("query: {}", query);
+    spdlog::debug("reply: {}", reply);
 
     if (status)
     {
@@ -497,7 +498,6 @@ asynStatus EtalonMultiline2::performMeasurement(asynStatus status)
         char tempBuffer[10];
         for(int i = 0; i < numSelectedChannels; i++)
         {
-            spdlog::debug("{}", measChEna_[i]);
             if(measChEna_[i])
             {
                 strcat(query,",");
@@ -516,8 +516,6 @@ asynStatus EtalonMultiline2::performMeasurement(asynStatus status)
         status = callParamCallbacks();
 
     status = serverQuery(query, reply, status);
-    spdlog::debug("query: {}", query);
-    spdlog::debug("reply: {}", reply);
 
     if(strcmp(reply, "ERROR") == 0)
     {
-- 
GitLab


From 01968228d7e36833fb61c34ea134316d66e6ac9b Mon Sep 17 00:00:00 2001
From: George Kontogiorgos <george.kontogiorgos@ess.eu>
Date: Tue, 26 Sep 2023 16:51:35 +0200
Subject: [PATCH 3/5] Do not send a query to server if none of channels are
 enabled

---
 etalonMultiLine2App/src/etalonMultiLine2.cpp | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/etalonMultiLine2App/src/etalonMultiLine2.cpp b/etalonMultiLine2App/src/etalonMultiLine2.cpp
index b199fe1..1b7792b 100644
--- a/etalonMultiLine2App/src/etalonMultiLine2.cpp
+++ b/etalonMultiLine2App/src/etalonMultiLine2.cpp
@@ -490,7 +490,7 @@ asynStatus EtalonMultiline2::performMeasurement(asynStatus status)
 {
     char query[MAX_QUERY_SIZE];
     char reply[MAX_REPLY_SIZE];
-
+    short int enabledChannelsCounter = 0;
     if(!status)
     {
         strcpy(query, "measurelength");
@@ -506,10 +506,12 @@ asynStatus EtalonMultiline2::performMeasurement(asynStatus status)
                 strcat(query,",");
                 sprintf(tempBuffer, "%lf", measPreshot_[i]);
                 strcat(query, tempBuffer);
+                enabledChannelsCounter++;
             }
         }
     }
-
+    if(enabledChannelsCounter)
+    {
     if(!status)
         status = setIntegerParam(MeasState_, StateMeasuring);
     if(!status)
@@ -659,6 +661,13 @@ asynStatus EtalonMultiline2::performMeasurement(asynStatus status)
             if(!status)
                 status = setIntegerParam(MeasState_, StateIdle);
         }
+        }
+    }
+    else
+    {
+        spdlog::warn("There are no enabled channels, I am not going to send a measure request to the server. Setting status to ERROR.");
+        if(!status)
+            status = setIntegerParam(MeasState_, StateError);
     }
     return status;
 }
-- 
GitLab


From 2c2020f9e10a57e1b8457a28cf936a76e083468e Mon Sep 17 00:00:00 2001
From: George Kontogiorgos <george.kontogiorgos@ess.eu>
Date: Wed, 27 Sep 2023 16:27:03 +0200
Subject: [PATCH 4/5] Fix counter behaviour to allow a unique ID for each
 measurement

---
 etalonMultiLine2App/Db/control.template      |  23 +-
 etalonMultiLine2App/Db/data.template         |  60 +++-
 etalonMultiLine2App/Db/status.template       |  35 +-
 etalonMultiLine2App/src/etalonMultiLine2.cpp | 334 +++++++++++--------
 etalonMultiLine2App/src/etalonMultiLine2.h   |  46 ++-
 5 files changed, 337 insertions(+), 161 deletions(-)

diff --git a/etalonMultiLine2App/Db/control.template b/etalonMultiLine2App/Db/control.template
index 002e428..04b7f90 100644
--- a/etalonMultiLine2App/Db/control.template
+++ b/etalonMultiLine2App/Db/control.template
@@ -31,7 +31,7 @@ record(waveform, "$(P)$(R)MeasEnableChannel-S") {
     field(DTYP, "asynInt32ArrayIn")
     field(PINI, "YES")
     field(FTVL, "LONG")
-    field(NELM, 40)
+    field(NELM, "40")
     field(INP,  "@asyn($(PORT),$(ADDR))MEAS_ENABLE_CHANNEL")
     info(autosaveFields, "VAL")
 }
@@ -41,7 +41,7 @@ record(waveform, "$(P)$(R)MeasPreshot-S") {
     field(DTYP, "asynFloat64ArrayIn")
     field(PINI, "YES")
     field(FTVL, "DOUBLE")
-    field(NELM, 40)
+    field(NELM, "40")
     field(INP,  "@asyn($(PORT),$(ADDR))MEAS_CHANNEL_PRESHOT")
     info(autosaveFields, "VAL")
 }
@@ -81,3 +81,22 @@ record(bo, "$(P)$(R)AlignmentStart-S") {
     field(ONAM, "True")
     field(OUT,  "@asyn($(PORT),$(ADDR))ALIGNMENT_START")
 }
+
+record(waveform, "$(P)$(R)ErrorMask-S") {
+    field(DESC, "Mask to enable checking errors")
+    field(DTYP, "asynInt32ArrayIn")
+    field(PINI, "YES")
+    field(FTVL, "LONG")
+    field(INP,  "@asyn($(PORT),$(ADDR))ERROR_MASK")
+    field(NELM, "40")
+    info(autosaveFields, "VAL")
+}
+
+record(ao, "$(P)$(R)GlobalMeasCounter-S") {
+    field(DESC, "Set measurement counter")
+    field(DOL, "$(P)$(R)GlobalMeasCounter-R")
+    field(OMSL, "closed_loop")
+    field(DTYP, "asynInt32")
+    field(PINI, "YES")
+    field(OUT,  "@asyn($(PORT),$(ADDR))GLOBAL_MEAS_COUNTER")
+}
diff --git a/etalonMultiLine2App/Db/data.template b/etalonMultiLine2App/Db/data.template
index 5cef77b..2b21c26 100644
--- a/etalonMultiLine2App/Db/data.template
+++ b/etalonMultiLine2App/Db/data.template
@@ -18,6 +18,16 @@ record(waveform, "$(P)$(R)DataLenLength-R") {
     field(NELM, "40")
 }
 
+record(waveform, "$(P)$(R)LstValDataLenLength-R") {
+    field(DESC, "Last valid Li: measured length")
+    field(DTYP, "asynFloat64ArrayIn")
+    field(EGU,  "mm")
+    field(SCAN, "I/O Intr")
+    field(INP,  "@asyn($(PORT),$(ADDR))LAST_VALID_MEASURED_LENGTH")
+    field(FTVL, "DOUBLE")
+    field(NELM, "40")
+}
+
 record(waveform, "$(P)$(R)DataLenIMin-R") {
     field(DESC, "IMini: minimum intensity")
     field(DTYP, "asynInt32ArrayIn")
@@ -27,6 +37,15 @@ record(waveform, "$(P)$(R)DataLenIMin-R") {
     field(NELM, "40")
 }
 
+record(waveform, "$(P)$(R)LstVldDataLenIMin-R") {
+    field(DESC, "Last vallid IMini: minimum intensity")
+    field(DTYP, "asynInt32ArrayIn")
+    field(SCAN, "I/O Intr")
+    field(INP,  "@asyn($(PORT),$(ADDR))LAST_VALID_MEASURED_IMIN")
+    field(FTVL, "LONG")
+    field(NELM, "40")
+}
+
 record(waveform, "$(P)$(R)DataLenIMax-R") {
     field(DESC, "IMaxi: maximum intensity")
     field(DTYP, "asynInt32ArrayIn")
@@ -36,6 +55,15 @@ record(waveform, "$(P)$(R)DataLenIMax-R") {
     field(NELM, "40")
 }
 
+record(waveform, "$(P)$(R)LstVldDataLenIMax-R") {
+    field(DESC, "Last valid IMaxi: maximum intensity")
+    field(DTYP, "asynInt32ArrayIn")
+    field(SCAN, "I/O Intr")
+    field(INP,  "@asyn($(PORT),$(ADDR))LAST_VALID_MEASURED_IMAX")
+    field(FTVL, "LONG")
+    field(NELM, "40")
+}
+
 record(waveform, "$(P)$(R)DataLenTemp-R") {
     field(DESC, "Ti: temperature")
     field(DTYP, "asynFloat64ArrayIn")
@@ -46,6 +74,16 @@ record(waveform, "$(P)$(R)DataLenTemp-R") {
     field(NELM, "40")
 }
 
+record(waveform, "$(P)$(R)LstVldDataLenTemp-R") {
+    field(DESC, "Last valid Ti: temperature")
+    field(DTYP, "asynFloat64ArrayIn")
+    field(EGU,  "o.C")
+    field(SCAN, "I/O Intr")
+    field(INP,  "@asyn($(PORT),$(ADDR))LAST_VALID_MEASURED_TEMP")
+    field(FTVL, "DOUBLE")
+    field(NELM, "40")
+}
+
 record(waveform, "$(P)$(R)DataLenPress-R") {
     field(DESC, "Pi: air pressure")
     field(DTYP, "asynFloat64ArrayIn")
@@ -56,6 +94,16 @@ record(waveform, "$(P)$(R)DataLenPress-R") {
     field(NELM, "40")
 }
 
+record(waveform, "$(P)$(R)LstVldDataLenPress-R") {
+    field(DESC, "Last valid Pi: air pressure")
+    field(DTYP, "asynFloat64ArrayIn")
+    field(EGU,  "hPa")
+    field(SCAN, "I/O Intr")
+    field(INP,  "@asyn($(PORT),$(ADDR))LAST_VALID_MEASURED_PRESS")
+    field(FTVL, "DOUBLE")
+    field(NELM, "40")
+}
+
 record(waveform, "$(P)$(R)DataLenHum-R") {
     field(DESC, "Hi: humidity")
     field(DTYP, "asynFloat64ArrayIn")
@@ -64,4 +112,14 @@ record(waveform, "$(P)$(R)DataLenHum-R") {
     field(INP,  "@asyn($(PORT),$(ADDR))MEASURED_HUM")
     field(FTVL, "DOUBLE")
     field(NELM, "40")
-}
\ No newline at end of file
+}
+
+record(waveform, "$(P)$(R)LstVldDataLenHum-R") {
+    field(DESC, "Last valid Hi: humidity")
+    field(DTYP, "asynFloat64ArrayIn")
+    field(EGU,  "%")
+    field(SCAN, "I/O Intr")
+    field(INP,  "@asyn($(PORT),$(ADDR))LAST_VALID_MEASURED_HUM")
+    field(FTVL, "DOUBLE")
+    field(NELM, "40")
+}
diff --git a/etalonMultiLine2App/Db/status.template b/etalonMultiLine2App/Db/status.template
index f9f31eb..e4182b9 100644
--- a/etalonMultiLine2App/Db/status.template
+++ b/etalonMultiLine2App/Db/status.template
@@ -54,7 +54,7 @@ record(waveform, "$(P)$(R)SelectedChannels-R") {
     field(DTYP, "asynInt32ArrayIn")
     field(INP,  "@asyn($(PORT),$(ADDR))SELECTED_CHANNELS")
     field(FTVL, "LONG")
-    field(NELM, 40)
+    field(NELM, "40")
     field(SCAN, "I/O Intr")
 }
 
@@ -119,7 +119,7 @@ record(waveform, "$(P)$(R)Gains-R") {
     field(DTYP, "asynInt32ArrayIn")
     field(FTVL, "LONG")
     field(INP,  "@asyn($(PORT),$(ADDR))GAINS")
-    field(NELM, 40)
+    field(NELM, "40")
     field(SCAN, "I/O Intr")
 }
 
@@ -128,7 +128,7 @@ record(waveform, "$(P)$(R)AlignDataMax-R") {
     field(DTYP, "asynFloat64ArrayIn")
     field(INP,  "@asyn($(PORT),$(ADDR))ALIGN_DATA_MAX")
     field(FTVL, "DOUBLE")
-    field(NELM, 40)
+    field(NELM, "40")
     field(SCAN, "I/O Intr")
 }
 
@@ -137,7 +137,7 @@ record(waveform, "$(P)$(R)AlignDataMin-R") {
     field(DTYP, "asynFloat64ArrayIn")
     field(INP,  "@asyn($(PORT),$(ADDR))ALIGN_DATA_MIN")
     field(FTVL, "DOUBLE")
-    field(NELM, 40)
+    field(NELM, "40")
     field(SCAN, "I/O Intr")
 }
 
@@ -180,10 +180,31 @@ record(mbbi, "$(P)$(R)CurrentTask-R") {
 }
 
 record(waveform, "$(P)$(R)MeasCounter-R") {
-    field(DESC, "Measure counter for each channel")
+    field(DESC, "Measure counter per channel")
     field(DTYP, "asynInt32ArrayIn")
     field(FTVL, "LONG")
     field(INP,  "@asyn($(PORT),$(ADDR))MEAS_COUNTER")
-    field(NELM, 40)
+    field(NELM, "40")
     field(SCAN, "I/O Intr")
-}
\ No newline at end of file
+    info(autosaveFields, "VAL")
+}
+
+record(waveform, "$(P)$(R)LstVldMeasCounter-R") {
+    field(DESC, "Last valid measure counter per ch")
+    field(DTYP, "asynInt32ArrayIn")
+    field(FTVL, "LONG")
+    field(INP,  "@asyn($(PORT),$(ADDR))LAST_VALID_MEAS_COUNTER")
+    field(NELM, "40")
+    field(SCAN, "I/O Intr")
+    info(autosaveFields, "VAL")
+}
+
+record(ai, "$(P)$(R)GlobalMeasCounter-R") {
+    field(DESC, "Get measurement counter")
+    field(PINI, "YES")
+    field(DTYP, "asynInt32")
+    field(INP,  "@asyn($(PORT),$(ADDR))GLOBAL_MEAS_COUNTER")
+    field(SCAN, "I/O Intr")
+    field(FLNK, "$(P)$(R)GlobalMeasCounter-S")
+    info(autosaveFields, "VAL")
+}
diff --git a/etalonMultiLine2App/src/etalonMultiLine2.cpp b/etalonMultiLine2App/src/etalonMultiLine2.cpp
index 1b7792b..c7951c5 100644
--- a/etalonMultiLine2App/src/etalonMultiLine2.cpp
+++ b/etalonMultiLine2App/src/etalonMultiLine2.cpp
@@ -1,17 +1,3 @@
-#define EPICS_THREAD_CAN_JOIN
-
-#include <iocsh.h>
-#include <epicsThread.h>
-#include <epicsEvent.h>
-#include <epicsExport.h>
-#include <asynPortDriver.h>
-#include <string.h>
-#include <epicsExit.h>
-
-#include <signal.h>
-
-#include "spdlog/spdlog.h"
-#include "asynOctetSyncIO.h"
 #include "etalonMultiLine2.h"
 
 static const char *driverName = "etalonMultiLine2";
@@ -83,13 +69,25 @@ EtalonMultiline2::EtalonMultiline2(const char *portName,
     createParam("ALIGN_DATA_MAX",               asynParamFloat64Array,  &AlignDataMax_);
     createParam("ALIGN_DATA_MIN",               asynParamFloat64Array,  &AlignDataMin_);
 
-    createParam("MEASURED_LENGTH",              asynParamFloat64Array,  &MeasuredLength_);
-    createParam("MEASURED_IMIN",                asynParamInt32Array,    &MeasuredImin_);
-    createParam("MEASURED_IMAX",                asynParamInt32Array,    &MeasuredImax_);
-    createParam("MEASURED_TEMP",                asynParamFloat64Array,  &MeasuredTemp_);
-    createParam("MEASURED_PRESS",               asynParamFloat64Array,  &MeasuredPress_);
-    createParam("MEASURED_HUM",                 asynParamFloat64Array,  &MeasuredHum_);
+    createParam("GLOBAL_MEAS_COUNTER",          asynParamInt32,        &GlobalMeasCounter_);
+
+    createParam("MEAS_COUNTER",                 asynParamInt32Array,   &MeasCounter_);
+    createParam("MEASURED_LENGTH",              asynParamFloat64Array, &MeasuredLength_);
+    createParam("MEASURED_IMIN",                asynParamInt32Array,   &MeasuredImin_);
+    createParam("MEASURED_IMAX",                asynParamInt32Array,   &MeasuredImax_);
+    createParam("MEASURED_TEMP",                asynParamFloat64Array, &MeasuredTemp_);
+    createParam("MEASURED_PRESS",               asynParamFloat64Array, &MeasuredPress_);
+    createParam("MEASURED_HUM",                 asynParamFloat64Array, &MeasuredHum_);
+
+    createParam("LAST_VALID_MEAS_COUNTER",      asynParamInt32Array,   &LastValidMeasCounter_);
+    createParam("LAST_VALID_MEASURED_LENGTH",   asynParamFloat64Array, &LastValidMeasuredLength_);
+    createParam("LAST_VALID_MEASURED_IMIN",     asynParamInt32Array,   &LastValidMeasuredImin_);
+    createParam("LAST_VALID_MEASURED_IMAX",     asynParamInt32Array,   &LastValidMeasuredImax_);
+    createParam("LAST_VALID_MEASURED_TEMP",     asynParamFloat64Array, &LastValidMeasuredTemp_);
+    createParam("LAST_VALID_MEASURED_PRESS",    asynParamFloat64Array, &LastValidMeasuredPress_);
+    createParam("LAST_VALID_MEASURED_HUM",      asynParamFloat64Array, &LastValidMeasuredHum_);
     
+    createParam("ERROR_MASK",                        asynParamInt32Array,  &ErrorMask_);
     createParam("MEASURED_ANALYSIS_ERROR",           asynParamInt32Array,  &MeasuredAnalysisError_);
     createParam("MEASURED_BEAM_INTERRUPTION",        asynParamInt32Array,  &MeasuredBeamInterruption_);
     createParam("MEASURED_TEMPERATURE_ERROR",        asynParamInt32Array,  &MeasuredTemperatureError_);
@@ -100,7 +98,6 @@ EtalonMultiline2::EtalonMultiline2(const char *portName,
     createParam("MEASURED_LASER_SPEED_ERROR",        asynParamInt32Array,  &MeasuredLaserSpeedError_);
     createParam("MEASURED_LASER_TEMPERATURE_ERROR",  asynParamInt32Array,  &MeasuredLaserTemperatureError_);
     createParam("MEASURED_DAQ_ERROR",                asynParamInt32Array,  &MeasuredDaqError_);
-    createParam("MEAS_COUNTER",                      asynParamInt32Array,  &MeasCounter_);
     
     /* Enable debug level */
     if(enableDebug)
@@ -511,156 +508,187 @@ asynStatus EtalonMultiline2::performMeasurement(asynStatus status)
         }
     }
     if(enabledChannelsCounter)
-    {
-    if(!status)
-        status = setIntegerParam(MeasState_, StateMeasuring);
-    if(!status)
-        status = callParamCallbacks();
-
-    status = serverQuery(query, reply, status);
-
-    if(strcmp(reply, "ERROR") == 0)
-    {
-        spdlog::error("Server replied error for the following request: {}", query);
-    
-        if(!status)
-            status = setIntegerParam(MeasState_, StateError);
-    
-        if(!status)
-            status = callParamCallbacks();
-    }
-    else if(strcmp(reply, "measurementfinished") == 0)
     {
         if(!status)
-            status = setIntegerParam(MeasState_, StateProcessing);
-        
+            status = setIntegerParam(MeasState_, StateMeasuring);
         if(!status)
             status = callParamCallbacks();
-        
-        if(!status)
-        {
-            size_t nbytesTransfered;
-            int eomReason;
-            status = pasynOctetSyncIO->read(pasynUserOctet_, 
-                                            reply, 1000*sizeof(char),
-                                            TIMEOUT,
-                                            &nbytesTransfered, &eomReason);
-            spdlog::debug("reply: {}", reply);
-        }
+
+        status = serverQuery(query, reply, status);
 
         if(strcmp(reply, "ERROR") == 0)
         {
-            spdlog::error("Error after processing. Query: {} Reply: {}. Did you set the preshot != 0?", query, reply);
-            
+            spdlog::error("Server replied error for the following request: {}", query);
+        
             if(!status)
                 status = setIntegerParam(MeasState_, StateError);
-            
+        
             if(!status)
                 status = callParamCallbacks();
         }
-        else
+        else if(strcmp(reply, "measurementfinished") == 0)
         {
-            double measuredLength[MAX_NUM_OF_CHANNELS];
-            int minimumIntensityRaw[MAX_NUM_OF_CHANNELS];
-            int maximumIntensityRaw[MAX_NUM_OF_CHANNELS];
-            double temperature[MAX_NUM_OF_CHANNELS];
-            double airPressure[MAX_NUM_OF_CHANNELS];
-            double humidity[MAX_NUM_OF_CHANNELS];
-            int analysisError[MAX_NUM_OF_CHANNELS];
-            int beamBreak[MAX_NUM_OF_CHANNELS];
-            int temperatureError[MAX_NUM_OF_CHANNELS];
-            int motionTolerance[MAX_NUM_OF_CHANNELS];
-            int intensityError[MAX_NUM_OF_CHANNELS];
-            int connectionErrorUSB[MAX_NUM_OF_CHANNELS];
-            int tcpServerError[MAX_NUM_OF_CHANNELS];
-            int errorSettingLaserSpeed[MAX_NUM_OF_CHANNELS];
-            int laserTemperatureError[MAX_NUM_OF_CHANNELS];
-            int daqErrorDuringDataAcq[MAX_NUM_OF_CHANNELS];
-            int tcpServerErrorFinal;
+            if(!status)
+                status = setIntegerParam(MeasState_, StateProcessing);
+            
+            if(!status)
+                status = callParamCallbacks();
+            
             if(!status)
             {
-                char * token;
-
-                token = strtok(reply, "_");
-                token = strtok(NULL, "_");
-
-                int channelNumber;
-
-                for(int i = 0; i < numSelectedChannels; i++)
-                {
-                    if(measChEna_[i])
-                    {
-                        sscanf(token, "%d,%lf,%d,%d,%lf,%lf,%lf,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", &channelNumber, 
-                                                                                                measuredLength + i,
-                                                                                                minimumIntensityRaw + i,
-                                                                                                maximumIntensityRaw + i,
-                                                                                                temperature + i,
-                                                                                                airPressure + i,
-                                                                                                humidity + i,
-                                                                                                analysisError + i,
-                                                                                                beamBreak + i,
-                                                                                                temperatureError + i,
-                                                                                                motionTolerance + i,
-                                                                                                intensityError + i,
-                                                                                                connectionErrorUSB + i,
-                                                                                                tcpServerError + i,
-                                                                                                errorSettingLaserSpeed + i,
-                                                                                                laserTemperatureError + i,
-                                                                                                daqErrorDuringDataAcq + i);
+                size_t nbytesTransfered;
+                int eomReason;
+                status = pasynOctetSyncIO->read(pasynUserOctet_, 
+                                                reply, 1000*sizeof(char),
+                                                TIMEOUT,
+                                                &nbytesTransfered, &eomReason);
+                spdlog::debug("reply: {}", reply);
+            }
 
-                        token = strtok(NULL, "_");
-                    }
-                }
-                sscanf(token, "%d", &tcpServerErrorFinal);
+            if(strcmp(reply, "ERROR") == 0)
+            {
+                spdlog::error("Error after processing. Query: {} Reply: {}. Did you set the preshot != 0?", query, reply);
+                
+                if(!status)
+                    status = setIntegerParam(MeasState_, StateError);
+                
+                if(!status)
+                    status = callParamCallbacks();
             }
-            if(!status)
-                status = doCallbacksFloat64Array(measuredLength, MAX_NUM_OF_CHANNELS, MeasuredLength_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(minimumIntensityRaw, MAX_NUM_OF_CHANNELS, MeasuredImin_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(maximumIntensityRaw, MAX_NUM_OF_CHANNELS, MeasuredImax_, 0);
-            if(!status)
-                status = doCallbacksFloat64Array(temperature, MAX_NUM_OF_CHANNELS, MeasuredTemp_, 0);
-            if(!status)
-                status = doCallbacksFloat64Array(airPressure, MAX_NUM_OF_CHANNELS, MeasuredPress_, 0);
-            if(!status)
-                status = doCallbacksFloat64Array(humidity, MAX_NUM_OF_CHANNELS, MeasuredHum_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(analysisError, MAX_NUM_OF_CHANNELS, MeasuredAnalysisError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(beamBreak, MAX_NUM_OF_CHANNELS, MeasuredBeamInterruption_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(temperatureError, MAX_NUM_OF_CHANNELS, MeasuredTemperatureError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(motionTolerance, MAX_NUM_OF_CHANNELS, MeasuredMovementToleranceError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(intensityError, MAX_NUM_OF_CHANNELS, MeasuredIntensityError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(connectionErrorUSB, MAX_NUM_OF_CHANNELS, MeasuredUsbConnectionError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(tcpServerError, MAX_NUM_OF_CHANNELS, MeasuredTcpServerError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(errorSettingLaserSpeed, MAX_NUM_OF_CHANNELS, MeasuredLaserSpeedError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(laserTemperatureError, MAX_NUM_OF_CHANNELS, MeasuredLaserTemperatureError_, 0);
-            if(!status)
-                status = doCallbacksInt32Array(daqErrorDuringDataAcq, MAX_NUM_OF_CHANNELS, MeasuredDaqError_, 0);
-            
-            for(int i = 0; i < numSelectedChannels; i++)
+            else
             {
-                if(measChEna_[i])
+                double measuredLength[MAX_NUM_OF_CHANNELS];
+                int minimumIntensityRaw[MAX_NUM_OF_CHANNELS];
+                int maximumIntensityRaw[MAX_NUM_OF_CHANNELS];
+                double temperature[MAX_NUM_OF_CHANNELS];
+                double airPressure[MAX_NUM_OF_CHANNELS];
+                double humidity[MAX_NUM_OF_CHANNELS];
+                int analysisError[MAX_NUM_OF_CHANNELS];
+                int beamBreak[MAX_NUM_OF_CHANNELS];
+                int temperatureError[MAX_NUM_OF_CHANNELS];
+                int motionToleranceError[MAX_NUM_OF_CHANNELS];
+                int intensityError[MAX_NUM_OF_CHANNELS];
+                int connectionErrorUSB[MAX_NUM_OF_CHANNELS];
+                int tcpServerError[MAX_NUM_OF_CHANNELS];
+                int errorSettingLaserSpeed[MAX_NUM_OF_CHANNELS];
+                int laserTemperatureError[MAX_NUM_OF_CHANNELS];
+                int daqErrorDuringDataAcq[MAX_NUM_OF_CHANNELS];
+                int tcpServerErrorFinal;
+                int errorResult[MAX_NUM_OF_CHANNELS];
+                if(!status)
                 {
+                    char * token;
+
+                    token = strtok(reply, "_");
+                    token = strtok(NULL, "_");
+
+                    int channelNumber;
+
+                    // Update valid measure ID
+                    int globalMeasCounter;
+                    if(!status)
+                        status = getIntegerParam(GlobalMeasCounter_, &globalMeasCounter);
+                    spdlog::debug("Global measure counter: {}", globalMeasCounter);
+                    globalMeasCounter++;
                     if(!status)
+                        status = setIntegerParam(GlobalMeasCounter_, globalMeasCounter);
+
+                    for(int i = 0; i < numSelectedChannels; i++)
                     {
-                        measCounter_[i]++;
-                        status = doCallbacksInt32Array(measCounter_, MAX_NUM_OF_CHANNELS, MeasCounter_, 0);
+                        if(measChEna_[i])
+                        {
+                            sscanf(token, "%d,%lf,%d,%d,%lf,%lf,%lf,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", &channelNumber, 
+                                                                                                    measuredLength + i,
+                                                                                                    minimumIntensityRaw + i,
+                                                                                                    maximumIntensityRaw + i,
+                                                                                                    temperature + i,
+                                                                                                    airPressure + i,
+                                                                                                    humidity + i,
+                                                                                                    analysisError + i,
+                                                                                                    beamBreak + i,
+                                                                                                    temperatureError + i,
+                                                                                                    motionToleranceError + i,
+                                                                                                    intensityError + i,
+                                                                                                    connectionErrorUSB + i,
+                                                                                                    tcpServerError + i,
+                                                                                                    errorSettingLaserSpeed + i,
+                                                                                                    laserTemperatureError + i,
+                                                                                                    daqErrorDuringDataAcq + i);
+                            token = strtok(NULL, "_");
+                            errorResult[i] = ((analysisError[i] << ANALYSIS_ERROR_BIT_POSITION) +
+                                                   (beamBreak[i] << BEAM_BREAK_BIT_POSITION) +
+                                                   (temperatureError[i] << TEMPERATURE_ERROR_BIT_POSITION) +
+                                                   (motionToleranceError[i] << MOTION_TOLERANCE_ERROR_BIT_POSITION) +
+                                                   (intensityError[i] << INTENSITY_ERROR_BIT_POSITION) +
+                                                   (connectionErrorUSB[i] << CONNECTION_ERROR_USB_BIT_POSITION) +
+                                                   (tcpServerError[i] << TCP_SERVER_ERROR_BIT_POSITION) +
+                                                   (errorSettingLaserSpeed[i] << ERROR_SETTING_LASER_SPEED_BIT_POSITION) +
+                                                   (laserTemperatureError[i] << LASER_TEMPERATURE_ERROR_BIT_POSITION) +
+                                                   (daqErrorDuringDataAcq[i] << DAQ_ERROR_DURING_DATA_ACQ_BIT_POSITION) + 
+                                                   (tcpServerErrorFinal << TCP_SERVER_ERROR_FINAL_BIT_POSITION)) & errorMask_[i];
+                            spdlog::debug("Channel: {} Error mask: 0x{:X} Error result: 0x{:X}", channelList_[i], errorMask_[i], errorResult[i]);
+                            // Update last valid measurement if no errors
+                            if(errorResult[i] == 0)
+                            {
+                                if(!status)
+                                    status = doCallbacksFloat64Array(measuredLength, MAX_NUM_OF_CHANNELS, LastValidMeasuredLength_, 0);
+                                if(!status)
+                                    status = doCallbacksInt32Array(minimumIntensityRaw, MAX_NUM_OF_CHANNELS, LastValidMeasuredImin_, 0);
+                                if(!status)
+                                    status = doCallbacksInt32Array(maximumIntensityRaw, MAX_NUM_OF_CHANNELS, LastValidMeasuredImax_, 0);
+                                if(!status)
+                                    status = doCallbacksFloat64Array(temperature, MAX_NUM_OF_CHANNELS, LastValidMeasuredTemp_, 0);
+                                if(!status)
+                                    status = doCallbacksFloat64Array(airPressure, MAX_NUM_OF_CHANNELS, LastValidMeasuredPress_, 0);
+                                if(!status)
+                                    status = doCallbacksFloat64Array(humidity, MAX_NUM_OF_CHANNELS, LastValidMeasuredHum_, 0);
+                                // Update valid measurement counter per channel
+                                lastValidMeasCounter_[i] = globalMeasCounter;
+                            }
+                            measCounter_[i] = globalMeasCounter;
+                        }
                     }
+                    status = doCallbacksInt32Array(lastValidMeasCounter_, MAX_NUM_OF_CHANNELS, LastValidMeasCounter_, 0);
+                    status = doCallbacksInt32Array(measCounter_, MAX_NUM_OF_CHANNELS, MeasCounter_, 0);
+                    sscanf(token, "%d", &tcpServerErrorFinal);
                 }
-            }
+                // Update current measurement 
+                if(!status)
+                    status = doCallbacksFloat64Array(measuredLength, MAX_NUM_OF_CHANNELS, MeasuredLength_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(minimumIntensityRaw, MAX_NUM_OF_CHANNELS, MeasuredImin_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(maximumIntensityRaw, MAX_NUM_OF_CHANNELS, MeasuredImax_, 0);
+                if(!status)
+                    status = doCallbacksFloat64Array(temperature, MAX_NUM_OF_CHANNELS, MeasuredTemp_, 0);
+                if(!status)
+                    status = doCallbacksFloat64Array(airPressure, MAX_NUM_OF_CHANNELS, MeasuredPress_, 0);
+                if(!status)
+                    status = doCallbacksFloat64Array(humidity, MAX_NUM_OF_CHANNELS, MeasuredHum_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(analysisError, MAX_NUM_OF_CHANNELS, MeasuredAnalysisError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(beamBreak, MAX_NUM_OF_CHANNELS, MeasuredBeamInterruption_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(temperatureError, MAX_NUM_OF_CHANNELS, MeasuredTemperatureError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(motionToleranceError, MAX_NUM_OF_CHANNELS, MeasuredMovementToleranceError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(intensityError, MAX_NUM_OF_CHANNELS, MeasuredIntensityError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(connectionErrorUSB, MAX_NUM_OF_CHANNELS, MeasuredUsbConnectionError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(tcpServerError, MAX_NUM_OF_CHANNELS, MeasuredTcpServerError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(errorSettingLaserSpeed, MAX_NUM_OF_CHANNELS, MeasuredLaserSpeedError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(laserTemperatureError, MAX_NUM_OF_CHANNELS, MeasuredLaserTemperatureError_, 0);
+                if(!status)
+                    status = doCallbacksInt32Array(daqErrorDuringDataAcq, MAX_NUM_OF_CHANNELS, MeasuredDaqError_, 0);
 
-            if(!status)
-                status = setIntegerParam(MeasState_, StateIdle);
-        }
+                if(!status)
+                    status = setIntegerParam(MeasState_, StateIdle);
+            }
         }
     }
     else
@@ -788,7 +816,7 @@ asynStatus EtalonMultiline2::writeInt32(asynUser *pasynUser,
         else
             pollingEnable_ = 0;
     }
-    if(function == AlignmentStart_)
+    else if(function == AlignmentStart_)
     {
         if(value)
         {
@@ -801,8 +829,12 @@ asynStatus EtalonMultiline2::writeInt32(asynUser *pasynUser,
             if(!status)
                 status = setIntegerParam(AlignmentStart_, 0);
     }
+    else
+        status = asynPortDriver::writeInt32(pasynUser, value);
+
     if(!status)
         status = callParamCallbacks();
+
     return status;
 }
 
@@ -817,6 +849,12 @@ asynStatus EtalonMultiline2::readInt32Array(asynUser *pasynUser,
     {
         memcpy(measChEna_, value, MAX_NUM_OF_CHANNELS*sizeof(int));
     }
+    else if(function == ErrorMask_)
+    {
+        memcpy(errorMask_, value, MAX_NUM_OF_CHANNELS*sizeof(int));
+    }
+    else
+        status = asynPortDriver::readInt32Array(pasynUser, value, nElements, nIn);
     *nIn = nElements;
     return status;
 }
diff --git a/etalonMultiLine2App/src/etalonMultiLine2.h b/etalonMultiLine2App/src/etalonMultiLine2.h
index 6e31063..bcb91d8 100644
--- a/etalonMultiLine2App/src/etalonMultiLine2.h
+++ b/etalonMultiLine2App/src/etalonMultiLine2.h
@@ -1,3 +1,18 @@
+#include <iocsh.h>
+#include <epicsThread.h>
+#include <epicsEvent.h>
+#include <epicsExport.h>
+#include <string.h>
+#include <epicsExit.h>
+
+#include <signal.h>
+#include "spdlog/spdlog.h"
+
+#include <asynPortDriver.h>
+#include <asynOctetSyncIO.h>
+
+#define EPICS_THREAD_CAN_JOIN
+
 #define MAX_NUM_OF_CHANNELS 40
 #define MAX_QUERY_SIZE 1000
 #define MAX_REPLY_SIZE 1000
@@ -6,6 +21,18 @@
 #define CHANNEL_SEPARATOR               "_"
 #define VALUE_WITHIN_CHANNEL_SEPARATOR  ","
 
+#define ANALYSIS_ERROR_BIT_POSITION             0
+#define BEAM_BREAK_BIT_POSITION                 1
+#define TEMPERATURE_ERROR_BIT_POSITION          2
+#define MOTION_TOLERANCE_ERROR_BIT_POSITION     3
+#define INTENSITY_ERROR_BIT_POSITION            4
+#define CONNECTION_ERROR_USB_BIT_POSITION       5
+#define TCP_SERVER_ERROR_BIT_POSITION           6
+#define ERROR_SETTING_LASER_SPEED_BIT_POSITION  7
+#define LASER_TEMPERATURE_ERROR_BIT_POSITION    8
+#define DAQ_ERROR_DURING_DATA_ACQ_BIT_POSITION  9
+#define TCP_SERVER_ERROR_FINAL_BIT_POSITION     10
+
 class EtalonMultiline2 : public asynPortDriver
 {
 
@@ -56,6 +83,9 @@ protected:
     int AlignDataMax_;
     int AlignDataMin_;
 
+    int GlobalMeasCounter_;
+
+    int MeasCounter_;
     int MeasuredLength_;
     int MeasuredImin_;
     int MeasuredImax_;
@@ -63,6 +93,15 @@ protected:
     int MeasuredPress_;
     int MeasuredHum_;
 
+    int LastValidMeasCounter_;
+    int LastValidMeasuredLength_;
+    int LastValidMeasuredImin_;
+    int LastValidMeasuredImax_;
+    int LastValidMeasuredTemp_;
+    int LastValidMeasuredPress_;
+    int LastValidMeasuredHum_;
+
+    int ErrorMask_;
     int MeasuredAnalysisError_;
     int MeasuredBeamInterruption_;
     int MeasuredTemperatureError_;
@@ -73,7 +112,6 @@ protected:
     int MeasuredLaserSpeedError_;
     int MeasuredLaserTemperatureError_;
     int MeasuredDaqError_;
-    int MeasCounter_;
 
 private:
     asynUser  *pasynUserOctet_;
@@ -92,11 +130,13 @@ private:
     epicsInt32 gains_[MAX_NUM_OF_CHANNELS];
     epicsInt32 measChEna_[MAX_NUM_OF_CHANNELS];
     epicsFloat64 measPreshot_[MAX_NUM_OF_CHANNELS];
+    epicsInt32 errorMask_[MAX_NUM_OF_CHANNELS];
+    epicsInt32 measCounter_[MAX_NUM_OF_CHANNELS];
+    epicsInt32 lastValidMeasCounter_[MAX_NUM_OF_CHANNELS];
 
     epicsInt32 pollingEnable_;
     epicsInt32 initialPolling_;
-
-    epicsInt32 measCounter_[MAX_NUM_OF_CHANNELS];
+    epicsInt64 measureCurrentID_;
 
     enum MeasurementState
     {
-- 
GitLab


From f318a4ab5e16f9d05e5b7b8e5d7475b02c3470c0 Mon Sep 17 00:00:00 2001
From: George Kontogiorgos <george.kontogiorgos@ess.eu>
Date: Wed, 27 Sep 2023 16:52:03 +0200
Subject: [PATCH 5/5] Add missing PV for general server error

---
 etalonMultiLine2App/Db/data.template         | 2 +-
 etalonMultiLine2App/Db/error.template        | 9 +++++++++
 etalonMultiLine2App/src/etalonMultiLine2.cpp | 5 +++++
 etalonMultiLine2App/src/etalonMultiLine2.h   | 1 +
 4 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/etalonMultiLine2App/Db/data.template b/etalonMultiLine2App/Db/data.template
index 2b21c26..104cceb 100644
--- a/etalonMultiLine2App/Db/data.template
+++ b/etalonMultiLine2App/Db/data.template
@@ -18,7 +18,7 @@ record(waveform, "$(P)$(R)DataLenLength-R") {
     field(NELM, "40")
 }
 
-record(waveform, "$(P)$(R)LstValDataLenLength-R") {
+record(waveform, "$(P)$(R)LstVldDataLenLength-R") {
     field(DESC, "Last valid Li: measured length")
     field(DTYP, "asynFloat64ArrayIn")
     field(EGU,  "mm")
diff --git a/etalonMultiLine2App/Db/error.template b/etalonMultiLine2App/Db/error.template
index 85eb9f6..793e06b 100644
--- a/etalonMultiLine2App/Db/error.template
+++ b/etalonMultiLine2App/Db/error.template
@@ -101,3 +101,12 @@ record(waveform, "$(P)$(R)DataLenDAQErr-R") {
     field(FTVL, "LONG")
     field(NELM, "40")
 }
+
+record(bi, "$(P)$(R)TcpFinalServerErr-R") {
+    field(DESC, "SE: TCP server error")
+    field(DTYP, "asynInt32")
+    field(ZNAM, "False")
+    field(ONAM, "True")
+    field(INP,  "@asyn($(PORT),$(ADDR))TCP_FINAL_SERVER_ERROR")
+    field(SCAN, "I/O Intr")
+}
diff --git a/etalonMultiLine2App/src/etalonMultiLine2.cpp b/etalonMultiLine2App/src/etalonMultiLine2.cpp
index c7951c5..21ecad8 100644
--- a/etalonMultiLine2App/src/etalonMultiLine2.cpp
+++ b/etalonMultiLine2App/src/etalonMultiLine2.cpp
@@ -98,6 +98,7 @@ EtalonMultiline2::EtalonMultiline2(const char *portName,
     createParam("MEASURED_LASER_SPEED_ERROR",        asynParamInt32Array,  &MeasuredLaserSpeedError_);
     createParam("MEASURED_LASER_TEMPERATURE_ERROR",  asynParamInt32Array,  &MeasuredLaserTemperatureError_);
     createParam("MEASURED_DAQ_ERROR",                asynParamInt32Array,  &MeasuredDaqError_);
+    createParam("TCP_FINAL_SERVER_ERROR",            asynParamInt32,  &TcpFinalServerError_);
     
     /* Enable debug level */
     if(enableDebug)
@@ -651,6 +652,10 @@ asynStatus EtalonMultiline2::performMeasurement(asynStatus status)
                     status = doCallbacksInt32Array(lastValidMeasCounter_, MAX_NUM_OF_CHANNELS, LastValidMeasCounter_, 0);
                     status = doCallbacksInt32Array(measCounter_, MAX_NUM_OF_CHANNELS, MeasCounter_, 0);
                     sscanf(token, "%d", &tcpServerErrorFinal);
+                    if(!status)
+                        status = setIntegerParam(TcpFinalServerError_, tcpServerErrorFinal);
+                    if(!status)
+                        status = callParamCallbacks();
                 }
                 // Update current measurement 
                 if(!status)
diff --git a/etalonMultiLine2App/src/etalonMultiLine2.h b/etalonMultiLine2App/src/etalonMultiLine2.h
index bcb91d8..c12468e 100644
--- a/etalonMultiLine2App/src/etalonMultiLine2.h
+++ b/etalonMultiLine2App/src/etalonMultiLine2.h
@@ -112,6 +112,7 @@ protected:
     int MeasuredLaserSpeedError_;
     int MeasuredLaserTemperatureError_;
     int MeasuredDaqError_;
+    int TcpFinalServerError_;
 
 private:
     asynUser  *pasynUserOctet_;
-- 
GitLab