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

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