Пример #1
0
  @Override
  public void addReader(ChannelHandlerReadSubscription subscription) {
    super.addReader(subscription);

    if (!monitorCreated.getAndSet(true)) {
      // TODO remove this....
      for (int i = 0;
          i < 100 && channel.getConnectionState() == ConnectionState.NEVER_CONNECTED;
          i++) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
        }
      }
      // TODO optimize fields
      channel.createMonitor(this, CreateRequestFactory.createRequest("field()", this));
    }
  }
Пример #2
0
/** @author msekoranja */
public class PVAChannelHandler extends MultiplexedChannelHandler<PVAChannelHandler, PVStructure>
    implements ChannelRequester, GetFieldRequester, ChannelPutRequester, MonitorRequester {

  private final ChannelProvider pvaChannelProvider;
  private final short priority;
  private final PVATypeSupport pvaTypeSupport;

  private volatile Channel channel = null;

  private final AtomicBoolean monitorCreated = new AtomicBoolean(false);
  // private volatile Monitor monitor = null;

  private volatile Field channelType = null;
  private volatile boolean isChannelEnumType = false;

  private final AtomicBoolean channelPutCreated = new AtomicBoolean(false);
  private volatile ChannelPut channelPut = null;
  private volatile PVField channelPutValueField = null;

  private static final Logger logger = Logger.getLogger(PVAChannelHandler.class.getName());

  private static PVStructure standardPutPVRequest =
      CreateRequestFactory.createRequest("field(value)", null);
  private static PVStructure enumPutPVRequest =
      CreateRequestFactory.createRequest("field(value.index)", null);

  public PVAChannelHandler(
      String channelName,
      ChannelProvider channelProvider,
      short priority,
      PVATypeSupport typeSupport) {
    super(channelName);
    this.pvaChannelProvider = channelProvider;
    this.priority = priority;
    this.pvaTypeSupport = typeSupport;
  }

  /** @return the channel */
  public Channel getChannel() {
    return channel;
  }

  /** @return the channelType */
  public Field getChannelType() {
    return channelType;
  }

  @Override
  public String getRequesterName() {
    return this.getClass().getName();
  }

  @Override
  public void message(String message, MessageType messageType) {
    logger.log(toLoggerLevel(messageType), message);
  }

  /**
   * Converts MessageType to Java Logging API Level.
   *
   * @param messageType pvData message type.
   * @return Corresponded Java Logging API Level.
   */
  public static Level toLoggerLevel(MessageType messageType) {
    switch (messageType) {
      case info:
        return Level.INFO;
      case warning:
        return Level.WARNING;
      case error:
      case fatalError:
        return Level.SEVERE;
      default:
        return Level.INFO;
    }
  }

  private void reportStatus(String message, Status status) {
    if (!status.isSuccess()) {
      logger.log(Level.WARNING, message + ": " + status.getMessage());

      // for developers
      String dump = status.getStackDump();
      if (dump != null && !dump.isEmpty())
        logger.log(Level.FINER, message + ": " + status.getMessage() + ", cause:\n" + dump);
    }
  }

  @Override
  public void connect() {
    pvaChannelProvider.createChannel(getChannelName(), this, priority);
  }

  @Override
  public void channelCreated(Status status, Channel channel) {
    reportStatus("Failed to create channel instance '" + channel.getChannelName(), status);
    this.channel = channel;
  }

  @Override
  public void channelStateChange(Channel channel, ConnectionState connectionState) {
    try {

      // introspect
      if (connectionState == ConnectionState.CONNECTED) {
        channel.getField(this, null);
      } else {
        processConnection(this);
      }

    } catch (Exception ex) {
      reportExceptionToAllReadersAndWriters(ex);
    }
  }

  /* (non-Javadoc)
   * @see org.epics.pvaccess.client.GetFieldRequester#getDone(org.epics.pvdata.pv.Status, org.epics.pvdata.pv.Field)
   */
  @Override
  public void getDone(Status status, Field field) {
    reportStatus("Failed to instrospect channel '" + channel.getChannelName() + "'", status);

    if (status.isSuccess()) {
      channelType = field;

      Field valueField = ((Structure) channelType).getField("value");
      if (valueField != null && valueField.getID().equals("enum_t")) {
        isChannelEnumType = true;
        // TODO could create a monitor just to get value.choices
      } else isChannelEnumType = false;
    }

    processConnection(this);
  }

  @Override
  public boolean isConnected(PVAChannelHandler channel) {
    final Channel c = channel.getChannel();
    return c != null && c.isConnected();
  }

  @Override
  protected boolean isWriteConnected(PVAChannelHandler channel) {
    // NOTE: access-rights not yet supported
    final Channel c = channel.getChannel();
    return c != null && c.isConnected();
  }

  @Override
  public synchronized Map<String, Object> getProperties() {
    Map<String, Object> properties = new HashMap<String, Object>();
    if (channel != null) {
      properties.put("Channel name", channel.getChannelName());
      properties.put("Connection state", channel.getConnectionState().name());
      properties.put("Provider name", channel.getProvider().getProviderName());
      if (channel.getConnectionState() == Channel.ConnectionState.CONNECTED) {
        properties.put("Remote address", channel.getRemoteAddress());
        properties.put("Channel type", channelType.getID());
        // properties.put("Read access", channel.getReadAccess());
        // properties.put("Write access", channel.getWriteAccess());
      }
    }
    return properties;
  }

  @Override
  public void disconnect() {
    // Close the channel
    try {
      channel.destroy();
    } finally {
      channel = null;

      // monitor = null;
      monitorCreated.set(false);

      channelType = null;

      channelPut = null;
      channelPutValueField = null;
      channelPutCreated.set(false);
    }
  }

  static class WriteRequest {
    private final Object newValue;
    private final ChannelWriteCallback callback;

    public WriteRequest(Object newValue, ChannelWriteCallback callback) {
      this.newValue = newValue;
      this.callback = callback;
    }

    public Object getNewValue() {
      return newValue;
    }

    public ChannelWriteCallback getCallback() {
      return callback;
    }
  }

  private final LinkedList<WriteRequest> writeRequests = new LinkedList<WriteRequest>();

  @Override
  public void write(Object newValue, ChannelWriteCallback callback) {

    boolean wasEmpty;
    synchronized (writeRequests) {
      wasEmpty = writeRequests.isEmpty();
      writeRequests.add(new WriteRequest(newValue, callback));
    }

    if (!channelPutCreated.getAndSet(true)) {
      channel.createChannelPut(this, isChannelEnumType ? enumPutPVRequest : standardPutPVRequest);
    } else if (wasEmpty) {
      doNextWrite();
    }
  }

  private void doNextWrite() {
    WriteRequest writeRequest;
    synchronized (writeRequests) {
      writeRequest = writeRequests.peek();
    }

    if (writeRequest != null) {
      try {
        if (channelPutValueField == null) throw new RuntimeException("No 'value' field");

        fromObject(channelPutValueField, writeRequest.getNewValue());
        channelPut.put(false);
      } catch (Exception ex) {
        writeRequests.poll();
        writeRequest.getCallback().channelWritten(ex);
      }
    }
  }

  /* (non-Javadoc)
   * @see org.epics.pvaccess.client.ChannelPutRequester#channelPutConnect(org.epics.pvdata.pv.Status, org.epics.pvaccess.client.ChannelPut, org.epics.pvdata.pv.PVStructure, org.epics.pvdata.misc.BitSet)
   */
  @Override
  public void channelPutConnect(
      Status status, ChannelPut channelPut, PVStructure pvStructure, BitSet bitSet) {
    reportStatus("Failed to create ChannelPut instance", status);

    if (status.isSuccess()) {
      this.channelPut = channelPut;

      if (isChannelEnumType) {
        // handle inconsistent behavior
        this.channelPutValueField = pvStructure.getSubField("value");
        if (this.channelPutValueField instanceof PVStructure)
          this.channelPutValueField = ((PVStructure) pvStructure).getSubField("index");
      } else {
        this.channelPutValueField = pvStructure.getSubField("value");
      }

      // set BitSet
      bitSet.clear(); // re-connect case
      if (this.channelPutValueField != null) bitSet.set(channelPutValueField.getFieldOffset());
    }

    doNextWrite();
  }

  /* (non-Javadoc)
   * @see org.epics.pvaccess.client.ChannelPutRequester#putDone(org.epics.pvdata.pv.Status)
   */
  @Override
  public void putDone(Status status) {
    reportStatus("Failed to put value", status);

    WriteRequest writeRequest;
    synchronized (writeRequests) {
      writeRequest = writeRequests.poll();
    }

    if (writeRequest != null) {
      if (status.isSuccess()) {
        writeRequest.getCallback().channelWritten(null);
      } else {
        writeRequest.getCallback().channelWritten(new Exception(status.getMessage()));
      }

      doNextWrite();
    }
  }

  /* (non-Javadoc)
   * @see org.epics.pvaccess.client.ChannelPutRequester#getDone(org.epics.pvdata.pv.Status)
   */
  @Override
  public void getDone(Status status) {
    // never used, i.e. ChannelPut.get() never called
  }

  private static final Convert convert = ConvertFactory.getConvert();

  // 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()
              + "'");
  }

  @Override
  protected PVATypeAdapter findTypeAdapter(ValueCache<?> cache, PVAChannelHandler connection) {
    PVATypeAdapter pta = null;
    try {
      pta = pvaTypeSupport.find(cache, connection);
    } catch (Throwable th) {
      th.printStackTrace();
    }
    return pta;
  }

  @Override
  public void addReader(ChannelHandlerReadSubscription subscription) {
    super.addReader(subscription);

    if (!monitorCreated.getAndSet(true)) {
      // TODO remove this....
      for (int i = 0;
          i < 100 && channel.getConnectionState() == ConnectionState.NEVER_CONNECTED;
          i++) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
        }
      }
      // TODO optimize fields
      channel.createMonitor(this, CreateRequestFactory.createRequest("field()", this));
    }
  }

  /* (non-Javadoc)
   * @see org.epics.pvdata.monitor.MonitorRequester#monitorConnect(org.epics.pvdata.pv.Status, org.epics.pvdata.monitor.Monitor, org.epics.pvdata.pv.Structure)
   */
  @Override
  public void monitorConnect(Status status, Monitor monitor, Structure structure) {
    reportStatus("Failed to create monitor", status);

    if (status.isSuccess()) {
      // this.monitor = monitor;
      monitor.start();
    }
  }

  /* (non-Javadoc)
   * @see org.epics.pvdata.monitor.MonitorRequester#monitorEvent(org.epics.pvdata.monitor.Monitor)
   */
  @Override
  public void monitorEvent(Monitor monitor) {
    MonitorElement monitorElement;
    while ((monitorElement = monitor.poll()) != null) {
      // TODO combine bitSet, etc.... do we need to copy structure?
      processMessage(monitorElement.getPVStructure());
      monitor.release(monitorElement);
    }
  }

  /* (non-Javadoc)
   * @see org.epics.pvdata.monitor.MonitorRequester#unlisten(org.epics.pvdata.monitor.Monitor)
   */
  @Override
  public void unlisten(Monitor monitor) {
    // TODO Auto-generated method stub
  }

  @Override
  public String toString() {
    return "PVAChannelHandler [getChannelName()=" + getChannelName() + "]";
  }
}