This is an automated email from the ASF dual-hosted git repository.

cgarcia pushed a commit to branch feature/merlot
in repository https://gitbox.apache.org/repos/asf/plc4x-extras.git


The following commit(s) were added to refs/heads/feature/merlot by this push:
     new a3dd723  Conversion of VType to EPICSevent types for serialization.
a3dd723 is described below

commit a3dd72324183509e0873173a390528ff82034609
Author: César García <[email protected]>
AuthorDate: Mon Apr 6 11:26:48 2026 -0400

    Conversion of VType to EPICSevent types for serialization.
---
 .../org.apache.plc4x.merlot.archiver/pom.xml       |  38 ++++-
 .../random_data.pbraw                              | Bin 0 -> 252 bytes
 .../plc4x/merlot/archiver/api/MerlotHtc.java       |  34 ++--
 .../merlot/archiver/command/MerlotHtcCommand.java  |  96 ++++++++---
 .../archiver/core/MerlotPBRawSerializer.java       | 117 +++++++++++++
 .../merlot/archiver/core/MerlotPayloadMapping.java | 188 +++++++++++++++++++++
 .../merlot/archiver/core/MerlotTypeMapping.java    |  77 +++++++++
 .../impl/MerlotDataBrowserRTGetDataPVImpl.java     |  25 ++-
 .../merlot/archiver/impl/MerlotHtcIoTDBImpl.java   |   2 +-
 .../merlot/archiver/impl/MerlotHtcRTImpl.java      |  93 +++++-----
 .../archiver/test/MerlotPayloadMappingTest.java    |  94 +++++++++++
 .../archiver/test/MerlotTypeMappingTest.java       | 123 ++++++++++++++
 .../merlot/archiver/test/PBRawFileSerializer.java  | 135 +++++++++++++++
 .../merlot/org.apache.plc4x.merlot.das.api/pom.xml |   5 +
 14 files changed, 938 insertions(+), 89 deletions(-)

diff --git a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
index fc27612..6755351 100644
--- a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
+++ b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/pom.xml
@@ -104,7 +104,14 @@
                     </execution>
                 </executions>
             </plugin>          
-            
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>3.5.1</version>
+                <configuration>
+                    
<mainClass>org.apache.plc4x.merlot.archiver.test.NewMain</mainClass>
+                </configuration>
+            </plugin>            
         </plugins>
     </build>
     <dependencies>
@@ -206,5 +213,34 @@
             <artifactId>org.apache.plc4x.merlot.api</artifactId>
             <version>0.13.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>4.34.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>5.10.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>5.10.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>5.10.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opentest4j</groupId>
+            <artifactId>opentest4j</artifactId>
+            <version>1.3.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/random_data.pbraw 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/random_data.pbraw
new file mode 100644
index 0000000..294b85d
Binary files /dev/null and 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/random_data.pbraw differ
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
index 66411ae..3c52460 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/api/MerlotHtc.java
@@ -27,42 +27,52 @@ import org.epics.vtype.VType;
 public interface MerlotHtc {
     
     /*
-    *
+    * Bundle Initialization
     */    
     public void init();
         
     /*
-    *
+    * Bundle Destruction
     */    
     public void destroy();        
         
     /*
-    *
+    * Identify the Bundle's internal ID, required for remote commands 
+    * and functions.
     */    
     public String getID();      
     
     
     /*
-    *
+    * Add a tag to the historian. Only tags using the PVA protocol 
+    * are supported.
+    * @param strPV
+    * @param maxRate 
     */
-    void addPV(String strPV, Double interval);
+    void addPV(String strPV, Double maxRate);
     
     /*
-    *
+    * Removes a tag from the historian. Intended for use only from 
+    * the command line.
+    * @param strPV
     */
     void removePV(String strPV);
     
-    
-    
-    
     /*
-    *
+    * A set of tags currently being processed by the historian.
     */
     Set<String> getPVs();
     
     /*
-    *
+    * It returns the values ​​stored in the historian as pairs of
+    * LocalDateTime and VType (value) pairs, for subsequent processing 
+    * according to the format required by the client application—for example, 
+    * PBRAW, JSON, or XML.
+    * @param strPV
+    * @param from
+    * @param to  
+    * @return A List of Pair 
     */
-    List<Pair<LocalDateTime, VType>> getPVs(String strPV, String init, String 
end);
+    List<VType> getPVs(String strPV, String init, String end);
     
 }
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
index 5b26b97..37b529f 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/command/MerlotHtcCommand.java
@@ -17,11 +17,13 @@
 package org.apache.plc4x.merlot.archiver.command;
 
 
+import java.time.Instant;
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
@@ -45,19 +47,47 @@ public class MerlotHtcCommand   implements Action {
     String strHtc; 
 
     @Option(name = "-p", aliases = "--pv", description = "Displays the stored 
values ​​of the process variable.", required = false, multiValued = false)
-    String strPV;    
+    String strPV;
+
+    @Option(name = "-a", aliases = "--add", description = "The designated PV 
tags must be incorporated into the collector.", required = false, multiValued = 
false)
+    Boolean blnAdd;  
+    
+    @Option(name = "-f", aliases = "--from", description = "Start date for 
data query.", required = false, multiValued = false)
+    String from;      
+    
+    @Option(name = "-t", aliases = "--to", description = "End date for data 
consultation.", required = false, multiValued = false)
+    String to;  
+    
+    @Option(name = "-r", aliases = "--remove", description = "The designated 
PV tags must be remove from the collector.", required = false, multiValued = 
false)
+    String strRemovePV;       
+        
+    
+    @Argument(index = 0, name = "htc", description = "The Collector to which 
the PV tag will be assigned.", required = false, multiValued = false)
+    String strMainHtc;   
+
+    @Argument(index = 1, name = "rate", description = "Maximum rate at which a 
tag change must be acquired.", required = false, multiValued = false)
+    String strMaxRate;      
+    
+    @Argument(index = 2, name = "pvs", description = "List of PVS tags to 
htc.", required = false, multiValued = true)
+    List<String> strPVs;    
+    
+    
     
     @Override
     public Object execute() throws Exception {
-        if (null == strHtc){
+                
+        if ((null == strHtc) && (null == strMainHtc) ){
             ListCollectorsServices(null);
-        } else {
-            if (null == strPV) {
+        } else  if ((null != strHtc) && (null == strMainHtc) && (null == 
strPV)) {
             ListCollectorsServices(strHtc);
-            } else {
-                ListHistoricalValues(strPV, null, null);
-            }
-        }
+        } else  if ((null != strHtc) && (null == strMainHtc) && (null != 
strPV)){
+                ListHistoricalValues(strPV, from, to);
+         } else  if ((blnAdd) && (null != strMainHtc) && (null != strMaxRate) 
&& (null != strPVs)){
+            addPV(strMainHtc, strMaxRate, strPVs);
+         } else if ((null != strRemovePV) && (null != strMainHtc)) {
+             removePV(strMainHtc, strRemovePV);
+         }
+        
         return null;
     }
     
@@ -73,7 +103,7 @@ public class MerlotHtcCommand   implements Action {
             
         } else {
             htcs.forEach(h -> {
-                if(h.getID().equalsIgnoreCase(strHtc)){
+                if(h.getID().equalsIgnoreCase(id)){
                     int count = 1;
                     ShellTable table = new ShellTable();
                     table.column("Item");
@@ -88,22 +118,44 @@ public class MerlotHtcCommand   implements Action {
         }
     }
     
-    private void ListHistoricalValues(String pv, String ini, String end){    
+    private void ListHistoricalValues(String pv, String init, String end){  
+        int[] counter = new int[1];
+        htcs.forEach(h -> {
+            if(h.getID().equalsIgnoreCase(strHtc)){
+                String strInit = (null == init)?Instant.MIN.toString():init;
+                String strEnd  = (null == end)?Instant.MAX.toString():end;     
                
+
+                var pvs = h.getPVs(pv, strInit, strEnd);
+                ShellTable table = new ShellTable();
+                table.column("Date");
+                table.column("Value");  
+                counter[0] = 1;
+                pvs.forEach((p) ->{
+                    table.addRow().addContent(counter[0], p.toString());       
                 
+                    counter[0]++;
+                });
+                table.print(System.out); 
+            }
+        });       
+    }
+    
+    private void addPV(String pv, String maxRate, List<String> listPVs) {
             htcs.forEach(h -> {
-                if(h.getID().equalsIgnoreCase(strHtc)){
-                    System.out.println(LocalDateTime.MIN.toString());
-                    System.out.println(LocalDateTime.MAX.toString());          
          
-                    var pvs = h.getPVs(pv, LocalDateTime.MIN.toString(), 
LocalDateTime.MAX.toString());
-                    ShellTable table = new ShellTable();
-                    table.column("Date");
-                    table.column("Value");  
-                    pvs.forEach((p) ->{
-                        table.addRow().addContent(p.getLeft(), p.getRight());  
                      
+                if(h.getID().equalsIgnoreCase(strMainHtc)){
+                    listPVs.forEach(s ->{
+                        h.addPV(s, Double.MAX_VALUE);                        
                     });
-                    table.print(System.out); 
-                }
-            });       
+                }   
+            });
     }
+    
+    private void removePV(String htc, String strpv) {
+            htcs.forEach(h -> {
+                if(h.getID().equalsIgnoreCase(htc)){
+                    h.removePV(strpv);
+                }   
+            });
+    }    
  
     
 }
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPBRawSerializer.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPBRawSerializer.java
new file mode 100644
index 0000000..9e0139d
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPBRawSerializer.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.core;
+
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.epics.vtype.VType;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * Clase utilitaria para la generación de archivos en formato PBRAW compatibles
+ * con el EPICS Archiver Appliance.
+ */
+public final class MerlotPBRawSerializer {
+
+    // Constructor privado para evitar instanciación de clase utilitaria
+    private MerlotPBRawSerializer() {}
+
+    /**
+     * Serializa una lista de eventos VType a un archivo en formato .pbraw. 
+     *
+     * @param events  Lista de eventos provenientes de la red de control.
+     * @param pvName  Nombre de la Variable de Proceso (PV).
+     * @param fileName Nombre del archivo de salida.
+     * @throws IOException Si ocurre un error durante la escritura.
+     */
+    public static void serializeToPBRaw(List<VType> events, String pvName, 
OutputStream out) throws IOException {
+            
+        if (events == null || events.isEmpty()) {
+            return;
+        }
+
+        // 1. Escribir el PayloadInfo (Metadatos obligatorios para 
pbrawclient) 
+        // Se asume el tipo basado en el primer elemento de la lista
+        MerlotPayloadMapping mapping = 
MerlotPayloadMapping.fromVType(events.get(0));
+        if (mapping == null) {
+            throw new IOException("Tipo de VType no soportado para 
serialización.");
+        }
+
+        EPICSEvent.PayloadInfo info = EPICSEvent.PayloadInfo.newBuilder()
+                .setPvname(pvName)
+                .setType(MerlotTypeMapping.fromVType(events.get(0)))
+                .setElementCount(1)
+                .build();
+
+        info.writeDelimitedTo(out);
+
+        // 2. Serializar cada evento VType usando la factoría 
MerlotPayloadMapping 
+        for (VType vType : events) {
+            Object pbEvent = MerlotPayloadMapping.createEvent(vType);
+
+            if (pbEvent != null) {
+                writeEventToStream(out, pbEvent);
+            }
+        }
+    }
+
+    /**
+     * Escribe el objeto de Protocol Buffers en el stream usando formato 
delimitado. 
+     */
+    private static void writeEventToStream(OutputStream out, Object pbEvent) 
throws IOException {
+        if (pbEvent instanceof com.google.protobuf.MessageLite) {
+            ((com.google.protobuf.MessageLite) pbEvent).writeDelimitedTo(out);
+        } else {
+            // Manejo manual de varint si no es un mensaje directo de Protobuf
+            byte[] bytes = serializeToBytes(pbEvent);
+            if (bytes != null) {
+                writeVarint32(out, bytes.length);
+                out.write(bytes);
+            }
+        }
+    }
+
+    /**
+     * Utilidad para escribir el prefijo de tamaño (Varint32) requerido por el 
protocolo. 
+     */
+    private static void writeVarint32(OutputStream out, int value) throws 
IOException {
+        while (true) {
+            if ((value & ~0x7F) == 0) {
+                out.write(value);
+                return;
+            } else {
+                out.write((value & 0x7F) | 0x80);
+                value >>>= 7;
+            }
+        }
+    }
+
+    /**
+     * Convierte el objeto del evento en su representación de bytes. 
+     */
+    private static byte[] serializeToBytes(Object pbEvent) {
+        if (pbEvent instanceof EPICSEvent.ScalarDouble) {
+            return ((EPICSEvent.ScalarDouble) pbEvent).toByteArray();
+        } else if (pbEvent instanceof EPICSEvent.ScalarInt) {
+            return ((EPICSEvent.ScalarInt) pbEvent).toByteArray();
+        }
+        // Añadir otros tipos según sea necesario
+        return null;
+    }
+}
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPayloadMapping.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPayloadMapping.java
new file mode 100644
index 0000000..6d0b852
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotPayloadMapping.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.core;
+
+
+import org.epics.vtype.*;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.function.Function;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadType;
+import org.epics.util.array.IteratorDouble;
+
+/**
+ * Mapeador de tipos entre la jerarquía VType de EPICS y los mensajes Protobuf 
+ * de la arquitectura del Archiver Appliance.
+ */
+public enum MerlotPayloadMapping {
+    // Escalares
+    SCALAR_DOUBLE(PayloadType.SCALAR_DOUBLE, VDouble.class),
+    SCALAR_FLOAT(PayloadType.SCALAR_FLOAT, VFloat.class),
+    SCALAR_INT(PayloadType.SCALAR_INT, VInt.class),
+    SCALAR_SHORT(PayloadType.SCALAR_SHORT, VShort.class),
+    SCALAR_BYTE(PayloadType.SCALAR_BYTE, VByte.class),
+    SCALAR_STRING(PayloadType.SCALAR_STRING, VString.class),
+    SCALAR_ENUM(PayloadType.SCALAR_ENUM, VEnum.class),
+    
+    // Vectores (Waveforms)
+    VECTOR_DOUBLE(PayloadType.WAVEFORM_DOUBLE, VDoubleArray.class),
+    VECTOR_FLOAT(PayloadType.WAVEFORM_FLOAT, VFloatArray.class),
+    VECTOR_INT(PayloadType.WAVEFORM_INT, VIntArray.class),
+    VECTOR_SHORT(PayloadType.WAVEFORM_SHORT, VShortArray.class),
+    VECTOR_BYTE(PayloadType.WAVEFORM_BYTE, VByteArray.class);
+
+    private final PayloadType protoType;
+    private final Class<? extends VType> vTypeClass;
+
+    private static final EnumMap<MerlotPayloadMapping, Function<VType, 
Object>> CONSTRUCTORS = new EnumMap<>(MerlotPayloadMapping.class);
+
+    static {
+        // Registro de factorías para Escalares
+        CONSTRUCTORS.put(SCALAR_DOUBLE, (v) -> buildScalarDouble((VDouble) v));
+        CONSTRUCTORS.put(SCALAR_FLOAT,  (v) -> buildScalarFloat((VFloat) v));
+        CONSTRUCTORS.put(SCALAR_INT,    (v) -> buildScalarInt((VInt) v));
+        CONSTRUCTORS.put(SCALAR_SHORT,  (v) -> buildScalarShort((VShort) v));
+        CONSTRUCTORS.put(SCALAR_BYTE,   (v) -> buildScalarByte((VByte) v));
+        CONSTRUCTORS.put(SCALAR_STRING, (v) -> buildScalarString((VString) v));
+        CONSTRUCTORS.put(SCALAR_ENUM,   (v) -> buildScalarEnum((VEnum) v));
+
+        // Registro de factorías para Vectores
+        CONSTRUCTORS.put(VECTOR_DOUBLE, (v) -> 
buildVectorDouble((VDoubleArray) v));
+        CONSTRUCTORS.put(VECTOR_FLOAT,  (v) -> buildVectorFloat((VFloatArray) 
v));
+        CONSTRUCTORS.put(VECTOR_INT,    (v) -> buildVectorInt((VIntArray) v));
+        CONSTRUCTORS.put(VECTOR_SHORT,  (v) -> buildVectorShort((VShortArray) 
v));
+        CONSTRUCTORS.put(VECTOR_BYTE,   (v) -> buildVectorByte((VByteArray) 
v));
+    }
+
+    MerlotPayloadMapping(PayloadType protoType, Class<? extends VType> 
vTypeClass) {
+        this.protoType = protoType;
+        this.vTypeClass = vTypeClass;
+    }
+
+    /**
+     * Punto de entrada principal para crear un evento serializable a partir 
de un VType.
+     */
+    public static Object createEvent(VType vtype) {
+        MerlotPayloadMapping mapping = fromVType(vtype);
+        if (mapping != null && CONSTRUCTORS.containsKey(mapping)) {
+            return CONSTRUCTORS.get(mapping).apply(vtype);
+        }
+        return null;
+    }
+
+    public static MerlotPayloadMapping fromVType(VType v) {
+        for (MerlotPayloadMapping m : values()) {
+            if (m.vTypeClass.isInstance(v)) return m;
+        }
+        return null;
+    }
+
+    // --- Implementación de Builders Escalares ---
+
+    private static EPICSEvent.ScalarDouble buildScalarDouble(VDouble v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+        return EPICSEvent.ScalarDouble.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+                .setSeverity(v.getAlarm().getStatus().ordinal())
+                .setStatus(v.getAlarm().getStatus().ordinal()).build();
+    }
+
+    private static EPICSEvent.ScalarFloat buildScalarFloat(VFloat v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+        return EPICSEvent.ScalarFloat.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+                .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+    }
+
+    private static EPICSEvent.ScalarInt buildScalarInt(VInt v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+        return EPICSEvent.ScalarInt.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+                .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+    }
+    
+    private static EPICSEvent.ScalarShort buildScalarShort(VShort v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());        
+        return EPICSEvent.ScalarShort.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+                .setSeverity(v.getAlarm().getStatus().ordinal()).build();  
+        }   
+    
+    private static EPICSEvent.ScalarString buildScalarString(VString v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+        return EPICSEvent.ScalarString.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getValue())
+                .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+    }
+
+    private static EPICSEvent.ScalarEnum buildScalarEnum(VEnum v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+        return EPICSEvent.ScalarEnum.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).setVal(v.getIndex())
+                .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+    }
+
+    // --- Implementación de Builders de Vectores (Waveforms) ---
+
+    private static EPICSEvent.VectorDouble buildVectorDouble(VDoubleArray v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+        double[] values = v.getData().toArray(new double[0]);
+        List<Double> iterableValues = Arrays.stream(values)
+                                  .boxed()
+                                  .toList();
+        return EPICSEvent.VectorDouble.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).addAllVal(iterableValues)
+                .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+    }
+
+    private static EPICSEvent.VectorInt buildVectorInt(VIntArray v) {
+        TimeInfo ti = getTimeInfo(v.getTime().getTimestamp());
+        Integer[] values = v.getData().toArray(new Integer[0]);
+        Iterable<Integer> iterableValues = Arrays.asList(values);        
+        return EPICSEvent.VectorInt.newBuilder()
+                
.setSecondsintoyear(ti.sec).setNano(ti.nano).addAllVal(iterableValues)
+                .setSeverity(v.getAlarm().getStatus().ordinal()).build();
+    }
+
+    // Métodos abreviados para otros tipos numéricos
+    
+    private static EPICSEvent.ScalarByte buildScalarByte(VByte v) { return 
null;}
+    
+    private static Object buildVectorFloat(VFloatArray v) { return null; }
+    
+    private static Object buildVectorShort(VShortArray v) { return null; }
+    
+    private static Object buildVectorByte(VByteArray v) { return null; }
+
+    // --- Utilidad de Tiempo ---
+
+    private static class TimeInfo { int sec; int nano; }
+
+    private static TimeInfo getTimeInfo(Instant ts) {
+        ZonedDateTime zdt = ZonedDateTime.ofInstant(ts, ZoneId.of("UTC"));
+        long yearStart = ZonedDateTime.of(zdt.getYear(), 1, 1, 0, 0, 0, 0, 
ZoneId.of("UTC")).toEpochSecond();
+        TimeInfo ti = new TimeInfo();
+        ti.sec = (int) (ts.getEpochSecond() - yearStart);
+        ti.nano = ts.getNano();
+        return ti;
+    }
+}
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotTypeMapping.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotTypeMapping.java
new file mode 100644
index 0000000..2a67646
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/core/MerlotTypeMapping.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.core;
+
+
+import org.epics.vtype.*;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadType;
+
+public enum MerlotTypeMapping {
+    SCALAR_FLOAT(PayloadType.SCALAR_FLOAT, VFloat.class),    
+    SCALAR_DOUBLE(PayloadType.SCALAR_DOUBLE, VDouble.class),
+    SCALAR_INT(PayloadType.SCALAR_INT, VInt.class),
+    SCALAR_STRING(PayloadType.SCALAR_STRING, VString.class),
+    SCALAR_ENUM(PayloadType.SCALAR_ENUM, VEnum.class),
+    VECTOR_DOUBLE(PayloadType.WAVEFORM_DOUBLE, VDoubleArray.class),
+    VECTOR_INT(PayloadType.WAVEFORM_INT, VIntArray.class);
+
+    private final PayloadType protoType;
+    private final Class<? extends VType> vTypeClass;
+
+    // Mapa para búsqueda rápida inversa
+    private static final Map<PayloadType, MerlotTypeMapping> BY_PROTO = new 
HashMap<>();
+
+    static {
+        for (MerlotTypeMapping mapping : values()) {
+            BY_PROTO.put(mapping.protoType, mapping);
+        }
+    }
+
+    MerlotTypeMapping(PayloadType protoType, Class<? extends VType> 
vTypeClass) {
+        this.protoType = protoType;
+        this.vTypeClass = vTypeClass;
+    }
+
+    public PayloadType getProtoType() { return protoType; }
+    public Class<? extends VType> getVTypeClass() { return vTypeClass; }
+
+    /**
+     * Obtiene el mapeo a partir del tipo de Protobuf
+     */
+    public static MerlotTypeMapping fromProto(PayloadType type) {
+        return BY_PROTO.get(type);
+    }
+    
+    /**
+     * Obtiene el PayloadType correspondiente a una instancia de VType.
+     * @param vtype La instancia de datos de EPICS (VDouble, VInt, etc.)
+     * @return El PayloadType de EPICSEvent o null si no hay coincidencia.
+     */
+    public static PayloadType fromVType(VType vtype) {
+        if (vtype == null) return null;
+
+        for (MerlotTypeMapping mapping : values()) {
+            // Verificamos si la instancia de vtype implementa la clase del 
mapeo
+            if (mapping.getVTypeClass().isInstance(vtype)) {
+                return mapping.getProtoType();
+            }
+        }
+        return null;
+    }    
+}
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
index 9d9b280..e0aed53 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotDataBrowserRTGetDataPVImpl.java
@@ -17,14 +17,12 @@
 package org.apache.plc4x.merlot.archiver.impl;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintWriter;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import org.apache.plc4x.merlot.api.PB.EPICSEvent;
-import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadInfo;
-import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadInfo.Builder;
 import org.apache.plc4x.merlot.archiver.api.MerlotHtc;
 
 public class MerlotDataBrowserRTGetDataPVImpl extends HttpServlet {
@@ -38,20 +36,19 @@ public class MerlotDataBrowserRTGetDataPVImpl extends 
HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
         String nombre = req.getParameter("nombre");  
-        resp.setContentType("text/plain");  
+        resp.setContentType("text/plain");
         PrintWriter pw = resp.getWriter();  
         pw.println("Tu nombre: " + nombre);
         pw.close();
     }
     
-    
-//    private Builder buildHeader(){
-//        Builder builder = PayloadInfo.newBuilder()
-//                .setPvname("")
-//                .setType(EPICSEvent.PayloadType.SCALAR_ENUM)
-//                .setYear(0).
-//                .setElementCount(0);
-//        return null;
-//    }
-    
+    /*
+    * 
+    */
+    private void PBRawResponse(String pv req, OutputStream out) {
+        String[] pvs = req.getParameterValues("pv");
+        
+
+    }
+        
 }
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
index e8e2651..8b34897 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcIoTDBImpl.java
@@ -106,7 +106,7 @@ public class MerlotHtcIoTDBImpl implements MerlotHtc {
     }
 
     @Override
-    public List<Pair<LocalDateTime, VType>> getPVs(String strPV, String init, 
String end) {
+    public List<VType> getPVs(String strPV, String init, String end) {
         return null;
     }
     
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
index 4e80cdb..6ad4817 100644
--- 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/main/java/org/apache/plc4x/merlot/archiver/impl/MerlotHtcRTImpl.java
@@ -16,8 +16,10 @@
  */
 package org.apache.plc4x.merlot.archiver.impl;
 
+import java.time.Instant;
 import java.time.LocalDateTime;
-import java.util.Collection;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -26,12 +28,10 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import org.apache.commons.collections4.queue.CircularFifoQueue;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
 import org.apache.plc4x.merlot.archiver.api.MerlotGPClient;
 import org.apache.plc4x.merlot.archiver.api.MerlotHtc;
 import org.epics.gpclient.GPClientInstance;
-import org.epics.gpclient.PV;
+import org.epics.gpclient.PVEvent;
 import org.epics.gpclient.PVReader;
 import org.epics.gpclient.datasource.CompositeDataSource;
 import org.epics.vtype.VDouble;
@@ -49,42 +49,32 @@ public class MerlotHtcRTImpl implements MerlotHtc {
     private static final Pattern SIM_PATTERN = Pattern.compile("(^noise)");
 
     private static final String strID = "rt";
+    
+    private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
+            .append(DateTimeFormatter.ISO_INSTANT)
+            .appendLiteral("(0)")
+            .toFormatter();
 
     CompositeDataSource cds = new CompositeDataSource();
 
     private Map<String, PVReader<VType>> readerPvs = new ConcurrentHashMap<>();
-    private Map<String, CircularFifoQueue<Pair<LocalDateTime, VType>>> pvs = 
new ConcurrentHashMap<>();
+    private Map<String, CircularFifoQueue<VType>> pvs = new 
ConcurrentHashMap<>();
 
     GPClientInstance gpClient;
-
-    private PVReader<VType> pv1;
-    private PVReader<VType> pv2;
+    
 
     public MerlotHtcRTImpl(MerlotGPClient gpMerlotClient) {     
-        gpClient = gpMerlotClient.gpClientDefaultInstance();
+        gpClient = gpMerlotClient.gpClientFactory("HtcRT ");
     }
 
     @Override
     public void init() {       
-        CircularFifoQueue<Pair<LocalDateTime, VType>> queue = new 
CircularFifoQueue<Pair<LocalDateTime, VType>>(2400);
-        pvs.put("sim://noise", queue);
-        
-        pv1 = gpClient.read("sim://noise")
-                .addReadListener((event, p) -> {
-                    pvs.get("sim://noise").add(new 
ImmutablePair(LocalDateTime.now(), p.getValue()));
-                })
-                .start();
-        
-        readerPvs.put("sim://noise", pv1);
-       
+        //       
     }
 
     @Override
     public void destroy() {
         try {
-            pv1.close();
-            pv2.close();
-            gpClient.getDefaultDataSource().getChannels().clear();
             gpClient.close();
         } catch (IllegalStateException e) {
             e.printStackTrace();
@@ -97,13 +87,31 @@ public class MerlotHtcRTImpl implements MerlotHtc {
     }
 
     @Override
-    public void addPV(String strPV, Double interval) {
-        //
+    public void addPV(String strPV, Double maxRate) {
+        
+        PVReader<VType> pv ;
+        CircularFifoQueue<VType> queue = new CircularFifoQueue<VType>(2400);   
     
+        pvs.put(strPV, queue);
+        String strPVA = "pva://" + strPV.trim();
+        
+        pv = gpClient.read(strPVA)
+                .addReadListener((event, p) -> {
+                    if (event.isType(PVEvent.Type.VALUE)) {
+                        pvs.get(strPV).add(p.getValue());
+                    }
+                })
+                .start();
+        
+        readerPvs.put(strPV, pv);        
+                
     }
 
     @Override
     public void removePV(String strPV) {
-        //
+        var pv = readerPvs.get(strPV);
+        readerPvs.remove(strPV);
+        pv.close();
+        pvs.remove(strPV);                
     }
 
     @Override
@@ -115,19 +123,26 @@ public class MerlotHtcRTImpl implements MerlotHtc {
     }
 
     @Override
-    public List<Pair<LocalDateTime, VType>> getPVs(String strPV, String init, 
String end) {
-        List<Pair<LocalDateTime, VType>> result;
-        LocalDateTime inicio = LocalDateTime.parse(init);
-        LocalDateTime fin = LocalDateTime.parse(end);
-
-        return pvs.values().stream() // Obtenemos todas las CircularFifoQueue
-                .flatMap(Collection::stream) // Aplanamos todas las colas en 
un solo stream de pares
-                .filter(pair -> {
-                    LocalDateTime fecha = pair.getLeft();
-                    // Filtro: inicio <= fecha <= fin
-                    return !fecha.isBefore(inicio) && !fecha.isAfter(fin);
-                })
-                .collect(Collectors.toList());
+    public List<VType> getPVs(String strPV, String init, String end) {
+        List<VType> result;
+        if (init.indexOf("(") > 0) {
+            init = init.substring(0, init.indexOf("("));
+        }
+        if (end.indexOf("(") > 0) {
+            end = end.substring(0, end.indexOf("("));
+        }
+
+        Instant inicio = Instant.parse(init);
+        Instant fin    = Instant.parse(end);
+       
+        var queue = pvs.get(strPV);
+
+        return queue.stream()              
+            .filter(v -> {
+                    Instant fecha = ((VDouble) v).getTime().getTimestamp();
+                    return !fecha.isBefore(inicio) && !fecha.isAfter(fin);     
                   
+            })
+            .collect(Collectors.toList());                
     }
 
 }
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotPayloadMappingTest.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotPayloadMappingTest.java
new file mode 100644
index 0000000..85d2e68
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotPayloadMappingTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.test;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.apache.plc4x.merlot.archiver.core.MerlotPayloadMapping;
+import org.epics.vtype.Alarm;
+import org.epics.vtype.Display;
+import org.epics.vtype.Time;
+import org.epics.vtype.VDouble;
+import org.epics.vtype.VDoubleArray;
+import org.epics.vtype.VInt;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+class MerlotPayloadMappingTest {
+
+    @Test
+    @DisplayName("Debe mapear correctamente un VDouble a ScalarDouble de 
Protobuf")
+    void testCreateScalarDouble() {
+        // 1. Preparar datos de prueba
+        double testValue = 123.456;
+        Instant now = Instant.now();
+        // Usamos implementaciones estándar de VType para la prueba
+        VDouble vDouble = VDouble.of(testValue, Alarm.none(), Time.of(now), 
Display.none());
+
+        // 2. Ejecutar el mapeo
+        Object result = MerlotPayloadMapping.createEvent(vDouble);
+
+        // 3. Verificaciones 
+        assertNotNull(result);
+        assertTrue(result instanceof EPICSEvent.ScalarDouble);
+        
+        EPICSEvent.ScalarDouble pbEvent = (EPICSEvent.ScalarDouble) result;
+        assertEquals(testValue, pbEvent.getVal(), 0.0001);
+        assertEquals(now.getNano(), pbEvent.getNano());
+        assertTrue(pbEvent.getSecondsintoyear() >= 0);
+    }
+
+    @Test
+    @DisplayName("Debe mapear correctamente un VDoubleArray a VectorDouble de 
Protobuf")
+    void testCreateVectorDouble() {
+        // 1. Preparar Waveform de prueba
+        List<Double> testData = Arrays.asList(1.0, 2.0, 3.0);
+        VDoubleArray vArray = 
VDoubleArray.of(org.epics.util.array.ArrayDouble.of(1.0, 2.0, 3.0), 
+                                             Alarm.none(), Time.now(), 
Display.none());
+
+        // 2. Ejecutar 
+        Object result = MerlotPayloadMapping.createEvent(vArray);
+
+        // 3. Verificaciones
+        assertTrue(result instanceof EPICSEvent.VectorDouble);
+        EPICSEvent.VectorDouble vector = (EPICSEvent.VectorDouble) result;
+        assertEquals(3, vector.getValCount());
+        assertEquals(1.0, vector.getVal(0));
+    }
+
+    @Test
+    @DisplayName("Debe identificar el tipo MerlotPayloadMapping desde una 
instancia")
+    void testFromVType() {
+        VInt vInt = VInt.of(10, Alarm.none(), Time.now(), Display.none());
+        MerlotPayloadMapping mapping = MerlotPayloadMapping.fromVType(vInt);
+        
+        assertEquals(MerlotPayloadMapping.SCALAR_INT, mapping);
+//        assertEquals(EPICSEvent.PayloadType.SCALAR_INT, mapping.SCALAR_INT.);
+    }
+
+    @Test
+    @DisplayName("Debe retornar null para tipos no soportados o nulos")
+    void testUnsupportedTypes() {
+        assertNull(MerlotPayloadMapping.createEvent(null));
+    }
+}
\ No newline at end of file
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotTypeMappingTest.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotTypeMappingTest.java
new file mode 100644
index 0000000..2ad7a54
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/MerlotTypeMappingTest.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.test;
+
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt 
to change this license
+ * Click 
nbfs://nbhost/SystemFileSystem/Templates/UnitTests/JUnit5TestClass.java to edit 
this template
+ */
+
+import java.time.Instant;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent.PayloadType;
+import org.apache.plc4x.merlot.archiver.core.MerlotTypeMapping;
+import org.epics.vtype.Alarm;
+import org.epics.vtype.AlarmSeverity;
+import org.epics.vtype.Display;
+import org.epics.vtype.Time;
+import org.epics.vtype.VDouble;
+import org.epics.vtype.VType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/**
+ *
+ * @author cgarcia
+ */
+public class MerlotTypeMappingTest {
+    
+    public MerlotTypeMappingTest() {
+    }
+    
+    @BeforeAll
+    public static void setUpClass() {
+    }
+    
+    @AfterAll
+    public static void tearDownClass() {
+    }
+    
+    @BeforeEach
+    public void setUp() {
+    }
+    
+    @AfterEach
+    public void tearDown() {
+    }
+
+    @Test
+    @DisplayName("Debe retornar el mapeo correcto para SCALAR_DOUBLE")
+    void testScalarDoubleMapping() {
+        MerlotTypeMapping mapping = 
MerlotTypeMapping.fromProto(PayloadType.SCALAR_DOUBLE);
+        
+        assertNotNull(mapping, "El mapeo no debería ser nulo");
+        assertEquals(VDouble.class, mapping.getVTypeClass(), 
+            "SCALAR_DOUBLE debe mapear a la interfaz VDouble");
+    }
+
+    @ParameterizedTest
+    @EnumSource(MerlotTypeMapping.class)
+    @DisplayName("Validar integridad de todos los mapeos definidos")
+    void testAllMappings(MerlotTypeMapping mapping) {
+        // Verifica que la clase VType asociada sea efectivamente una 
subinterfaz de VType
+        assertTrue(VType.class.isAssignableFrom(mapping.getVTypeClass()),
+            "La clase mapeada " + mapping.getVTypeClass().getName() + " debe 
ser un VType");
+        
+        // Verifica que el tipo de proto no sea nulo
+        assertNotNull(mapping.getProtoType(), 
+            "El tipo de Protobuf para " + mapping.name() + " no debe ser 
nulo");
+    }
+
+    @Test
+    @DisplayName("Debe manejar tipos de payload desconocidos devolviendo null")
+    void testUnknownPayloadType() {
+        // Usamos un valor que no esté en nuestro Enum de mapeo (si existe en 
el .proto)
+        // o simplemente verificamos el comportamiento con null
+        assertNull(MerlotTypeMapping.fromProto(null), 
+            "El mapeo de un tipo null debe resultar en null");
+    }
+
+    @Test
+    @DisplayName("Verificar consistencia del mapa estático inverso")
+    void testReverseMapConsistency() {
+        for (MerlotTypeMapping mapping : MerlotTypeMapping.values()) {
+            MerlotTypeMapping retrieved = 
MerlotTypeMapping.fromProto(mapping.getProtoType());
+            assertEquals(mapping, retrieved, 
+                "El mapeo recuperado por PayloadType debe coincidir con la 
instancia del Enum");
+        }
+    }
+    
+    @Test
+    @DisplayName("Debe detectar el PayloadType correcto desde una instancia 
concreta")
+    void testFromVTypeInstance() {
+        // Creamos una instancia de prueba (VDouble)
+        VDouble myValue = VDouble.of(10.5, Alarm.none(), Time.now(), 
Display.none());
+
+        PayloadType detectedType = MerlotTypeMapping.fromVType(myValue);
+
+        assertEquals(PayloadType.SCALAR_DOUBLE, detectedType, 
+            "Una instancia de VDouble debe ser reconocida como SCALAR_DOUBLE");
+    }    
+
+    
+}
diff --git 
a/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/PBRawFileSerializer.java
 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/PBRawFileSerializer.java
new file mode 100644
index 0000000..0297338
--- /dev/null
+++ 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.archiver/src/test/java/org/apache/plc4x/merlot/archiver/test/PBRawFileSerializer.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.plc4x.merlot.archiver.test;
+
+/**
+ *
+ * @author cgarcia
+ */
+import org.epics.vtype.VDouble;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import org.apache.plc4x.merlot.api.PB.EPICSEvent;
+import org.epics.vtype.Alarm;
+import org.epics.vtype.AlarmSeverity;
+import org.epics.vtype.Display;
+import org.epics.vtype.Time;
+
+public class PBRawFileSerializer {
+
+    // Caracter de escape y delimitadores según el protocolo del Archiver
+    private static final int ESCAPE = 0x1B;
+    private static final int NEWLINE = 0x0A;
+    
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        PBRawFileSerializer serializer = new PBRawFileSerializer();
+        List<VDouble> randomEvents = new ArrayList<>();
+        Random rand = new Random();
+        
+        // Generar 10 valores aleatorios empezando desde ahora
+        Instant now = Instant.now();
+        for (int i = 0; i < 10; i++) {
+            double randomValue = 20.0 + (30.0 - 20.0) * rand.nextDouble();
+            // Incrementamos el tiempo en 1 segundo por cada muestra
+            Instant timestamp = now.plusSeconds(i);
+            Time ts = Time.of(timestamp);
+
+            randomEvents.add(VDouble.of(randomValue,
+                    Alarm.none(),
+                    ts,
+                    Display.none()));
+        }
+        
+        try {
+            String fileName = "random_data.pbraw";
+            serializer.serializeToPBRaw(randomEvents, "MY:RANDOM:PV", 
fileName);
+            System.out.println("Archivo '" + fileName + "' generado con 10 
valores aleatorios.");
+        } catch (IOException e) {
+            System.err.println("Error al generar el archivo: " + 
e.getMessage());
+        }
+    }       
+    
+    
+    public void serializeToPBRaw(List<VDouble> events, String pvName, String 
fileName) throws IOException {
+        try (FileOutputStream fos = new FileOutputStream(fileName)) {
+            
+            // 1. Escribir el PayloadInfo (Metadatos iniciales)
+            // El cliente necesita esto para saber que los datos son 
ScalarDouble
+            EPICSEvent.PayloadInfo info = EPICSEvent.PayloadInfo.newBuilder()
+                    .setPvname(pvName)
+                    .setType(EPICSEvent.PayloadType.SCALAR_DOUBLE)
+                    .setYear(2026)                    
+                    .setElementCount(1)
+                    .build();
+            
+            info.writeDelimitedTo(fos);
+
+            // 2. Serializar cada VDouble
+            for (VDouble vDouble : events) {
+                EPICSEvent.ScalarDouble pbEvent = buildProtosEvent(vDouble);
+                
+                // El cliente PBRAW espera los datos escapados si se 
transmiten por stream
+                // Para un archivo local simple, writeDelimitedTo suele 
bastar, 
+                // pero implementamos el guardado binario puro aquí:
+                byte[] eventBytes = pbEvent.toByteArray();
+                
+                // Escribir tamaño del mensaje (como varint o delimitado)
+                writeVarint32(fos, eventBytes.length);
+                fos.write(eventBytes);
+            }
+        }
+    }
+
+    private EPICSEvent.ScalarDouble buildProtosEvent(VDouble v) {
+        Instant ts = v.getTime().getTimestamp();
+        int year = ZonedDateTime.ofInstant(ts, ZoneId.of("UTC")).getYear();
+        long yearStart = ZonedDateTime.of(year, 1, 1, 0, 0, 0, 0, 
ZoneId.of("UTC")).toEpochSecond();
+
+        return EPICSEvent.ScalarDouble.newBuilder()
+                .setSecondsintoyear((int) (ts.getEpochSecond() - yearStart))
+                .setNano(ts.getNano())
+                .setVal(v.getValue())
+                .setSeverity(v.getAlarm().getStatus().ordinal())
+                .build();
+    }
+
+    // Utilidad para escribir el prefijo de tamaño que espera el 
deserializador de Google
+    private void writeVarint32(OutputStream out, int value) throws IOException 
{
+        while (true) {
+            if ((value & ~0x7F) == 0) {
+                out.write(value);
+                return;
+            } else {
+                out.write((value & 0x7F) | 0x80);
+                value >>>= 7;
+            }
+        }
+    }
+            
+    
+    
+}
diff --git a/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml 
b/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml
index 2b87992..d45abf4 100644
--- a/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml
+++ b/plc4j/tools/merlot/org.apache.plc4x.merlot.das.api/pom.xml
@@ -244,6 +244,11 @@
             <version>0.13.0-SNAPSHOT</version>
         </dependency>
 -->
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+            <version>4.34.1</version>
+        </dependency>
     </dependencies>
     
     <profiles>

Reply via email to