View Javadoc

1   package de.desy.video.sw;
2   
3   import com.cosylab.gui.components.util.RunnerHelper;
4   
5   import de.desy.acop.transport.AccessMode;
6   import de.desy.acop.transport.ConnectionFailed;
7   import de.desy.tine.client.TLink;
8   import de.desy.tine.client.TLinkCallback;
9   import de.desy.tine.dataUtils.TDataType;
10  import de.desy.tine.definitions.TAccess;
11  import de.desy.tine.definitions.TMode;
12  import de.desy.tine.types.IMAGE;
13  import de.desy.tine.queryUtils.TPropertyQuery;
14  import de.desy.tine.queryUtils.TQuery;
15  import de.desy.tine.startup.TInitializer;
16  import de.desy.tine.startup.DefaultTInitializerFactory;
17  import de.desy.tine.startup.TInitializerFactory;
18  
19  /**
20  * <code>TineHandler</code> encapsulates and divides reception of VSv3 and VSv2 image streams 
21  * from displaying class ImageDisplayer. It implements probing of sources and reception, 
22  * prearrangement and forwarding of video image blobs from TINE to displaying part.
23  * 
24  * @author <a href="mailto:stefan.weisse@desy.de">Stefan Weisse</a>
25  * @version $Id: Templates.xml,v 1.10 2008/06/23 15:30:13 sweisse Exp $
26  *
27  */
28  public final class TineHandler implements TLinkCallback {
29  
30  	/** will get the prearranged images for further use. */
31  	private ImageDisplayer displayer;
32  	
33  	/** temporary storage of next successfully received video image from TINE, also
34  	 * used to wrap VSv2 data blob as VSv3 image. */
35  	private IMAGE m_srcImgHdr;
36  	
37  	/** VSv3 image bits buffer, may also contain VSv2 marshalled bits. */
38  	private byte[] m_srcBuf=null;
39  	
40  	/** VSv2 raw bits buffer. */
41  	private byte[] m_srcBufV2=null;
42  	
43  	/** TINE video data transmission link. */
44  	private TLink m_vidLink;
45  	/** is a video data transfer running? */
46  	private boolean m_bTransferRunning = false;
47  	/** should the ImageDisplayer be reset on next callback? */
48  	private boolean m_bResetDisplayer = false;
49  	/** is a VSv3 transfer ongoing? */
50  	private boolean m_bV3Transfer = true;
51  	/** initial, default transport length of VSv2, might change substantially.*/
52  	private int m_iTransportLengthV2 = 768*574*1+CVideoHeader2.HDRSIZE;
53  	
54  	/** defined (maximal) transport length for VSv3. */
55  	private final int m_iTransportLengthV3 = CVideoHeader3.TRANSPORT_LENGTH_V3;
56  	
57  	/** Constructor. The instance must always have a valid displayer-reference. */
58  	public TineHandler(ImageDisplayer displayer) {
59  		this.displayer = displayer;
60  	}
61  
62  	/**
63  	 * TINE callback implemented via TLinkCallback. It is called for each event on an 
64  	 * asynchronous connection between image source and TineHandler class. Events 
65  	 * can be new image delivery and error on image delivery property. 
66  	 * 
67  	 */
68  	public void callback(TLink link) {
69  		
70  		// accidental callback-call, let's get outta here
71  		if (!m_bTransferRunning) return;
72  		
73  		// is currently a VSv3 transfer?
74  		if (m_bV3Transfer == true)
75  		{
76  			// error code of 516 means for VSv3: currently no new image to be transferred
77  			// (e.g. timeout on schedule)
78  			if (link.getLinkStatus() == 516)
79  				return;
80  
81  			// any other error will give a debug print output to console and wait for next frame
82  			// TODO why no System.err ?
83  			if (link.getLinkStatus() != 0) {
84  				System.out.println("Link Error : code=" + link.getLinkStatus() + " msg=" + link.getLastError());
85  				return;
86  			}
87  		}
88  		else
89  		{
90  			// error code of 607 means for VSv2: currently no new image to be transferred
91  			// (e.g. timeout on schedule)
92  			if (link.getLinkStatus() == 607) // VSV2 no data to send
93  				return;
94  
95  			// any other error will give a debug print output to console and wait for next frame
96  			// TODO why no System.err ?
97  			if (link.getLinkStatus() != 0) {
98  				System.out.println("Link Error : code=" + link.getLinkStatus() + " msg=" + link.getLastError());
99  				return;
100 			}
101 
102 			//TDataType out = link.getOutputDataObject();
103 			//System.out.println("cc="+link.getLinkStatus()+" Completionlength="+out.dCompletionLength); 
104 			
105 			// a good VSv2 blob was received, so marshal it inside a VSv3 object
106 			// if no error happens on marshalling, pass further downstream
107 			CVideoHeader2 vidH2 = new CVideoHeader2();
108 			
109 			// do trick for camera port name, as it is not delivered with oldfashioned 
110 			// VideoSystem v2 header
111 			String vidFrom = "'V2:/"+link.getContext()+"/"+link.getDeviceServer()+"'";
112 			
113 			// "real" marshalling
114 			boolean ret = vidH2.packageV2BlobIntoIMAGE(vidFrom, m_srcBufV2, m_srcImgHdr);
115 			if (ret == false) // error while marshalling? error output and no passing along 
116 			{
117 				// DEBUG SW!
118 				System.out.println("TINE Callback: Videotype V2 conversion unsuccessful");
119 				return;
120 			}
121 			
122 		}
123 
124 		// on first call of callback method, the displayer is reset to allow proper 
125 		// initialisation of variables for following transfer
126 		if (m_bResetDisplayer == true) {
127 			displayer.resetForReceiving();
128 			m_bResetDisplayer = false;
129 		}
130 		
131 		// pass video image marshalled into CVideoHeader3 along to displayer component
132 		displayer.updateValue(new CVideoHeader3(m_srcImgHdr));
133 	}
134 	
135 	/**
136 	 *	Opens a one-time or permanent connection to an image source which can be a VSv3 
137 	 *  component with output interface like SGP and CoreProvider or a VSv2 server like 
138 	 *  GrabServer2. 
139 	 * 
140 	 * @param aAddress full qualified or incomplete TINE Address for VSv3 Output or VSv2 
141 	 * server (either /Context/Server/ or /Context/Server/Device or Server/ or Server/Device)
142 	 * @param mode TINE access mode (AccessMode.POLL usually) 
143 	 * @param accessRate TINE polling rate 
144 	 * @throws ConnectionFailed
145 	 * @throws ImageException
146 	 */
147 	
148 	public void openLink(String aAddress, AccessMode mode, int accessRate) throws Exception
149 	{
150 		String possiblyIncompleteTineDeviceName = aAddress;
151 		String tineAddress;
152 
153 		// check image source connection and type, throw connection otherwise
154 		m_bV3Transfer = probeTineV3andV2(possiblyIncompleteTineDeviceName); // SW! throw
155 
156 		// create proper VSv3 buffer for 
157 		// (a) receiving of VSv3 images
158 		// (b) passing along transformed VSv2 images to ImageDisplayer as VSv3 image
159 		if (m_srcBuf == null)
160 			m_srcBuf = new byte[m_iTransportLengthV3];
161    
162 		m_srcImgHdr = new IMAGE();
163 		m_srcImgHdr.setImageFrameBuffer(m_srcBuf);
164 		
165 		// reset the displayer component on first successful callback!
166 		m_bResetDisplayer = true;
167 
168 		// startup VSv3 transfer
169 		if (m_bV3Transfer == true)
170 		{
171 			// create proper VSv3 link
172 			tineAddress = getFullQualifiedV3Address( possiblyIncompleteTineDeviceName );
173 			TDataType img_dout = new TDataType(m_srcImgHdr);
174 			m_vidLink = new TLink(tineAddress,img_dout,null,TAccess.CA_READ);
175 		}
176 		else
177 		{
178 			// create proper VSv2 link
179 			tineAddress = getFullQualifiedV2Address( possiblyIncompleteTineDeviceName );
180 
181 			// SW! workaround: returned data got cut down to 192 bytes (WTF?!) 
182 			// SW! somehow if not +1 byte is asked
183 			// SW! but only if really full transport length is transferred by V2 server
184 			// SW! e.g. HFYU usually works, uncompressed transfers don't
185 			int v2Size = m_iTransportLengthV2+1;
186 			
187 			// adjust buffer if not right
188 			if (m_srcBufV2 == null || m_srcBufV2.length != v2Size)
189 			{
190 				m_srcBufV2 = new byte[v2Size]; 
191 			}
192 			
193 			TDataType img_dout = new TDataType(m_srcBufV2);
194 			m_vidLink = new TLink(tineAddress,img_dout,null,TAccess.CA_READ);
195 		}
196 
197 		// construct proper mode integer for attaching the link 
198 		short tMode;
199 		if (mode == AccessMode.POLL) {
200 			tMode = TMode.CM_POLL;			
201 		} else if (mode == AccessMode.POLL_NETWORK)	{
202 			tMode = TMode.CM_POLL | TMode.CM_MCAST;
203 		} else if (mode == AccessMode.READ) {
204 			tMode = TMode.CM_SINGLE;
205 		} else {
206 			throw new ConnectionFailed("Cannot establish a connection. AccessMode should be POLL or READ.", null);
207 		}
208 	    
209 		if (m_vidLink.attach(tMode, this, accessRate) < 0)
210 	    {
211 	    	m_vidLink.cancel();
212 	    	m_vidLink = null;
213     	  
214 	    	throw new ConnectionFailed("No permanent connection to server (\""+m_vidLink.getLastError()+"\".", null);
215 	    }
216 				
217 	    m_bTransferRunning = true;
218 	}
219 
220 	/**
221 	 * used to cancel a running transfer. Designed to work even in case no transfer was 
222 	 * started at all.
223 	 *
224 	 */
225 	public void closeLink() {
226 		if (m_vidLink != null) {
227 			m_vidLink.cancel();
228 			m_vidLink = null;
229 		}
230 		m_bTransferRunning = false;
231 	}
232 
233 	
234 	/** creates a full-qualified TINE VSv2 address out of a valid partial address.
235 	 * 
236 	 * 
237 	 * @param aPartialAddress either /Context/Server/ or /Context/Server/Device or
238 	 *        Server/ or Server/Device
239 	 * @return "/Context/Server/device_0/FRAME.GET" or "Server/device_0/FRAME.GET"
240 	 * 
241 	 */
242 	private String getFullQualifiedV2Address(String aPartialAddress)
243 	{
244 		String retval;
245 		
246 		retval = aPartialAddress;
247 		if (retval.endsWith("/") == true) retval +="device_0";
248 		retval += "/FRAME.GET";
249 	
250 		return retval;
251 	}
252 
253 	/** creates a full-qualified TINE VSv3 address out of a valid partial address.
254 	 * 
255 	 * @param aPartialAddress either /Context/Server/ or /Context/Server/Device or
256 	 *        Server/ or Server/Device
257 	 * @return "/Context/Server/Output/Frame.Sched" or "Server/Output/Frame.Sched"
258 	 * 
259 	 */
260 	private String getFullQualifiedV3Address(String aPartialAddress)
261 	{
262 		String retval;
263 		
264 		retval = aPartialAddress;
265 		if (retval.endsWith("/") == true) retval +="Output";
266 		retval += "/Frame.Sched";
267 		return retval;
268 	}
269 
270 	/** probes partial address passed whether there is a VSv3 connection or a VSv2 
271 	 * data transport connection behind that. VSv3 is prefered over VSv2 in case
272 	 * both are provided.<br><br> 
273 	 * <b>Note: </b>In case of VSv2 transport this function is very important because 
274 	 * it will adjust the VSv2 transport length to the VSv2 server's very own transport
275 	 * length.
276 	 * 
277 	 * @param aPartialAddress either /Context/Server/ or /Context/Server/Device or
278 	 *        Server/ or Server/Device
279 	 * @return 
280 	 * <ul>
281 	 * <li>true in case of VSv3 transport
282 	 * <li>false in case of VSv2 transport
283 	 * </ul> 
284 	 * @throws ConnectionFailed
285 	 * @throws ImageException
286 	 */
287 	private boolean probeTineV3andV2(String aPartialAddress) throws Exception {
288 
289 		// construct appropriate local variables
290 		byte[] framebits3 = new byte[CVideoHeader3.HDRSIZE];
291 		String addressVSV2 = getFullQualifiedV2Address(aPartialAddress);
292 		TDataType dout3 = new TDataType(framebits3);
293 		
294 		String devProp="Header";
295 		if (aPartialAddress.endsWith("/")) aPartialAddress += "Output";
296 
297 		// test VSv3 connection using simple single synchronous execute call
298 		// to acquire a VSv3 video header 
299 		TLink ref = new TLink(aPartialAddress, devProp, dout3, null, TAccess.CA_READ);
300 		int cc = ref.execute();
301 		if (cc != 0) 
302 		{
303 			// here: return code not equal to 0 (success) is interpreted as 
304 			// no VSv3 connection
305 			
306 			// check V2 from now on:
307 
308 			// prep: create proper settings for VSv2, split and recombine aPartialAddress,
309 			// which was adapted before to full VSv3
310 
311 			String context = "DEFAULT";
312 			String server = "";
313 			String device = "device_0";
314 			String property = "FRAME.GET";
315 			
316 			String[] list = aPartialAddress.split("/");
317 			int l = list.length;
318 				
319 			if (l>0)
320 			{
321 				if (list[0].compareTo("") == 0)
322 				{
323 					if ((list.length>1) && (list[1].compareTo("") != 0)) context = list[1];
324 					if ((list.length>2) && (list[2].compareTo("") != 0)) server = list[2];
325 				}
326 				else
327 				{
328 					server = list[0];
329 				}
330 			}
331 
332 			// first: get _real_ transport length of TINE v2:
333 			
334 			// will usually throw exception also in error case
335 			TPropertyQuery[] propList = TQuery.getPropertyInformation(context,server,device,property);
336 			
337 			// no VSv3 and also no VSv2 transport detected, so throw exception
338 			if (propList == null)
339 			{
340 				framebits3 = null;				
341 				throw new ConnectionFailed("Probing Address did not succeed. Transfer was cancelled.", null);
342 			}
343 
344 			// no VSv3 and also no VSv2 transport detected, so throw exception			
345 			if (propList.length<1) 
346 			{
347 				framebits3 = null;				
348 				throw new ConnectionFailed("Probing Address did not succeed. Transfer was cancelled.", null);
349 			}
350 
351 			// VSv2 transport is detected, now test whether it _works_ 
352 
353 			// create special byte blob array of queried transport length 
354 			m_iTransportLengthV2 = propList[0].prpSize;
355 			byte [] framebits2 = new byte[m_iTransportLengthV2+1];
356 			TDataType dout2 = new TDataType(framebits2);
357 			
358 			// get a single frame using synchronous method
359 			TLink ref1 = new TLink(addressVSV2, dout2, null, TAccess.CA_READ);
360 			int cc1 = ref1.execute();
361 			
362 			if ((cc1 == 0) || (cc1 == 607)) // success or currently_no_new_image 
363 			{
364 				// it is okay for us to have success (frame was send back) or
365 				// error code 607, which means that currently there is no new image
366 				// to download because both cases provide that also remote peer is
367 				// working good enough for us
368 				
369 				// cancel transfer, reset arrays
370 				ref1.cancel();
371 				framebits2 = null;
372 				framebits3 = null;				
373 				
374 				// return false to indicate that VSv2 was detected
375 				return false;
376 			}
377 			
378 			ref1.cancel();
379 			framebits2 = null;
380 			framebits3 = null;				
381 			
382 			// System.out.println("Error: get reference : " +
383 			// ref.getLinkStatus());
384 			// 517==RETCODE_NOFRAMEYET
385 			
386 			// bad VSv3, bad VSv2 -> throw Exception 
387 			throw new ConnectionFailed("Probing Address did not succeed. Transfer was cancelled.", null);
388 		} else {
389 			
390 			// good VSv3 connection, but check completionlength for array match,
391 			// if bad throw Exception
392 			
393 			ref.cancel();
394 			if (dout3.dCompletionLength != CVideoHeader3.HDRSIZE) {
395 				throw new ImageException("CF_IMAGE header is not "+CVideoHeader3.HDRSIZE+" bytes in size. Transfer was cancelled.");
396 			}
397 		}
398 
399 		// success case for VSv3 connection, cleanup, return true to indicate VSv3 
400 		// was chosen
401 		framebits3 = null;		
402 		return true;
403 	}
404 
405 	/** example main for TineHandler plus ImageDisplayer. */
406 	public static void main(String[] args) throws Exception 
407 	{
408 		// SW! Oct 01, 2008
409 		//DefaultTInitializerFactory.getInstance().getInitializer().setClnRcvBufferSize(131072);
410 		ImageDisplayer d = new ImageDisplayer();
411 		TineHandler handler = new TineHandler(d);
412 		handler.openLink("/TEST/SGP_IMM1", AccessMode.POLL, 1000);
413 		RunnerHelper.runComponent(d, 800,600);
414 	}
415 }