Index: trunk/MagicSoft/Cosy/candrv/canopen.cc
===================================================================
--- trunk/MagicSoft/Cosy/candrv/canopen.cc	(revision 1703)
+++ trunk/MagicSoft/Cosy/candrv/canopen.cc	(revision 1727)
@@ -16,7 +16,7 @@
 !
 !
-!   Author(s): Thomas Bretz <mailto:tbretz@uni-sw.gwdg.de>, 2001
-!
-!   Copyright: MAGIC Software Development, 2000-2001
+!   Author(s): Thomas Bretz <mailto:tbretz@astro.uni-wuerzburg.de>, 2003
+!
+!   Copyright: MAGIC Software Development, 2000-2003
 !
 !
@@ -84,4 +84,5 @@
 {
     const WORD_t fcode = cobid >> 7;
+    const BYTE_t  node = cobid & 0x1f;
 
     switch (fcode)
@@ -94,11 +95,21 @@
         return;
 
-    case kSYNC:
-        cout << "Sync" << endl;
+    case kEMERGENCY: // also case kSYNC:
+        if (cobid==0)
+            cout << "Sync" << endl;
+        else
+        {
+            cout << "EMERGENCY Node #" << dec << (int)data[1] << endl;
+            HandleEmergency(node, tv);
+        }
+        return;
+
+    case kNodeguard:
+        //cout << "Nodeguard Node #" << dec << (int)node << endl;
+        HandleNodeguard(node, tv);
         return;
 
     case kSDO_RX:
         {
-            const BYTE_t  node   = cobid & 0x1f;
             const BYTE_t  cmd    = data[0];
             const LWORD_t dat    = data[4] | (data[5]<<8) | (data[6]<<16) | (data[7]<<24);
@@ -114,5 +125,4 @@
     case kPDO1_TX:
         {
-            const BYTE_t node = cobid & 0x1f;
             HandlePDO1(node, data, tv);
             pthread_cond_broadcast(&fPdoCond[node-1][0]);
@@ -122,5 +132,4 @@
     case kPDO2_TX:
         {
-            const BYTE_t node = cobid & 0x1f;
             HandlePDO2(node, data, tv);
             pthread_cond_broadcast(&fPdoCond[node-1][1]);
@@ -130,5 +139,4 @@
     case kPDO3_TX:
         {
-            const BYTE_t node = cobid & 0x1f;
             HandlePDO3(node, data, tv);
             pthread_cond_broadcast(&fPdoCond[node-1][2]);
@@ -138,5 +146,4 @@
     case kPDO4_TX:
         {
-            const BYTE_t node = cobid & 0x1f;
             HandlePDO4(node, data, tv);
             pthread_cond_broadcast(&fPdoCond[node-1][3]);
@@ -145,6 +152,5 @@
     }
 
-    const BYTE_t node = cobid & 0x1f;
-    cout << "Function Code: 0x" << hex << fcode << "  Node: " << dec << (int)node << endl;
+    cout << "CanOpen::HandleCanMessage - Unhandled Message: Function Code=0x" << hex << fcode << "  Node #" << dec << (int)node << endl;
 }
 
@@ -168,4 +174,13 @@
 {
     EnableCanMsg(node, kEMERGENCY);
+}
+
+// --------------------------------------------------------------------------
+//
+// Enables Nodeguard messages for a given node
+//
+void CanOpen::EnableNodeguard(BYTE_t node)
+{
+    EnableCanMsg(node, kNodeguard);
 }
 
@@ -372,5 +387,5 @@
 // --------------------------------------------------------------------------
 //
-// Send an NMT Message to the given node with command cmd
+// Send a NMT Message to the given node with command cmd
 //
 void CanOpen::SendNMT(BYTE_t node, BYTE_t cmd)
@@ -381,4 +396,14 @@
 // --------------------------------------------------------------------------
 //
+// Send a Nodeguard Message to the given node with command cmd
+//
+void CanOpen::SendNodeguard(BYTE_t node)
+{
+    BYTE_t msg[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+    SendCanFrame(CobId(node, kNodeguard), msg, 1);
+}
+
+// --------------------------------------------------------------------------
+//
 // Decodes node and function code into a CobId
 //
Index: trunk/MagicSoft/Cosy/candrv/network.cc
===================================================================
--- trunk/MagicSoft/Cosy/candrv/network.cc	(revision 1703)
+++ trunk/MagicSoft/Cosy/candrv/network.cc	(revision 1727)
@@ -89,48 +89,30 @@
 void Network::HandleSDO(BYTE_t node, BYTE_t cmd, WORD_t idx, BYTE_t subidx, LWORD_t data, timeval_t *tv)
 {
-    switch (cmd)
-    {
-    case kSDO_TX4:       // answer to 0x40 with 4 bytes of data
-        if (fNodes[node])
+    if (fNodes[node])
+        switch (cmd)
         {
+        case kSDO_TX4:       // answer to 0x40 with 4 bytes of data
             fNodes[node]->HandleSDO(idx, subidx, data, tv);
             return;
+
+        case kSDO_TX3:       // answer to 0x40 with 2 bytes of data
+            fNodes[node]->HandleSDO(idx, subidx, data>>16, tv);
+            return;
+
+        case kSDO_TX1:       // answer to 0x40 with 1 byte  of data
+            fNodes[node]->HandleSDO(idx, subidx, data>>24, tv);
+            return;
+
+        case kSDO_TX_OK:     // answer to a SDO_TX message
+            fNodes[node]->HandleSDOOK(idx, subidx, tv);
+            return;
+
+        case kSDO_TX_ERROR:  // error message (instead of 0x60)
+            fNodes[node]->HandleSDOError(data);
+            return;
         }
-        break;
-
-    case kSDO_TX3:       // answer to 0x40 with 2 bytes of data
-        if (fNodes[node])
-        {
-            fNodes[node]->HandleSDO(idx, subidx, data>>16, tv);
-            return;
-        }
-        break;
-
-    case kSDO_TX1:       // answer to 0x40 with 1 byte  of data
-        if (fNodes[node])
-        {
-            fNodes[node]->HandleSDO(idx, subidx, data>>24, tv);
-            return;
-        }
-        break;
-
-    case kSDO_TX_OK:     // answer to a SDO_TX message
-        if (fNodes[node])
-        {
-            fNodes[node]->HandleSDOOK(idx, subidx);
-            return;
-        }
-        break;
-
-    case kSDO_TX_ERROR:  // error message (instead of 0x60)
-        if (fNodes[node])
-        {
-            fNodes[node]->HandleSDOError(data);
-            return;
-        }
-        break;
-    }
+
     cout << dec << setfill('0');
-    cout << "Node=" << (int)node  << " Cmd=0x" << hex << (int)cmd << ": ";
+    cout << "Network::HandleSDO: Node=" << (int)node  << " Cmd=0x" << hex << (int)cmd << ": ";
     cout << "Sdo=" << idx  << "/" << (int)subidx << ": 0x" << setw(8) << data;
     cout << endl;
@@ -145,5 +127,5 @@
     if (!fNodes[node])
     {
-        cout << "Node " << dec << (int)node << ", PDO1: " << hex;
+        cout << "Network::HandlePDO1: Node #" << dec << (int)node << " not found - PDO1: " << hex;
         for (int i=0; i<8; i++)
             cout << " 0x" << (int)data[i];
@@ -163,5 +145,5 @@
     if (!fNodes[node])
     {
-        cout << "Node " << dec << (int)node << ", PDO2: " << hex;
+        cout << "Network::HandlePDO2: Node #" << dec << (int)node << " not found - PDO2: " << hex;
         for (int i=0; i<8; i++)
             cout << " 0x" << (int)data[i];
@@ -181,5 +163,5 @@
     if (!fNodes[node])
     {
-        cout << "Node " << dec << (int)node << ", PDO3: " << hex;
+        cout << "Network::HandlePDO3: Node #" << dec << (int)node << " not found - PDO3: " << hex;
         for (int i=0; i<8; i++)
             cout << " 0x" << (int)data[i];
@@ -199,5 +181,5 @@
     if (!fNodes[node])
     {
-        cout << "Node " << dec << (int)node << ", PDO4: " << hex;
+        cout << "Network::HandlePDO4: Node #" << dec << (int)node << " not found - PDO4: " << hex;
         for (int i=0; i<8; i++)
             cout << " 0x" << (int)data[i];
@@ -208,4 +190,37 @@
     fNodes[node]->HandlePDO4(data, tv);
 }
+
+// --------------------------------------------------------------------------
+//
+// Distributes Nodeguard messages to the correspoding node calling
+// HandleNodeguard
+//
+void Network::HandleNodeguard(BYTE_t node, timeval_t *tv)
+{
+    if (!fNodes[node])
+    {
+        cout << "Network::HandleNodeguard: Node #" << dec << (int)node << " not found: Nodeguard." << endl;
+        return;
+    }
+
+    fNodes[node]->HandleNodeguard(tv);
+}
+
+// --------------------------------------------------------------------------
+//
+// Distributes Emergency messages to the correspoding node calling
+// HandleEmergency
+//
+void Network::HandleEmergency(BYTE_t node, timeval_t *tv)
+{
+    if (!fNodes[node])
+    {
+        cout << "Network::HandleEmergency: Node #" << dec << (int)node << " not found: Emergency." << endl;
+        return;
+    }
+
+    fNodes[node]->HandleEmergency(tv);
+}
+
 
 // --------------------------------------------------------------------------
@@ -281,6 +296,6 @@
             continue;
 
-        lout << "- Node #" << dec << i << " '" << fNodes[i]->GetNodeName();
-        lout << "' has error #" << fNodes[i]->GetError() << endl;
+        //lout << "- Node #" << dec << i << " '" << fNodes[i]->GetNodeName();
+        //lout << "' has error #" << fNodes[i]->GetError() << endl;
     }
 
Index: trunk/MagicSoft/Cosy/candrv/network.h
===================================================================
--- trunk/MagicSoft/Cosy/candrv/network.h	(revision 1703)
+++ trunk/MagicSoft/Cosy/candrv/network.h	(revision 1727)
@@ -16,4 +16,6 @@
     void HandlePDO3(BYTE_t node, BYTE_t *data, timeval_t *tv);
     void HandlePDO4(BYTE_t node, BYTE_t *data, timeval_t *tv);
+    void HandleNodeguard(BYTE_t node, timeval_t *tv);
+    void HandleEmergency(BYTE_t node, timeval_t *tv);
 
     void InitNodes();
Index: trunk/MagicSoft/Cosy/candrv/nodedrv.cc
===================================================================
--- trunk/MagicSoft/Cosy/candrv/nodedrv.cc	(revision 1703)
+++ trunk/MagicSoft/Cosy/candrv/nodedrv.cc	(revision 1727)
@@ -33,5 +33,5 @@
 //  virtual void StopDevice()
 //  virtual void HandleSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, timeval_t *tv)
-//  virtual void HandleSDOOK(WORD_t idx, BYTE_t subidx)
+//  virtual void HandleSDOOK(WORD_t idx, BYTE_t subidx, timeval_t *tv)
 //  virtual void HandleSDOError(LWORD_t data)
 //  virtual void HandlePDO1(BYTE_t *data, timeval_t *tv)
@@ -48,4 +48,7 @@
 #include <iostream.h>
 
+#include <TTimer.h>
+
+#include "base/timer.h"
 #include "network.h"
 #include "MLogManip.h"
@@ -76,13 +79,19 @@
     }
 
+    fTimerOn = kFALSE;
+    fTimeout = new TTimer(this, 100, kFALSE); // 100ms, asynchronous
+
     lout << "- Node #" << (int)nodeid << " (" << name << ") initialized." << endl;
-}
-
-// --------------------------------------------------------------------------
-//
-// Empty destructor
+
+}
+
+// --------------------------------------------------------------------------
+//
+// destructor
 //
 NodeDrv::~NodeDrv()
 {
+    fTimerOn = kFALSE;
+    delete fTimeout;
 }
 
@@ -122,4 +131,6 @@
     EnableCanMsg(kSDO_RX);
     EnableCanMsg(kSDO_TX);
+    EnableCanMsg(kNodeguard);
+    EnableCanMsg(kEMERGENCY);
 
     fIsZombie = kFALSE;
@@ -135,5 +146,5 @@
 // This output is never redirected to the GUI
 //
-void NodeDrv::HandleSDOOK(WORD_t idx, BYTE_t subidx)
+void NodeDrv::HandleSDOOK(WORD_t idx, BYTE_t subidx, timeval_t *tv)
 {
     const Bool_t gui = lout.IsOutputDeviceEnabled(MLog::eGui);
@@ -367,5 +378,5 @@
 // --------------------------------------------------------------------------
 //
-// Send an NMT message (command) to this device
+// Send a NMT message (command) to this device
 //
 // The message is not send if the node has the status Zombie.
@@ -377,4 +388,13 @@
         fNetwork->SendNMT(fId, cmd);
     return !fIsZombie;
+}
+
+// --------------------------------------------------------------------------
+//
+// Send a Nodeguard message (command) to this device
+//
+void NodeDrv::SendNodeguard()
+{
+    fNetwork->SendNodeguard(fId);
 }
 
@@ -404,6 +424,15 @@
 
     if (!rc)
+    {
+        lout << "NodeDrv::WaitForSdo: 0x" << hex << idx << "/" << dec << (int)subidx << " " << GetNodeName() << " --> ZOMBIE!" << endl;
+        SetZombie();
+    }
+/*
+    if (HasError())
+    {
+        lout << "NodeDrv::WaitForSdo: HasError 0x" << hex << idx << "/" << dec << (int)subidx << " " << GetNodeName() << " --> ZOMBIE!" << endl;
         fIsZombie = kTRUE;
-
+    }
+*/
     return fIsZombie ? false : rc;
 }
@@ -453,2 +482,128 @@
 }
 
+// --------------------------------------------------------------------------
+//
+// Start the standard CANopen guarding of the device.
+// While ms is the guard time in millisec. This is the time between
+// two requests for a Nodeguard message.
+// ltf is the LifeTimeFactor. This means how often it is checked, that at
+// least one Nodeguard message was answered.
+//
+void NodeDrv::StartGuarding()
+{
+    if (fTimerOn)
+        return;
+
+    SendNodeguard();
+
+    fTimerOn = kTRUE;
+    fTimeout->SetTime(fGuardTime);
+    fTimeout->Reset();
+    fTimeout->TurnOn();
+    //fTimeout->Start(fGuardTime, kTRUE);
+
+    lout << "- " << GetNodeName() << ": Guarding (" << dec;
+    lout << fLifeTimeFactor << "*" << fGuardTime << "ms) started." << endl;
+}
+
+void NodeDrv::StartGuarding(Int_t ms, Int_t ltf)
+{
+    if (fTimerOn)
+    {
+        lout << "- " << GetNodeName() << ": ERROR - Guarding already started." << endl;
+        return;
+    }
+    fGuardTime      = ms;
+    fLifeTimeFactor = ltf;
+
+    StartGuarding();
+}
+
+void NodeDrv::StopGuarding()
+{
+    fTimeout->TurnOff();
+    fTimerOn = kFALSE;
+
+    lout << "- " << GetNodeName() << ": Guarding stopped." << endl;
+}
+
+// --------------------------------------------------------------------------
+//
+// Handle the Nodeguard-Timer Event.
+// It checks whether the node timed out. If it timed out it is set to
+// the Zombie state.
+// A new Nodeguard request is send and a new timer event is triggered.
+//
+Bool_t NodeDrv::HandleTimer(TTimer *t)
+{
+    //
+    // WARNING:
+    //           It seems, that you should never access ANY output from
+    //           here. Neither the GUI, nor COUT. This can result in
+    //           'unexpected async reply'
+    //
+
+   /*
+     Fons:
+     -----
+
+     timers never trigger at the same time or when in a TTimer::Notify.
+     Little explanation:
+
+     - there are two types of timers synchronous and a-synchronous.
+     - synchronous timers are only handled via the ROOT eventloop
+       (see TUnixSystem::DispatchOneEvent()). If there are no mouse/keyboard
+       events then the synchronous timer queue is checked. So if the processing
+       of a mouse/keyboard event takes a long time synchronous timers are not
+       called for a while. To prevent this from happening one can call in long
+       procedures gSystem->ProcessEvents(). The system schedules only the
+       next timer in the queue when the current one's Notify() has finished.
+     - a-synchronous timers are triggered via SIGALARM, i.e. the program is
+       interupted and execution jumps to the Notify() function. When the
+       notify is finished the next a-sync timer is scheduled and the system
+       resumes from the place where it was initially interrupted. One of the
+       things to remember when using a-sync timers is don't make any graphics
+       calls in them. X11 is not re-entrant and it might be that the SIGALARM
+       signal interrupted the system while being in X11. A-sync timers are best
+       used to set flags that you can test at a convenient and controlled
+       time.
+       */
+    if (fIsZombie)
+        return kTRUE;
+
+    Timer time;
+    Double_t now = time.Now();
+    if (now > fTimeoutTime)
+    {
+        //cout << "ERROR - " << GetNodeName() << " didn't respond in timeout window." << endl;
+        //lout << "ERROR - " << GetNodeName() << " didn't respond in timeout window." << endl;
+        //cout << dec << "+" << (int)GetId() << ": Handle: " << fmod(now, 500) << endl;
+        //cout << dec << "+" << (int)GetId() << ": Handle: " << fmod(fTimeoutTime, 500) << endl;
+        //cout << fGuardTime << endl;
+        fIsZombie = true;
+        //SetZombie();
+
+        return kTRUE;
+    }
+
+    SendNodeguard();
+
+    return kTRUE;
+}
+
+// --------------------------------------------------------------------------
+//
+// Set the timeout timer to the time the event was received plus the
+// guard time times lifetimefactor.
+//
+void NodeDrv::HandleNodeguard(timeval_t *tv)
+{
+    Timer t(tv);
+    fTimeoutTime = t + (fGuardTime*fLifeTimeFactor/1000.);
+}
+
+void NodeDrv::SetZombie()
+{
+    fIsZombie = true;
+    StopGuarding();
+}
Index: trunk/MagicSoft/Cosy/candrv/nodedrv.h
===================================================================
--- trunk/MagicSoft/Cosy/candrv/nodedrv.h	(revision 1703)
+++ trunk/MagicSoft/Cosy/candrv/nodedrv.h	(revision 1727)
@@ -20,5 +20,5 @@
 class Network;
 
-class NodeDrv : public Log
+class NodeDrv : public Log, public TObject
 {
 private:
@@ -30,9 +30,22 @@
     int fError;
 
+    Bool_t fIsZombie; // A Zombie node is a node which doesn't answer...
+
+    TTimer  *fTimeout;
+    Int_t    fGuardTime;      // Guardtime [ms]
+    Int_t    fLifeTimeFactor;
+    Double_t fTimeoutTime;
+    Bool_t   fTimerOn;
+
+    Bool_t HandleTimer(TTimer *t);
+
 protected:
     void SetError(int err) { fError = err; }
     void DelError()        { fError = 0; }
 
-    Bool_t fIsZombie; // A Zombie node is a node which doesn't answer...
+    Int_t GetLifeTimeFactor() const { return fLifeTimeFactor; }
+    Int_t GetGuardTime() const      { return fGuardTime; }
+
+    virtual void SetZombie();
 
 public:
@@ -56,11 +69,13 @@
 
     virtual void HandleSDO(WORD_t idx, BYTE_t subidx, LWORD_t val, timeval_t *tv);
-    virtual void HandleSDOOK(WORD_t idx, BYTE_t subidx);
+    virtual void HandleSDOOK(WORD_t idx, BYTE_t subidx, timeval_t *tv);
     virtual void HandleSDOError(LWORD_t data);
 
-    virtual void HandlePDO1(BYTE_t *data, timeval_t *tv) {};
-    virtual void HandlePDO2(BYTE_t *data, timeval_t *tv) {};
-    virtual void HandlePDO3(BYTE_t *data, timeval_t *tv) {};
-    virtual void HandlePDO4(BYTE_t *data, timeval_t *tv) {};
+    virtual void HandlePDO1(BYTE_t *data, timeval_t *tv) {}
+    virtual void HandlePDO2(BYTE_t *data, timeval_t *tv) {}
+    virtual void HandlePDO3(BYTE_t *data, timeval_t *tv) {}
+    virtual void HandlePDO4(BYTE_t *data, timeval_t *tv) {}
+    virtual void HandleNodeguard(timeval_t *tv);
+    virtual void HandleEmergency(timeval_t *tv) {}
 
     bool SendPDO1(BYTE_t data[8]);
@@ -80,4 +95,5 @@
 
     bool SendNMT(BYTE_t cmd);
+    virtual void SendNodeguard();
 
     bool RequestSDO(WORD_t idx, BYTE_t subidx=0);
@@ -93,4 +109,8 @@
     void EnableCanMsg(BYTE_t fcode);
 
+    void StartGuarding();
+    void StartGuarding(Int_t ms, Int_t ltf);
+    void StopGuarding();
+
     ClassDef(NodeDrv, 0) // base class for an object describing hardware
 };
Index: trunk/MagicSoft/Cosy/candrv/vmodican.cc
===================================================================
--- trunk/MagicSoft/Cosy/candrv/vmodican.cc	(revision 1703)
+++ trunk/MagicSoft/Cosy/candrv/vmodican.cc	(revision 1727)
@@ -409,5 +409,5 @@
     while (!Send(&msg));      /* transmitt to module */
 
-    lout << "- Baudrate set to " << rate << "bps" << endl;
+    lout << "- Baudrate set to " << rate << "kbps" << endl;
 }
 
@@ -904,7 +904,7 @@
 //   */
 //
-void VmodIcan::SendCanFrame(WORD_t cobid, BYTE_t m[8])
-{
-    const WORD_t desc = MsgDescr(cobid, 8);
+void VmodIcan::SendCanFrame(WORD_t cobid, BYTE_t m[8], BYTE_t rtr)
+{
+    const WORD_t desc = MsgDescr(cobid, 8, rtr);
 
     Message msg;
Index: trunk/MagicSoft/Cosy/candrv/vmodican.h
===================================================================
--- trunk/MagicSoft/Cosy/candrv/vmodican.h	(revision 1703)
+++ trunk/MagicSoft/Cosy/candrv/vmodican.h	(revision 1727)
@@ -73,5 +73,5 @@
     void EnableCobId(WORD_t cobid, int flag=TRUE);
 
-    void SendCanFrame(WORD_t cobid, BYTE_t m[8]);
+    void SendCanFrame(WORD_t cobid, BYTE_t m[8], BYTE_t rtr=0);
     void SendCanFrame(WORD_t cobid,
                       BYTE_t m0=0, BYTE_t m1=0, BYTE_t m2=0, BYTE_t m3=0,
