@Override
  public Object write(List<ChannelValueContainer> containers, Object containerListHandle)
      throws UnsupportedOperationException, ConnectionException {

    for (ChannelValueContainer container : containers) {

      ModbusChannel modbusChannel = getModbusChannel(container.getChannelAddress(), EAccess.WRITE);
      if (modbusChannel != null) {
        try {
          writeChannel(modbusChannel, container.getValue());
          container.setFlag(Flag.VALID);
        } catch (ModbusException modbusException) {
          container.setFlag(Flag.UNKNOWN_ERROR);
          modbusException.printStackTrace();
          throw new ConnectionException(
              "Unable to write data on channel address: " + container.getChannelAddress());
        } catch (Exception e) {
          container.setFlag(Flag.UNKNOWN_ERROR);
          e.printStackTrace();
          logger.error("Unable to write data on channel address: " + container.getChannelAddress());
        }
      } else {
        // TODO
        container.setFlag(Flag.UNKNOWN_ERROR);
        logger.error(
            "Unable to write data on channel address: "
                + container.getChannelAddress()
                + "modbusChannel = null");
      }
    }

    return null;
  }
  @Override
  public Object read(
      List<ChannelRecordContainer> containers, Object containerListHandle, String samplingGroup)
      throws UnsupportedOperationException, ConnectionException {

    // reads channels one by one
    if (samplingGroup.isEmpty()) {
      for (ChannelRecordContainer container : containers) {
        long receiveTime = System.currentTimeMillis();
        ModbusChannel channel = getModbusChannel(container.getChannelAddress(), EAccess.READ);
        Value value;
        try {
          value = readChannel(channel);
          // logger.debug("{}: value = '{}'", channel.getChannelAddress(), value.toString());
          container.setRecord(new Record(value, receiveTime));
        } catch (ModbusException e) {
          e.printStackTrace();
          container.setRecord(new Record(Flag.DRIVER_ERROR_CHANNEL_NOT_ACCESSIBLE));
        }
      }
    }
    // reads whole samplingGroup at once
    else {
      // TODO test channel group
      logger.warn("Reading samplingGroup is not tested yet!");
      readChannelGroupHighLevel(containers, containerListHandle, samplingGroup);
    }

    return null;
  }
  private Object readChannelGroupHighLevel(
      List<ChannelRecordContainer> containers, Object containerListHandle, String samplingGroup) {

    // NOTE: containerListHandle is null if something changed in configuration!!!

    ModbusChannelGroup channelGroup = null;

    // use existing channelGroup
    if (containerListHandle != null) {
      if (containerListHandle instanceof ModbusChannelGroup) {
        channelGroup = (ModbusChannelGroup) containerListHandle;
      }
    }

    // create new channelGroup
    if (channelGroup == null) {
      ArrayList<ModbusChannel> channelList = new ArrayList<ModbusChannel>();
      for (ChannelRecordContainer container : containers) {
        channelList.add(getModbusChannel(container.getChannelAddress(), EAccess.READ));
      }
      channelGroup = new ModbusChannelGroup(samplingGroup, channelList);
    }

    // read all channels of the group
    try {
      readChannelGroup(channelGroup, containers);

    } catch (ModbusException e) {
      e.printStackTrace();

      // set channel values and flag, otherwise the datamanager will throw a null pointer exception
      // and the framework collapses.
      setChannelsWithErrorFlag(containers);
    }

    // logger.debug("### readChannelGroup duration in ms = " + ((new Date().getTime()) -
    // startTime));

    return channelGroup;
  }