/** Read info from data server */
  public void read(RPCClientImpl client, double REQUEST_TIMEOUT) throws Exception {

    PVStructure pvRequest = createRequest();

    PVInt keyField = pvRequest.getIntField("key");
    keyField.put(key);
    PVString patternField = pvRequest.getStringField("pattern");
    patternField.put(pattern);

    PVStructure pvResult = client.request(pvRequest, REQUEST_TIMEOUT);

    // process result

    PVStructureArray infos = pvResult.getStructureArrayField("channels");

    StructureArrayData data = new StructureArrayData();
    infos.get(0, infos.getLength(), data);

    this.chNames = new String[data.data.length];
    this.starts = new Instant[data.data.length];
    this.ends = new Instant[data.data.length];

    for (int i = 0; i < data.data.length; i++) {

      PVStructure info = data.data[i];

      chNames[i] = info.getStringField("name").get();

      int start_secs = info.getIntField("start_sec").get();
      int start_nano = info.getIntField("start_nano").get();
      starts[i] = Instant.ofEpochSecond(start_secs, start_nano);

      int end_secs = info.getIntField("end_sec").get();
      int end_nano = info.getIntField("end_nano").get();
      ends[i] = Instant.ofEpochSecond(end_secs, end_nano);
    }
  }
  // TODO check if non-V types can ever be given as newValue
  private final void fromObject(PVField field, Object newValue) {
    // enum support
    if (isChannelEnumType) {
      // value.index int field expected
      PVInt indexPutField = (PVInt) channelPutValueField;

      int index = -1;
      if (newValue instanceof Number) {
        index = ((Number) newValue).intValue();
      } else if (newValue instanceof String) {
        String nv = (String) newValue;

        PVStructure lastValue = getLastMessagePayload();
        if (lastValue == null)
          throw new IllegalArgumentException(
              "no monitor on '" + getChannelName() + "' created to get list of valid enum choices");

        PVStringArray pvChoices = (PVStringArray) lastValue.getSubField("value.choices");
        StringArrayData data = new StringArrayData();
        pvChoices.get(0, pvChoices.getLength(), data);
        final String[] choices = data.data;

        for (int i = 0; i < choices.length; i++) {
          if (nv.equals(choices[i])) {
            index = i;
            break;
          }
        }

        if (index == -1)
          throw new IllegalArgumentException("enumeration '" + nv + "' is not a valid choice");
      }

      indexPutField.put(index);

      return;
    }

    if (channelPutValueField instanceof PVScalar) {
      if (newValue instanceof Double)
        convert.fromDouble((PVScalar) field, ((Double) newValue).doubleValue());
      else if (newValue instanceof Integer)
        convert.fromInt((PVScalar) field, ((Integer) newValue).intValue());
      else if (newValue instanceof String) convert.fromString((PVScalar) field, (String) newValue);
      else if (newValue instanceof Byte)
        convert.fromByte((PVScalar) field, ((Byte) newValue).byteValue());
      else if (newValue instanceof Short)
        convert.fromShort((PVScalar) field, ((Short) newValue).shortValue());
      else if (newValue instanceof Long)
        convert.fromLong((PVScalar) field, ((Long) newValue).longValue());
      else if (newValue instanceof Float)
        convert.fromFloat((PVScalar) field, ((Float) newValue).floatValue());
      else
        throw new RuntimeException(
            "Unsupported write, cannot put '"
                + newValue.getClass()
                + "' into scalar '"
                + channelPutValueField.getField()
                + "'");
    } else if (channelPutValueField instanceof PVScalarArray) {
      // if it's a ListNumber, extract the array
      if (newValue instanceof ListNumber) {
        ListNumber data = (ListNumber) newValue;
        Object wrappedArray = CollectionNumbers.wrappedArray(data);
        if (wrappedArray == null) {
          newValue = CollectionNumbers.doubleArrayCopyOf(data);
        } else {
          newValue = wrappedArray;
        }
      } else if (!newValue.getClass().isArray()) {
        // create an array
        Object newValueArray = Array.newInstance(newValue.getClass(), 1);
        Array.set(newValueArray, 0, newValue);
        newValue = newValueArray;
      }

      if (newValue instanceof double[])
        convert.fromDoubleArray(
            (PVScalarArray) field, 0, ((double[]) newValue).length, (double[]) newValue, 0);
      else if (newValue instanceof int[])
        convert.fromIntArray(
            (PVScalarArray) field, 0, ((int[]) newValue).length, (int[]) newValue, 0);
      else if (newValue instanceof String[])
        convert.fromStringArray(
            (PVScalarArray) field, 0, ((String[]) newValue).length, (String[]) newValue, 0);
      // special case from string to array
      else if (newValue instanceof String) {
        String str = ((String) newValue).trim();

        // remove []
        if (str.charAt(0) == '[' && str.charAt(str.length() - 1) == ']')
          str = str.substring(1, str.length() - 1);

        // split on commas and whitespaces
        String[] splitValues = str.split("[,\\s]+");
        convert.fromStringArray((PVScalarArray) field, 0, splitValues.length, splitValues, 0);
      } else if (newValue instanceof byte[])
        convert.fromByteArray(
            (PVScalarArray) field, 0, ((byte[]) newValue).length, (byte[]) newValue, 0);
      else if (newValue instanceof short[])
        convert.fromShortArray(
            (PVScalarArray) field, 0, ((short[]) newValue).length, (short[]) newValue, 0);
      else if (newValue instanceof long[])
        convert.fromLongArray(
            (PVScalarArray) field, 0, ((long[]) newValue).length, (long[]) newValue, 0);
      else if (newValue instanceof float[])
        convert.fromFloatArray(
            (PVScalarArray) field, 0, ((float[]) newValue).length, (float[]) newValue, 0);
      else
        throw new RuntimeException(
            "Unsupported write, cannot put '"
                + newValue.getClass()
                + "' into array'"
                + channelPutValueField.getField()
                + "'");
    } else
      throw new RuntimeException(
          "Unsupported write, cannot put '"
              + newValue.getClass()
              + "' into '"
              + channelPutValueField.getField()
              + "'");
  }