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

Last change on this file since 3146 was 2615, checked in by tbretz, 21 years ago
*** empty log message ***
File size: 16.9 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 "MTime.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), fGuard(NULL)
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 lout << "- Node #" << (int)nodeid << " (" << name << ") initialized." << endl;
82
83}
84
85// --------------------------------------------------------------------------
86//
87// destructor
88//
89NodeDrv::~NodeDrv()
90{
91}
92
93// --------------------------------------------------------------------------
94//
95// This should be called from a master or main thread to get a node out
96// of the Zombie-Status. Overload it by your needs.
97//
98bool NodeDrv::Reboot()
99{
100 fIsZombie = false;
101
102 Init();
103
104 return !fIsZombie;
105}
106
107// --------------------------------------------------------------------------
108//
109// Init device, sets the pointer to the whole network and enables
110// the Can messages to be passed through the interface:
111// PDO1 tx
112// PDO2 tx
113// PDO3 tx
114// PDO4 tx
115// SDO rx
116// SDO tx
117//
118bool NodeDrv::InitDevice(Network *net)
119{
120 fNetwork = net;
121
122 EnableCanMsg(kPDO1_TX);
123 EnableCanMsg(kPDO2_TX);
124 EnableCanMsg(kPDO3_TX);
125 EnableCanMsg(kPDO4_TX);
126 EnableCanMsg(kSDO_RX);
127 EnableCanMsg(kSDO_TX);
128 EnableCanMsg(kNodeguard);
129 EnableCanMsg(kEMERGENCY);
130
131 fIsZombie = kFALSE;
132
133 Init();
134
135 return !fIsZombie;
136}
137
138// --------------------------------------------------------------------------
139//
140// Print an "SDO idx/subidx set." from this device message.
141// This output is never redirected to the GUI.
142// In standard CANOpen operation data is meaningless (we are using
143// it in the 'non-standard' CANOpen communication with the MACS)
144//
145void NodeDrv::HandleSDOOK(WORD_t idx, BYTE_t subidx, LWORD_t data, timeval_t *tv)
146{
147 const Bool_t gui = lout.IsOutputDeviceEnabled(MLog::eGui);
148
149 if (gui)
150 lout << ddev(MLog::eGui);
151
152 lout << hex << setfill('0');
153 lout << "Node #" << dec << (int)fId << ": Sdo=" << hex << idx << "/" << (int)subidx << " set.";
154 lout << endl;
155
156 if (gui)
157 lout << edev(MLog::eGui);
158}
159
160// --------------------------------------------------------------------------
161//
162// Print an error message with the corresponding data from this device.
163//
164void NodeDrv::HandleSDOError(LWORD_t data)
165{
166 lout << "Nodedrv: SDO Error: Entry not found in dictionary (data=0x";
167 lout << hex << setfill('0') << setw(4) << data << ")";
168 lout << endl;
169}
170
171// --------------------------------------------------------------------------
172//
173// Prints the received SDo from this device
174//
175void NodeDrv::HandleSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, timeval_t *tv)
176{
177 cout << "SdoRx: Idx=0x"<< hex << idx << "/" << (int)subidx;
178 cout << ", val=0x" << val << endl;
179}
180
181// --------------------------------------------------------------------------
182//
183// Sends the given PDO1 through the network to this device
184// A PDO is carrying up to eight bytes of information.
185//
186// The message is not send if the node has the status Zombie.
187// In this case false is returned, otherwise true
188//
189bool NodeDrv::SendPDO1(BYTE_t data[8])
190{
191 if (!fIsZombie)
192 fNetwork->SendPDO1(fId, data);
193 return !fIsZombie;
194}
195
196// --------------------------------------------------------------------------
197//
198// Sends the given PDO2 through the network to this device
199// A PDO is carrying up to eight bytes of information.
200//
201// The message is not send if the node has the status Zombie.
202// In this case false is returned, otherwise true
203//
204bool NodeDrv::SendPDO2(BYTE_t data[8])
205{
206 if (!fIsZombie)
207 fNetwork->SendPDO2(fId, data);
208 return !fIsZombie;
209}
210
211// --------------------------------------------------------------------------
212//
213// Sends the given PDO1 through the network to this device
214// A PDO is carrying up to eight bytes of information.
215//
216// The message is not send if the node has the status Zombie.
217// In this case false is returned, otherwise true
218//
219bool NodeDrv::SendPDO1(BYTE_t m0=0, BYTE_t m1=0, BYTE_t m2=0, BYTE_t m3=0,
220 BYTE_t m4=0, BYTE_t m5=0, BYTE_t m6=0, BYTE_t m7=0)
221{
222 if (!fIsZombie)
223 fNetwork->SendPDO1(fId, m0, m1, m2, m3, m4, m5, m6, m7);
224 return !fIsZombie;
225}
226
227// --------------------------------------------------------------------------
228//
229// Sends the given PDO2 through the network to this device
230// A PDO is carrying up to eight bytes of information.
231//
232// The message is not send if the node has the status Zombie.
233// In this case false is returned, otherwise true
234//
235bool NodeDrv::SendPDO2(BYTE_t m0=0, BYTE_t m1=0, BYTE_t m2=0, BYTE_t m3=0,
236 BYTE_t m4=0, BYTE_t m5=0, BYTE_t m6=0, BYTE_t m7=0)
237{
238 if (!fIsZombie)
239 fNetwork->SendPDO2(fId, m0, m1, m2, m3, m4, m5, m6, m7);
240 return !fIsZombie;
241}
242
243// --------------------------------------------------------------------------
244//
245// Sends the given SDO through the network to this device
246// An SDO message contains
247// an address (this device)
248// an index of the dictionary entry to address
249// a subindex of this dictionary entry to access
250// and a value to set for this dictionary entry
251//
252// The message is not send if the node has the status Zombie.
253// In this case false is returned, otherwise true
254//
255bool NodeDrv::SendSDO(WORD_t idx, BYTE_t subidx, BYTE_t val, bool store)
256{
257 if (!fIsZombie)
258 fNetwork->SendSDO(fId, idx, subidx, val, store);
259 return !fIsZombie;
260}
261
262// --------------------------------------------------------------------------
263//
264// Sends the given SDO through the network to this device
265// An SDO message contains
266// an address (this device)
267// an index of the dictionary entry to address
268// a subindex of this dictionary entry to access
269// and a value to set for this dictionary entry
270//
271// The message is not send if the node has the status Zombie.
272// In this case false is returned, otherwise true
273//
274bool NodeDrv::SendSDO(WORD_t idx, BYTE_t subidx, WORD_t val, bool store)
275{
276 if (!fIsZombie)
277 fNetwork->SendSDO(fId, idx, subidx, val, store);
278 return !fIsZombie;
279}
280
281// --------------------------------------------------------------------------
282//
283// Sends the given SDO through the network to this device
284// An SDO message contains
285// an address (this device)
286// an index of the dictionary entry to address
287// a subindex of this dictionary entry to access
288// and a value to set for this dictionary entry
289//
290// The message is not send if the node has the status Zombie.
291// In this case false is returned, otherwise true
292//
293bool NodeDrv::SendSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, bool store)
294{
295 if (!fIsZombie)
296 fNetwork->SendSDO(fId, idx, subidx, val, store);
297 return !fIsZombie;
298}
299
300// --------------------------------------------------------------------------
301//
302// Sends the given SDO through the network to this device
303// An SDO message contains
304// an address (this device)
305// an index of the dictionary entry to address
306// a subindex of this dictionary entry to access
307// and a value to set for this dictionary entry
308//
309// The message is not send if the node has the status Zombie.
310// In this case false is returned, otherwise true
311//
312bool NodeDrv::SendSDO(WORD_t idx, BYTE_t val)
313{
314 if (!fIsZombie)
315 fNetwork->SendSDO(fId, idx, val, true);
316 return !fIsZombie;
317}
318
319// --------------------------------------------------------------------------
320//
321// Sends the given SDO through the network to this device
322// An SDO message contains
323// an address (this device)
324// an index of the dictionary entry to address
325// a subindex of this dictionary entry to access
326// and a value to set for this dictionary entry
327//
328// The message is not send if the node has the status Zombie.
329// In this case false is returned, otherwise true
330//
331bool NodeDrv::SendSDO(WORD_t idx, WORD_t val)
332{
333 if (!fIsZombie)
334 fNetwork->SendSDO(fId, idx, val, true);
335 return !fIsZombie;
336}
337
338// --------------------------------------------------------------------------
339//
340// Sends the given SDO through the network to this device
341// An SDO message contains
342// an address (this device)
343// an index of the dictionary entry to address
344// a subindex of this dictionary entry to access
345// and a value to set for this dictionary entry
346//
347// The message is not send if the node has the status Zombie.
348// In this case false is returned, otherwise true
349//
350bool NodeDrv::SendSDO(WORD_t idx, LWORD_t val)
351{
352 if (!fIsZombie)
353 fNetwork->SendSDO(fId, idx, val, true);
354 return !fIsZombie;
355}
356
357// --------------------------------------------------------------------------
358//
359// Request a SDO for a given idx/subidx
360// An SDO message contains
361// an address (this device)
362// an index of the dictionary entry to read
363// a subindex of this dictionary entry to access
364//
365// The message is not send if the node has the status Zombie.
366// In this case false is returned, otherwise true
367//
368bool NodeDrv::RequestSDO(WORD_t idx, BYTE_t subidx)
369{
370 if (!fIsZombie)
371 fNetwork->RequestSDO(fId, idx, subidx);
372 return !fIsZombie;
373}
374
375// --------------------------------------------------------------------------
376//
377// Send a NMT message (command) to this device
378//
379// The message is not send if the node has the status Zombie.
380// In this case false is returned, otherwise true
381//
382bool NodeDrv::SendNMT(BYTE_t cmd)
383{
384 if (!fIsZombie)
385 fNetwork->SendNMT(fId, cmd);
386 return !fIsZombie;
387}
388
389// --------------------------------------------------------------------------
390//
391// Send a Nodeguard message (command) to this device
392//
393void NodeDrv::SendNodeguard()
394{
395 fNetwork->SendNodeguard(fId);
396}
397
398// --------------------------------------------------------------------------
399//
400// Enable passthrough for the given functioncode of this device
401//
402void NodeDrv::EnableCanMsg(BYTE_t fcode)
403{
404 fNetwork->EnableCanMsg(fId, fcode, TRUE);
405}
406
407// --------------------------------------------------------------------------
408//
409// Wait a given timeout until the SDO with the given idx/subidx from
410// this device has been received.
411// You can stop waiting by StopWaitingForSDO.
412// Return false if waiting timed out.
413// If waiting timed out the node is set to status Zombie.
414//
415// If the node is already a zombie node, the message is deleted from the
416// queue and no waiting is done, false is returned..
417//
418bool NodeDrv::WaitForSdo(WORD_t idx, BYTE_t subidx, WORDS_t timeout)
419{
420 bool rc = fNetwork->WaitForSdo(fId, idx, subidx, fIsZombie?-1:timeout);
421
422 if (!rc)
423 {
424 lout << "NodeDrv::WaitForSdo: 0x" << hex << idx << "/" << dec << (int)subidx << " " << GetNodeName() << " --> ZOMBIE!" << endl;
425 SetZombie();
426 }
427/*
428 if (HasError())
429 {
430 lout << "NodeDrv::WaitForSdo: HasError 0x" << hex << idx << "/" << dec << (int)subidx << " " << GetNodeName() << " --> ZOMBIE!" << endl;
431 fIsZombie = kTRUE;
432 }
433*/
434 return fIsZombie ? false : rc;
435}
436
437/*
438void NodeDrv::WaitForSdos()
439{
440 while (fNetwork->WaitingForSdo(fId))
441 usleep(1);
442}
443*/
444
445// --------------------------------------------------------------------------
446//
447// Waits until the next Pdo1 from this device has been received
448//
449void NodeDrv::WaitForNextPdo1()
450{
451 fNetwork->WaitForNextPdo1(fId);
452}
453
454// --------------------------------------------------------------------------
455//
456// Waits until the next Pdo2 from this device has been received
457//
458void NodeDrv::WaitForNextPdo2()
459{
460 fNetwork->WaitForNextPdo2(fId);
461}
462
463// --------------------------------------------------------------------------
464//
465// Waits until the next Pdo3 from this device has been received
466//
467void NodeDrv::WaitForNextPdo3()
468{
469 fNetwork->WaitForNextPdo3(fId);
470}
471
472// --------------------------------------------------------------------------
473//
474// Waits until the next Pdo4 from this device has been received
475//
476void NodeDrv::WaitForNextPdo4()
477{
478 fNetwork->WaitForNextPdo4(fId);
479}
480
481// --------------------------------------------------------------------------
482//
483// Start the standard CANopen guarding of the device.
484// While ms is the guard time in millisec. This is the time between
485// two requests for a Nodeguard message.
486// ltf is the LifeTimeFactor. This means how often it is checked, that at
487// least one Nodeguard message was answered.
488//
489class NodeGuard : public MThread
490{
491 Double_t fTimeoutTime; //[s]
492 Double_t fGuardTime; //[s]
493 Int_t fLifeTimeFactor;
494
495 Bool_t fIsCanOpen;
496
497 NodeDrv *fDrv;
498
499public:
500 NodeGuard(NodeDrv *drv, Int_t guard, Int_t ltf, Bool_t canopen)
501 : MThread(false), fGuardTime(guard/1000.), fLifeTimeFactor(ltf), fIsCanOpen(canopen), fDrv(drv) { }
502
503 void Reset(timeval_t *tv=NULL)
504 {
505 MTime t;
506 if (tv)
507 t.Set(*tv);
508 else
509 t.Now();
510
511 fTimeoutTime = t + (fGuardTime*fLifeTimeFactor);
512 }
513
514 void *Thread()
515 {
516 Reset();
517
518 while (!HasStopFlag())
519 {
520 // Sending nodeguards seems to result in
521 // loosing CANbus messages or CANbus answers...
522 // strange. Also protecting VmodIcan::SendCanFrame
523 // by a Mutex doesn't help.
524 if (fIsCanOpen)
525 fDrv->SendNodeguard();
526
527 MTime t;
528 t.Now();
529
530 const Double_t t0 = t+fGuardTime;
531
532 while (!HasStopFlag() && t<t0 && t<fTimeoutTime)
533 {
534 usleep(5);
535 t.Now();
536 }
537
538 //cout << "-d-> " << (Long_t)((fTimeoutTime-t)*1000) << endl;
539 //cout << "-o-> " << (ULong_t)((fTimeoutTime)*1000)<< " " << (ULong_t)((t)*1000) << endl;
540 //cout << "-g-> " << (Long_t)((t-t0)*1000)<< endl;
541
542 if (t<fTimeoutTime)
543 continue;
544
545 fDrv->SetZombie(false);
546 return 0;
547 }
548 return 0;
549 }
550};
551
552void NodeDrv::StartGuarding(Bool_t real=kTRUE)
553{
554 if (fGuard)
555 return;
556
557 fGuard = new NodeGuard(this, fGuardTime, fLifeTimeFactor, real);
558 fGuard->Start();
559
560 lout << "- " << GetNodeName() << ": Guarding (" << dec;
561 lout << fLifeTimeFactor << "*" << fGuardTime << "ms) started." << endl;
562}
563
564void NodeDrv::StartGuarding(Int_t ms, Int_t ltf, Bool_t real)
565{
566 if (fGuard)
567 {
568 lout << "- " << GetNodeName() << ": ERROR - Guarding already started." << endl;
569 return;
570 }
571
572 fGuardTime = ms;
573 fLifeTimeFactor = ltf;
574
575 StartGuarding(real);
576}
577
578void NodeDrv::StopGuarding()
579{
580 if (!fGuard)
581 return;
582
583 delete fGuard;
584 fGuard=NULL;
585
586 lout << "- " << GetNodeName() << ": Guarding stopped." << endl;
587}
588
589// --------------------------------------------------------------------------
590//
591// Set the timeout timer to the time the event was received plus the
592// guard time times lifetimefactor.
593//
594void NodeDrv::HandleNodeguard(timeval_t *tv)
595{
596 if (fGuard)
597 fGuard->Reset(tv);
598}
599
600void NodeDrv::SetZombie(bool stopguard)
601{
602 fIsZombie = true;
603 if (stopguard)
604 StopGuarding();
605 else
606 lout << " - " << GetNodeName() << ": Zombie set due to timeout." << endl;
607}
Note: See TracBrowser for help on using the repository browser.