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

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