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

Last change on this file since 1959 was 1959, checked in by tbretz, 22 years ago
*** empty log message ***
File size: 18.2 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 #" << dec << (int)fId << ": Sdo=" << hex << 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
503 Timer t;
504 fTimeoutTime = t.Now() + (fGuardTime*fLifeTimeFactor/1000.);
505 //cout << GetNodeName() << ": " << fmod(fTimeoutTime*10000, 10000)/10 << endl;
506
507 fTimeout->TurnOn();
508 //fTimeout->Start(fGuardTime, kTRUE);
509
510 lout << "- " << GetNodeName() << ": Guarding (" << dec;
511 lout << fLifeTimeFactor << "*" << fGuardTime << "ms) started." << endl;
512}
513
514void NodeDrv::StartGuarding(Int_t ms, Int_t ltf)
515{
516 if (fTimerOn)
517 {
518 lout << "- " << GetNodeName() << ": ERROR - Guarding already started." << endl;
519 return;
520 }
521 fGuardTime = ms;
522 fLifeTimeFactor = ltf;
523
524 StartGuarding();
525}
526
527void NodeDrv::StopGuarding()
528{
529 if (!fTimerOn)
530 return;
531
532 fTimeout->TurnOff();
533 fTimerOn = kFALSE;
534
535 lout << "- " << GetNodeName() << ": Guarding stopped." << endl;
536}
537
538// --------------------------------------------------------------------------
539//
540// Handle the Nodeguard-Timer Event.
541// It checks whether the node timed out. If it timed out it is set to
542// the Zombie state.
543// A new Nodeguard request is send and a new timer event is triggered.
544//
545Bool_t NodeDrv::HandleTimer(TTimer *t)
546{
547 //
548 // WARNING:
549 // It seems, that you should never access ANY output from
550 // here. Neither the GUI, nor COUT. This can result in
551 // 'unexpected async reply'
552 //
553
554 /*
555 Fons:
556 -----
557
558 timers never trigger at the same time or when in a TTimer::Notify.
559 Little explanation:
560
561 - there are two types of timers synchronous and a-synchronous.
562 - synchronous timers are only handled via the ROOT eventloop
563 (see TUnixSystem::DispatchOneEvent()). If there are no mouse/keyboard
564 events then the synchronous timer queue is checked. So if the processing
565 of a mouse/keyboard event takes a long time synchronous timers are not
566 called for a while. To prevent this from happening one can call in long
567 procedures gSystem->ProcessEvents(). The system schedules only the
568 next timer in the queue when the current one's Notify() has finished.
569 - a-synchronous timers are triggered via SIGALARM, i.e. the program is
570 interupted and execution jumps to the Notify() function. When the
571 notify is finished the next a-sync timer is scheduled and the system
572 resumes from the place where it was initially interrupted. One of the
573 things to remember when using a-sync timers is don't make any graphics
574 calls in them. X11 is not re-entrant and it might be that the SIGALARM
575 signal interrupted the system while being in X11. A-sync timers are best
576 used to set flags that you can test at a convenient and controlled
577 time.
578 */
579 if (fIsZombie)
580 return kTRUE;
581
582 Timer time;
583 Double_t now = time.Now();
584 if (now > fTimeoutTime)
585 {
586 cout << GetNodeName() << ": " << "==out==> " << fmod(now*1000, 10000)/10 << " > " << fmod(fTimeoutTime*10000, 10000)/10 << endl;
587 //cout << "ERROR - " << GetNodeName() << " didn't respond in timeout window." << endl;
588 //lout << "ERROR - " << GetNodeName() << " didn't respond in timeout window." << endl;
589 //cout << dec << "+" << (int)GetId() << ": Handle: " << fmod(now, 500) << endl;
590 //cout << dec << "+" << (int)GetId() << ": Handle: " << fmod(fTimeoutTime, 500) << endl;
591 //cout << fGuardTime << endl;
592 fIsZombie = true;
593 //SetZombie();
594
595 return kTRUE;
596 }
597
598 SendNodeguard();
599
600 return kTRUE;
601}
602
603// --------------------------------------------------------------------------
604//
605// Set the timeout timer to the time the event was received plus the
606// guard time times lifetimefactor.
607//
608void NodeDrv::HandleNodeguard(timeval_t *tv)
609{
610 Timer t(tv);
611 fTimeoutTime = t + (fGuardTime*fLifeTimeFactor/1000.);
612 //cout << GetNodeName() << ": " << fmod(fTimeoutTime*10000, 10000)/10 << endl;
613}
614
615void NodeDrv::SetZombie()
616{
617 fIsZombie = true;
618 StopGuarding();
619}
Note: See TracBrowser for help on using the repository browser.