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

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