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 }