View Javadoc

1   /*
2    * Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
3    * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
4    *
5    * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
6    * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
7    * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
8    * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
9    * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
10   * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
11   * THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
12   * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
13   * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
14   * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
15   * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
16   * OR MODIFICATIONS.
17   * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
18   * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
19   * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
20   * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
21   */
22  
23  package de.desy.acop.transport.adapters;
24  
25  import java.beans.Beans;
26  import java.beans.PropertyVetoException;
27  import java.util.Arrays;
28  import java.util.HashMap;
29  import java.util.Map;
30  
31  import com.cosylab.gui.displayers.CommonDisplayer;
32  import com.cosylab.gui.displayers.DataConsumer;
33  import com.cosylab.gui.displayers.DataSource;
34  import com.cosylab.gui.displayers.DataSourceSupport;
35  import com.cosylab.gui.displayers.DataState;
36  import com.cosylab.gui.displayers.Displayer;
37  import com.cosylab.gui.displayers.DoubleSeqConsumer;
38  import com.cosylab.gui.displayers.StringConsumer;
39  import com.cosylab.gui.displayers.StringSeqConsumer;
40  import com.cosylab.util.ObjectList;
41  
42  import de.desy.acop.transport.AcopTransport;
43  import de.desy.acop.transport.AcopTransportEvent;
44  import de.desy.acop.transport.AcopTransportListener;
45  import de.desy.acop.transport.ConnectionFailed;
46  import de.desy.acop.transport.ConnectionParameters;
47  import de.desy.tine.definitions.TArrayType;
48  import de.desy.tine.definitions.TErrorList;
49  import de.desy.tine.definitions.TFormat;
50  import de.desy.tine.queryUtils.TQuery;
51  import de.desy.tine.types.NAME16;
52  import de.desy.tine.types.NAME32;
53  import de.desy.tine.types.NAME64;
54  import de.desy.tine.types.USTRING;
55  
56  
57  /**
58   * <code>AcopTransportDataSource</code> is a common DataSource for all TINE based
59   * connections. This class connects to the TINE CS via the <code>AcopTransport</code>.
60   * This data source works as a connection monitor receiving data as specified
61   * by the supplied <code>ConnectionParameters</code>.
62   * 
63   * @author Igor Kriznar (igor.kriznarATcosylab.com)
64   *
65   */
66  public class AcopTransportDataSource extends DataSourceSupport implements AcopTransportListener {
67  
68  	private static final long serialVersionUID = -6371576395329401933L;
69  	int useCount = 0;
70  	protected ObjectList dcon= new ObjectList(DoubleSeqConsumer.class);
71  	protected ObjectList scon = new ObjectList(StringConsumer.class);
72  	protected ObjectList sseqcon = new ObjectList(StringSeqConsumer.class);
73  	private AcopTransport transport;
74  	private int id=-1;
75  	protected double[] data;
76  	protected String[] objectData;
77  	private Object inData;
78  	private long timestamp;
79  	private Map<String,Object> characteristics;
80  	private ConnectionParameters param;
81  	private ReverseConsumer reverseConsumer;
82  	private TFormat dataFormat;
83  	private int errorId=-1;
84  	private DataState dataState = new DataState(DataState.UNDEFINED);
85  
86  	/**
87  	 * Creates new instance of AcopTransportDataSource.
88  	 */
89  	@SuppressWarnings("unchecked")
90  	public AcopTransportDataSource() {
91  		super(new Class[]{DoubleSeqConsumer.class});
92  		transport= new AcopTransport();
93          transport.addAcopTransportListener(this);
94  	}
95  	
96  	/* (non-Javadoc)
97  	 * @see com.cosylab.gui.displayers.DataSourceSupport#addConsumer(com.cosylab.gui.displayers.DataConsumer)
98  	 */
99  	@Override
100 	public void addConsumer(DataConsumer consumer) throws PropertyVetoException {
101 		DataConsumer dc = consumer.getDefaultDataConsumer();
102 		
103 		if (dc instanceof StringConsumer) {
104 			scon.add(dc);
105 			super.addConsumer(consumer);
106 			
107 		} else if (dc instanceof StringSeqConsumer) {
108 			sseqcon.add(dc);
109 			super.addConsumer(consumer);
110 			
111 		} else {
112 			dc = consumer.getDataConsumer(DoubleSeqConsumer.class);
113 			
114 			if (dc==null) {
115 				return;
116 			}
117 			dcon.add(dc);
118 			super.addConsumer(consumer);
119 			
120 		}
121 		
122 		
123 		try {
124 			Map<String, Object> ch = null;
125 			
126 			if (!Beans.isDesignTime()) {
127 				ch= getCharacteristics();
128 			} else if (param != null){
129 				ch = new HashMap<String, Object>();
130 				ch.put(CommonDisplayer.C_DISPLAY_NAME,param.getRemoteName());	
131 			}
132 			if (ch!=null) {
133 				consumer.setCharacteristics(ch);
134 			}
135 		} catch (ConnectionFailed e) {
136 			e.printStackTrace();
137 		}
138 		
139 		if (consumer instanceof DataSource) {
140 			if (dataFormat.getValue() != TFormat.CF_USTRING &&
141 					dataFormat.getValue() != TFormat.CF_NAME8 &&
142 					dataFormat.getValue() != TFormat.CF_NAME16 &&
143 					dataFormat.getValue() != TFormat.CF_NAME32 &&
144 					dataFormat.getValue() != TFormat.CF_NAME64) {
145 				((DataSource)consumer).addConsumer(getReverseConsumer());
146 			} else {
147 				Map<String, Object> ch = new HashMap<String, Object>();
148 				ch.put(Displayer.C_EDITABLE, false);
149 				consumer.setCharacteristics(ch);
150 			}
151 		}		
152 		
153 		consumer.updateDataState(dataState);
154 	}	
155 	
156 	/**
157 	 * Returns the characteristics that belong to the property specified by
158 	 * the ConnectionParameters attached to this data source.
159 	 * 
160 	 * @return a Map of all characteristics, where keys are the name of the
161 	 * 			characteristics and values are characteristics
162 	 * @throws ConnectionFailed if property information query failed
163 	 * @see DataSourceUtilities#getCharacteristics(ConnectionParameters)
164 	 */
165 	public Map<String,Object> getCharacteristics() throws ConnectionFailed {
166 		if (characteristics == null && param!=null) {
167 			characteristics = DataSourceUtilities.getCharacteristics(param);		
168 		}
169 		return characteristics;
170 	}
171 
172 	/**
173 	 * Returns the transport used by this data source.
174 	 * 
175 	 * @return the transport
176 	 */
177 	public AcopTransport getTransport() {
178 		return transport;
179 	}
180 	
181 	/**
182 	 * Returns the <code>ConnectionParameters</code> attached to this data source.
183 	 * 
184 	 * @return the connection parameters
185 	 */
186 	public ConnectionParameters getConnectionParameters() {
187 		return param;
188 	}
189 
190 	/**
191 	 * Connects this data source to the TINE channel.
192 	 * 
193 	 * @param conn parameters specifiyng the connection channel
194 	 * @throws ConnectionFailed if remote connection failed
195 	 */
196 	public void connect(ConnectionParameters conn) throws ConnectionFailed {
197 		param=conn;
198 		
199 		transport.setConnectionParameters(conn);
200 		int size = conn.getPropertySize();
201 		//TODO why this block??
202 		if ((size == ConnectionParameters.ACTUAL_PROPERTY_SIZE ||
203 				size == ConnectionParameters.AUTO_PROPERTY_SIZE) && 
204 				(Integer)getCharacteristics().get(CommonDisplayer.C_SEQUENCE_LENGTH) > 1 &&
205 				!((TArrayType)getCharacteristics().get("arrayType")).isChannel()) {
206 			size = (Integer)getCharacteristics().get(CommonDisplayer.C_SEQUENCE_LENGTH);
207 		}
208 		if (size == ConnectionParameters.ACTUAL_PROPERTY_SIZE ||
209 				size == ConnectionParameters.AUTO_PROPERTY_SIZE) {
210 			if (!param.getDeviceName().contains("*")) {
211 				size= (Integer)getCharacteristics().get(CommonDisplayer.C_SEQUENCE_LENGTH);
212 			} else {
213 				String[] names = TQuery.getDeviceNames(conn.getDeviceContext(), conn.getDeviceGroup());
214 				size = names.length;
215 			}
216 		} 
217 		if (param.getDeviceProperty().contains("*")) {
218 			dataFormat = TFormat.valueOf(TFormat.CF_USTRING);
219 		} else {
220 			dataFormat = (TFormat)getCharacteristics().get("dataFormat");
221 		}
222 		inData= DataSourceUtilities.getDataObject(dataFormat,size);
223 					
224 		if (inData instanceof double[]) {
225 			data= (double[])inData;
226 		} else if (inData instanceof USTRING[] || inData instanceof NAME64[] ||
227 				inData instanceof NAME32[] || inData instanceof NAME16[]) {
228 			objectData= new String[size];		
229 		} else {
230 			data= new double[size];
231 		}
232 		
233 		if (!Beans.isDesignTime()) {
234 			id = transport.attachLink(inData, conn.getDynamicParameters());
235 			if (id < 0) {
236 				updateDataState(new DataState(DataState.ERROR));
237 			}
238 		}
239 		updateCharacteristics();
240 	}
241 	
242 	/**
243 	 * Disconnects this data source from the remote TINE channel.
244 	 *
245 	 */
246 	public void disconnect() {
247 		if (!Beans.isDesignTime()) {
248 			transport.closeLink(id);
249 			clear();
250 			if (getReverseConsumer() != null)
251 				getReverseConsumer().release();
252 	    	reverseConsumer=null;
253 		}
254 		clear();
255 	}
256 	
257 	/*
258 	 * (non-Javadoc)
259 	 * @see de.desy.acop.transport.AcopTransportListener#dataEventReceived(de.desy.acop.transport.AcopTransportEvent)
260 	 */
261     public void dataEventReceived(AcopTransportEvent e)
262     {
263       int id = e.getLinkId();
264       if (id != this.id) 
265     	  return;
266       int error = e.getLinkError();
267       timestamp = DataSourceUtilities.toUTC(transport.getDataTimeStamp());
268 
269       if (error!=errorId) {
270     	  errorId=error;
271     	 
272     	  if (error==0) {
273     		  dataState= new DataState(DataState.NORMAL,timestamp,TErrorList.toString(error));
274     	  } else {
275     		  dataState= new DataState(DataState.ERROR,timestamp,TErrorList.toString(error));
276     	  }
277           updateDataState(dataState);
278           
279       }
280       
281       updateConsumers();   
282     }
283     
284     private void updateConsumers() {
285     	//System.out.println(">> "+Arrays.toString(data));
286     	if (dataFormat.getValue() == TFormat.CF_USTRING ||
287     			dataFormat.getValue() == TFormat.CF_NAME8 ||
288     			dataFormat.getValue() == TFormat.CF_NAME16 ||
289     			dataFormat.getValue() == TFormat.CF_NAME32 ||
290     			dataFormat.getValue() == TFormat.CF_NAME64) {
291     		objectData = DataSourceUtilities.extractData(dataFormat,inData,objectData);
292     		StringConsumer[] sc= (StringConsumer[])scon.toArray();
293     		
294     		String data = "";
295     		if (objectData != null) {
296     			if (objectData.length == 1) {
297     				data = objectData[0];
298     			} else if (objectData.length > 1) {
299     				data = Arrays.toString(objectData);
300     			}
301     		}
302     			
303         	for (int i = 0; i < sc.length; i++) {
304     			try {
305     				sc[i].updateValue(timestamp,data);
306     			} catch (Exception e) {
307     				e.printStackTrace();
308     			}
309     		}
310         	
311         	StringSeqConsumer[] ssc= (StringSeqConsumer[])sseqcon.toArray();
312         	for (int i = 0; i < ssc.length; i++) {
313     			try {
314     				ssc[i].updateValue(timestamp,objectData);
315     			} catch (Exception e) {
316     				e.printStackTrace();
317     			}
318     		}
319     	} else {
320     		data= DataSourceUtilities.extractData(dataFormat,inData,data);
321     		DoubleSeqConsumer[] dsc= (DoubleSeqConsumer[])dcon.toArray();
322         	for (int i = 0; i < dsc.length; i++) {
323     			try {
324     				dsc[i].updateValue(timestamp,data);
325     			} catch (Exception e) {
326     				e.printStackTrace();
327     			}
328     		}
329         	StringConsumer[] sc= (StringConsumer[])scon.toArray();
330         	for (int i = 0; i < sc.length; i++) {
331     			try {
332     				sc[i].updateValue(timestamp,Arrays.toString(data));
333     			} catch (Exception e) {
334     				e.printStackTrace();
335     			}
336     		}
337         	
338         	StringSeqConsumer[] ssc= (StringSeqConsumer[])sseqcon.toArray();
339         	for (int i = 0; i < ssc.length; i++) {
340     			try {
341     				ssc[i].updateValue(timestamp,DataSourceUtilities.toStringData(data));
342     			} catch (Exception e) {
343     				e.printStackTrace();
344     			}
345     		}
346     	}
347     	
348     }
349 
350     private void updateCharacteristics() {
351 		try {
352 			Map ch= getCharacteristics();
353 			DataConsumer[] dsc= (DataConsumer[])dcon.toArray();
354 			for (int i = 0; i < dsc.length; i++) {
355 				try {
356 					dsc[i].setCharacteristics(ch);
357 				} catch (Exception e) {
358 					e.printStackTrace();
359 				}
360 			}
361 		} catch (ConnectionFailed e) {
362 			e.printStackTrace();
363 		}
364     }
365     
366     /**
367      * Returns the consumer that is attached to the displayer and enables setting
368      * value on the channel specified by the ConnectionParameters of this data source.
369      * 
370      * @return
371      */
372     protected ReverseConsumer getReverseConsumer() {
373     	if (reverseConsumer == null) {
374     		if (data != null)
375     			reverseConsumer = new ReverseConsumer(param,transport,data.length);
376 		}
377 
378 		return reverseConsumer;
379     }
380     
381     /* (non-Javadoc)
382      * @see com.cosylab.gui.displayers.DataSourceSupport#clear()
383      */
384     @Override
385     public void clear() {
386     	DataConsumer[] cc= getConsumers();
387     	for (DataConsumer c:cc) {
388     		if (c instanceof DataSource) {
389     			((DataSource)c).removeConsumer(getReverseConsumer());
390     		}
391     	}
392     	super.clear();
393     }
394 
395 	
396 	/**
397 	 * Returns the format.
398 	 * @return Returns the format.
399 	 */
400     protected TFormat getDataFormat() {
401 		return dataFormat;
402 	}
403 
404 	
405 	/**
406 	 * Sets new format.
407 	 * @param format The format to set.
408 	 */
409 	protected void setDataFormat(TFormat format) {
410 		this.dataFormat = format;
411 	}
412 	
413 	/**
414 	 * Updates DataState on all consumers.
415 	 * 
416 	 * @param state new data state
417 	 */
418 	private void updateDataState(DataState state) {
419 		DataConsumer[] delegates = getConsumers();
420 	
421 		for (int i = 0; i < delegates.length; i++) {
422 			try {
423 				delegates[i].updateDataState(state);
424 			} catch (Exception e) {
425 				e.printStackTrace();
426 			}
427 		}
428 	}
429 	
430 	/**
431 	 * Removes the <code>DataConsumer</code> from this data source. 
432 	 */
433 	public void removeConsumer(DataConsumer consumer) {
434 		super.removeConsumer(consumer);
435 		dcon.remove(consumer);
436 		scon.remove(consumer);
437 		sseqcon.remove(consumer);
438 		if (consumer instanceof DataSource && 
439 				(dataFormat.getValue() != TFormat.CF_USTRING ||
440 				 dataFormat.getValue() != TFormat.CF_NAME8 ||
441 				 dataFormat.getValue() != TFormat.CF_NAME16 ||
442 				 dataFormat.getValue() != TFormat.CF_NAME32 ||
443 				 dataFormat.getValue() != TFormat.CF_NAME64)) {
444 			((DataSource)consumer).removeConsumer(getReverseConsumer());
445 		} 
446 		
447 	}
448 	
449 	/**
450 	 * Returns the length of data received from the remote channel.
451 	 * 
452 	 * @return
453 	 */
454 	protected int getDataLength() {
455 		return data.length;
456 	}
457 
458 }