source: trunk/MagicSoft/Cosy/candrv/nodedrv.cc@ 1784

Last change on this file since 1784 was 1784, checked in by tbretz, 22 years ago
*** empty log message ***
File size: 17.8 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of Stesy, the MAGIC Steering System
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz <mailto:tbretz@uni-sw.gwdg.de>, 2001
19!
20! Copyright: MAGIC Software Development, 2000-2001
21!
22!
23\* ======================================================================== */
24
25///////////////////////////////////////////////////////////////////////
26//
27// NodeDrv
28//
29// Base class for a class describing the interface for the CAN nodes.
30//
31// to be overloaded:
32// virtual void Init()
33// virtual void StopDevice()
34// virtual void HandleSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, timeval_t *tv)
35// virtual void HandleSDOOK(WORD_t idx, BYTE_t subidx, timeval_t *tv)
36// virtual void HandleSDOError(LWORD_t data)
37// virtual void HandlePDO1(BYTE_t *data, timeval_t *tv)
38// virtual void HandlePDO2(BYTE_t *data, timeval_t *tv)
39// virtual void HandlePDO3(BYTE_t *data, timeval_t *tv)
40// virtual void HandlePDO4(BYTE_t *data, timeval_t *tv)
41// virtual bool Reboot();
42// virtual void CheckConnection();
43//
44///////////////////////////////////////////////////////////////////////
45#include "nodedrv.h"
46
47#include <iomanip.h>
48#include <iostream.h>
49
50#include <TTimer.h>
51
52#include "base/timer.h"
53#include "network.h"
54#include "MLogManip.h"
55
56ClassImp(NodeDrv);
57
58// --------------------------------------------------------------------------
59//
60// Constructor for one node. Sets the Node Id (<32) the logging stream
61// and the node name. The name is a name for debug output.
62//
63NodeDrv::NodeDrv(BYTE_t nodeid, const char *name, MLog &out) : Log(out), fNetwork(NULL), fId(32), fError(0), fIsZombie(kTRUE)
64{
65 if (nodeid>0x1f)
66 {
67 cout << "SetNode - Error: Only node Numbers < 32 are allowed"<< endl;
68 return;
69 }
70
71 fId = nodeid;
72
73 if (name)
74 fName = name;
75 else
76 {
77 fName = "Node#";
78 fName += (int)nodeid;
79 }
80
81 fTimerOn = kFALSE;
82 fTimeout = new TTimer(this, 100, kFALSE); // 100ms, asynchronous
83
84 lout << "- Node #" << (int)nodeid << " (" << name << ") initialized." << endl;
85
86}
87
88// --------------------------------------------------------------------------
89//
90// destructor
91//
92NodeDrv::~NodeDrv()
93{
94 fTimerOn = kFALSE;
95 delete fTimeout;
96}
97
98// --------------------------------------------------------------------------
99//
100// This should be called from a master or main thread to get a node out
101// of the Zombie-Status. Overload it by your needs.
102//
103bool NodeDrv::Reboot()
104{
105 fIsZombie = false;
106
107 Init();
108
109 return !fIsZombie;
110}
111
112// --------------------------------------------------------------------------
113//
114// Init device, sets the pointer to the whole network and enables
115// the Can messages to be passed through the interface:
116// PDO1 tx
117// PDO2 tx
118// PDO3 tx
119// PDO4 tx
120// SDO rx
121// SDO tx
122//
123bool NodeDrv::InitDevice(Network *net)
124{
125 fNetwork = net;
126
127 EnableCanMsg(kPDO1_TX);
128 EnableCanMsg(kPDO2_TX);
129 EnableCanMsg(kPDO3_TX);
130 EnableCanMsg(kPDO4_TX);
131 EnableCanMsg(kSDO_RX);
132 EnableCanMsg(kSDO_TX);
133 EnableCanMsg(kNodeguard);
134 EnableCanMsg(kEMERGENCY);
135
136 fIsZombie = kFALSE;
137
138 Init();
139
140 return !fIsZombie;
141}
142
143// --------------------------------------------------------------------------
144//
145// Print an "SDO idx/subidx set." from this device message.
146// This output is never redirected to the GUI
147//
148void NodeDrv::HandleSDOOK(WORD_t idx, BYTE_t subidx, timeval_t *tv)
149{
150 const Bool_t gui = lout.IsOutputDeviceEnabled(MLog::eGui);
151
152 if (gui)
153 lout << ddev(MLog::eGui);
154
155 lout << hex << setfill('0');
156 lout << "Node #" << (int)fId << ": Sdo=" << idx << "/" << (int)subidx << " set.";
157 lout << endl;
158
159 if (gui)
160 lout << edev(MLog::eGui);
161}
162
163// --------------------------------------------------------------------------
164//
165// Print an error message with the corresponding data from this device.
166//
167void NodeDrv::HandleSDOError(LWORD_t data)
168{
169 lout << "Nodedrv: SDO Error: Entry not found in dictionary (data=0x";
170 lout << hex << setfill('0') << setw(4) << data << ")";
171 lout << endl;
172}
173
174// --------------------------------------------------------------------------
175//
176// Prints the received SDo from this device
177//
178void NodeDrv::HandleSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, timeval_t *tv)
179{
180 cout << "SdoRx: Idx=0x"<< hex << idx << "/" << (int)subidx;
181 cout << ", val=0x" << val << endl;
182}
183
184// --------------------------------------------------------------------------
185//
186// Sends the given PDO1 through the network to this device
187// A PDO is carrying up to eight bytes of information.
188//
189// The message is not send if the node has the status Zombie.
190// In this case false is returned, otherwise true
191//
192bool NodeDrv::SendPDO1(BYTE_t data[8])
193{
194 if (!fIsZombie)
195 fNetwork->SendPDO1(fId, data);
196 return !fIsZombie;
197}
198
199// --------------------------------------------------------------------------
200//
201// Sends the given PDO2 through the network to this device
202// A PDO is carrying up to eight bytes of information.
203//
204// The message is not send if the node has the status Zombie.
205// In this case false is returned, otherwise true
206//
207bool NodeDrv::SendPDO2(BYTE_t data[8])
208{
209 if (!fIsZombie)
210 fNetwork->SendPDO2(fId, data);
211 return !fIsZombie;
212}
213
214// --------------------------------------------------------------------------
215//
216// Sends the given PDO1 through the network to this device
217// A PDO is carrying up to eight bytes of information.
218//
219// The message is not send if the node has the status Zombie.
220// In this case false is returned, otherwise true
221//
222bool NodeDrv::SendPDO1(BYTE_t m0=0, BYTE_t m1=0, BYTE_t m2=0, BYTE_t m3=0,
223 BYTE_t m4=0, BYTE_t m5=0, BYTE_t m6=0, BYTE_t m7=0)
224{
225 if (!fIsZombie)
226 fNetwork->SendPDO1(fId, m0, m1, m2, m3, m4, m5, m6, m7);
227 return !fIsZombie;
228}
229
230// --------------------------------------------------------------------------
231//
232// Sends the given PDO2 through the network to this device
233// A PDO is carrying up to eight bytes of information.
234//
235// The message is not send if the node has the status Zombie.
236// In this case false is returned, otherwise true
237//
238bool NodeDrv::SendPDO2(BYTE_t m0=0, BYTE_t m1=0, BYTE_t m2=0, BYTE_t m3=0,
239 BYTE_t m4=0, BYTE_t m5=0, BYTE_t m6=0, BYTE_t m7=0)
240{
241 if (!fIsZombie)
242 fNetwork->SendPDO2(fId, m0, m1, m2, m3, m4, m5, m6, m7);
243 return !fIsZombie;
244}
245
246// --------------------------------------------------------------------------
247//
248// Sends the given SDO through the network to this device
249// An SDO message contains
250// an address (this device)
251// an index of the dictionary entry to address
252// a subindex of this dictionary entry to access
253// and a value to set for this dictionary entry
254//
255// The message is not send if the node has the status Zombie.
256// In this case false is returned, otherwise true
257//
258bool NodeDrv::SendSDO(WORD_t idx, BYTE_t subidx, BYTE_t val, bool store)
259{
260 if (!fIsZombie)
261 fNetwork->SendSDO(fId, idx, subidx, val, store);
262 return !fIsZombie;
263}
264
265// --------------------------------------------------------------------------
266//
267// Sends the given SDO through the network to this device
268// An SDO message contains
269// an address (this device)
270// an index of the dictionary entry to address
271// a subindex of this dictionary entry to access
272// and a value to set for this dictionary entry
273//
274// The message is not send if the node has the status Zombie.
275// In this case false is returned, otherwise true
276//
277bool NodeDrv::SendSDO(WORD_t idx, BYTE_t subidx, WORD_t val, bool store)
278{
279 if (!fIsZombie)
280 fNetwork->SendSDO(fId, idx, subidx, val, store);
281 return !fIsZombie;
282}
283
284// --------------------------------------------------------------------------
285//
286// Sends the given SDO through the network to this device
287// An SDO message contains
288// an address (this device)
289// an index of the dictionary entry to address
290// a subindex of this dictionary entry to access
291// and a value to set for this dictionary entry
292//
293// The message is not send if the node has the status Zombie.
294// In this case false is returned, otherwise true
295//
296bool NodeDrv::SendSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, bool store)
297{
298 if (!fIsZombie)
299 fNetwork->SendSDO(fId, idx, subidx, val, store);
300 return !fIsZombie;
301}
302
303// --------------------------------------------------------------------------
304//
305// Sends the given SDO through the network to this device
306// An SDO message contains
307// an address (this device)
308// an index of the dictionary entry to address
309// a subindex of this dictionary entry to access
310// and a value to set for this dictionary entry
311//
312// The message is not send if the node has the status Zombie.
313// In this case false is returned, otherwise true
314//
315bool NodeDrv::SendSDO(WORD_t idx, BYTE_t val)
316{
317 if (!fIsZombie)
318 fNetwork->SendSDO(fId, idx, val, true);
319 return !fIsZombie;
320}
321
322// --------------------------------------------------------------------------
323//
324// Sends the given SDO through the network to this device
325// An SDO message contains
326// an address (this device)
327// an index of the dictionary entry to address
328// a subindex of this dictionary entry to access
329// and a value to set for this dictionary entry
330//
331// The message is not send if the node has the status Zombie.
332// In this case false is returned, otherwise true
333//
334bool NodeDrv::SendSDO(WORD_t idx, WORD_t val)
335{
336 if (!fIsZombie)
337 fNetwork->SendSDO(fId, idx, val, true);
338 return !fIsZombie;
339}
340
341// --------------------------------------------------------------------------
342//
343// Sends the given SDO through the network to this device
344// An SDO message contains
345// an address (this device)
346// an index of the dictionary entry to address
347// a subindex of this dictionary entry to access
348// and a value to set for this dictionary entry
349//
350// The message is not send if the node has the status Zombie.
351// In this case false is returned, otherwise true
352//
353bool NodeDrv::SendSDO(WORD_t idx, LWORD_t val)
354{
355 if (!fIsZombie)
356 fNetwork->SendSDO(fId, idx, val, true);
357 return !fIsZombie;
358}
359
360// --------------------------------------------------------------------------
361//
362// Request a SDO for a given idx/subidx
363// An SDO message contains
364// an address (this device)
365// an index of the dictionary entry to read
366// a subindex of this dictionary entry to access
367//
368// The message is not send if the node has the status Zombie.
369// In this case false is returned, otherwise true
370//
371bool NodeDrv::RequestSDO(WORD_t idx, BYTE_t subidx)
372{
373 if (!fIsZombie)
374 fNetwork->RequestSDO(fId, idx, subidx);
375 return !fIsZombie;
376}
377
378// --------------------------------------------------------------------------
379//
380// Send a NMT message (command) to this device
381//
382// The message is not send if the node has the status Zombie.
383// In this case false is returned, otherwise true
384//
385bool NodeDrv::SendNMT(BYTE_t cmd)
386{
387 if (!fIsZombie)
388 fNetwork->SendNMT(fId, cmd);
389 return !fIsZombie;
390}
391
392// --------------------------------------------------------------------------
393//
394// Send a Nodeguard message (command) to this device
395//
396void NodeDrv::SendNodeguard()
397{
398 fNetwork->SendNodeguard(fId);
399}
400
401// --------------------------------------------------------------------------
402//
403// Enable passthrough for the given functioncode of this device
404//
405void NodeDrv::EnableCanMsg(BYTE_t fcode)
406{
407 fNetwork->EnableCanMsg(fId, fcode, TRUE);
408}
409
410// --------------------------------------------------------------------------
411//
412// Wait a given timeout until the SDO with the given idx/subidx from
413// this device has been received.
414// You can stop waiting by StopWaitingForSDO.
415// Return false if waiting timed out.
416// If waiting timed out the node is set to status Zombie.
417//
418// If the node is already a zombie node, the message is deleted from the
419// queue and no waiting is done, false is returned..
420//
421bool NodeDrv::WaitForSdo(WORD_t idx, BYTE_t subidx, WORDS_t timeout)
422{
423 bool rc = fNetwork->WaitForSdo(fId, idx, subidx, fIsZombie?-1:timeout);
424
425 if (!rc)
426 {
427 lout << "NodeDrv::WaitForSdo: 0x" << hex << idx << "/" << dec << (int)subidx << " " << GetNodeName() << " --> ZOMBIE!" << endl;
428 SetZombie();
429 }
430/*
431 if (HasError())
432 {
433 lout << "NodeDrv::WaitForSdo: HasError 0x" << hex << idx << "/" << dec << (int)subidx << " " << GetNodeName() << " --> ZOMBIE!" << endl;
434 fIsZombie = kTRUE;
435 }
436*/
437 return fIsZombie ? false : rc;
438}
439
440/*
441void NodeDrv::WaitForSdos()
442{
443 while (fNetwork->WaitingForSdo(fId))
444 usleep(1);
445}
446*/
447
448// --------------------------------------------------------------------------
449//
450// Waits until the next Pdo1 from this device has been received
451//
452void NodeDrv::WaitForNextPdo1()
453{
454 fNetwork->WaitForNextPdo1(fId);
455}
456
457// --------------------------------------------------------------------------
458//
459// Waits until the next Pdo2 from this device has been received
460//
461void NodeDrv::WaitForNextPdo2()
462{
463 fNetwork->WaitForNextPdo2(fId);
464}
465
466// --------------------------------------------------------------------------
467//
468// Waits until the next Pdo3 from this device has been received
469//
470void NodeDrv::WaitForNextPdo3()
471{
472 fNetwork->WaitForNextPdo3(fId);
473}
474
475// --------------------------------------------------------------------------
476//
477// Waits until the next Pdo4 from this device has been received
478//
479void NodeDrv::WaitForNextPdo4()
480{
481 fNetwork->WaitForNextPdo4(fId);
482}
483
484// --------------------------------------------------------------------------
485//
486// Start the standard CANopen guarding of the device.
487// While ms is the guard time in millisec. This is the time between
488// two requests for a Nodeguard message.
489// ltf is the LifeTimeFactor. This means how often it is checked, that at
490// least one Nodeguard message was answered.
491//
492void NodeDrv::StartGuarding()
493{
494 if (fTimerOn)
495 return;
496
497 SendNodeguard();
498
499 fTimerOn = kTRUE;
500 fTimeout->SetTime(fGuardTime);
501 fTimeout->Reset();
502 fTimeout->TurnOn();
503 //fTimeout->Start(fGuardTime, kTRUE);
504
505 lout << "- " << GetNodeName() << ": Guarding (" << dec;
506 lout << fLifeTimeFactor << "*" << fGuardTime << "ms) started." << endl;
507}
508
509void NodeDrv::StartGuarding(Int_t ms, Int_t ltf)
510{
511 if (fTimerOn)
512 {
513 lout << "- " << GetNodeName() << ": ERROR - Guarding already started." << endl;
514 return;
515 }
516 fGuardTime = ms;
517 fLifeTimeFactor = ltf;
518
519 StartGuarding();
520}
521
522void NodeDrv::StopGuarding()
523{
524 fTimeout->TurnOff();
525 fTimerOn = kFALSE;
526
527 lout << "- " << GetNodeName() << ": Guarding stopped." << endl;
528}
529
530// --------------------------------------------------------------------------
531//
532// Handle the Nodeguard-Timer Event.
533// It checks whether the node timed out. If it timed out it is set to
534// the Zombie state.
535// A new Nodeguard request is send and a new timer event is triggered.
536//
537Bool_t NodeDrv::HandleTimer(TTimer *t)
538{
539 //
540 // WARNING:
541 // It seems, that you should never access ANY output from
542 // here. Neither the GUI, nor COUT. This can result in
543 // 'unexpected async reply'
544 //
545
546 /*
547 Fons:
548 -----
549
550 timers never trigger at the same time or when in a TTimer::Notify.
551 Little explanation:
552
553 - there are two types of timers synchronous and a-synchronous.
554 - synchronous timers are only handled via the ROOT eventloop
555 (see TUnixSystem::DispatchOneEvent()). If there are no mouse/keyboard
556 events then the synchronous timer queue is checked. So if the processing
557 of a mouse/keyboard event takes a long time synchronous timers are not
558 called for a while. To prevent this from happening one can call in long
559 procedures gSystem->ProcessEvents(). The system schedules only the
560 next timer in the queue when the current one's Notify() has finished.
561 - a-synchronous timers are triggered via SIGALARM, i.e. the program is
562 interupted and execution jumps to the Notify() function. When the
563 notify is finished the next a-sync timer is scheduled and the system
564 resumes from the place where it was initially interrupted. One of the
565 things to remember when using a-sync timers is don't make any graphics
566 calls in them. X11 is not re-entrant and it might be that the SIGALARM
567 signal interrupted the system while being in X11. A-sync timers are best
568 used to set flags that you can test at a convenient and controlled
569 time.
570 */
571 if (fIsZombie)
572 return kTRUE;
573
574 Timer time;
575 Double_t now = time.Now();
576 if (now > fTimeoutTime)
577 {
578 //cout << "ERROR - " << GetNodeName() << " didn't respond in timeout window." << endl;
579 //lout << "ERROR - " << GetNodeName() << " didn't respond in timeout window." << endl;
580 //cout << dec << "+" << (int)GetId() << ": Handle: " << fmod(now, 500) << endl;
581 //cout << dec << "+" << (int)GetId() << ": Handle: " << fmod(fTimeoutTime, 500) << endl;
582 //cout << fGuardTime << endl;
583 fIsZombie = true;
584 //SetZombie();
585
586 return kTRUE;
587 }
588
589 SendNodeguard();
590
591 return kTRUE;
592}
593
594// --------------------------------------------------------------------------
595//
596// Set the timeout timer to the time the event was received plus the
597// guard time times lifetimefactor.
598//
599void NodeDrv::HandleNodeguard(timeval_t *tv)
600{
601 Timer t(tv);
602 fTimeoutTime = t + (fGuardTime*fLifeTimeFactor/1000.);
603}
604
605void NodeDrv::SetZombie()
606{
607 fIsZombie = true;
608 StopGuarding();
609}
Note: See TracBrowser for help on using the repository browser.