/ src / libserver / emi_common.cpp
emi_common.cpp
  1  /*
  2      EIBD eib bus access and management daemon
  3      Copyright (C) 2005-2011 Martin Koegler <mkoegler@auto.tuwien.ac.at>
  4  
  5      This program is free software; you can redistribute it and/or modify
  6      it under the terms of the GNU General Public License as published by
  7      the Free Software Foundation; either version 2 of the License, or
  8      (at your option) any later version.
  9  
 10      This program is distributed in the hope that it will be useful,
 11      but WITHOUT ANY WARRANTY; without even the implied warranty of
 12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13      GNU General Public License for more details.
 14  
 15      You should have received a copy of the GNU General Public License
 16      along with this program; if not, write to the Free Software
 17      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 18  */
 19  
 20  #include "emi_common.h"
 21  
 22  #include "emi.h"
 23  
 24  /* add formatter for fmt >= 10.0.0 */
 25  int format_as(E_state t) { return t; }
 26  
 27  EMIVer
 28  cfgEMIVersion(IniSectionPtr& s)
 29  {
 30    int v = s->value("version",vERROR);
 31    if (v > vRaw || v < vERROR)
 32      return vERROR;
 33    else if (v != vERROR)
 34      return EMIVer(v);
 35  
 36    std::string sv = s->value("version","");
 37    if (!sv.size())
 38      return vUnknown;
 39    else if (sv == "EMI1")
 40      return vEMI1;
 41    else if (sv == "EMI2")
 42      return vEMI2;
 43    else if (sv == "CEMI")
 44      return vCEMI;
 45    else if (sv == "raw")
 46      return vRaw;
 47    else
 48      return vERROR;
 49  }
 50  
 51  EMI_Common::EMI_Common (LowLevelIface* c, IniSectionPtr& s, LowLevelDriver *i) : LowLevelFilter(c,s,i)
 52  {
 53    timeout.set<EMI_Common, &EMI_Common::timeout_cb>(this);
 54    t->setAuxName("EMI_common");
 55    iface = i;
 56  }
 57  
 58  bool
 59  EMI_Common::setup()
 60  {
 61    if (iface == nullptr)
 62      return false;
 63    if(!LowLevelFilter::setup())
 64      return false;
 65    send_timeout = cfg->value("send-timeout",2000) / 1000.;
 66    max_retries = cfg->value("send-retries",3);
 67    monitor = cfg->value("monitor",false);
 68  
 69    return true;
 70  }
 71  
 72  void
 73  EMI_Common::start()
 74  {
 75    state = E_idle;
 76    LowLevelFilter::start();
 77  }
 78  
 79  void
 80  EMI_Common::started()
 81  {
 82    state = E_idle;
 83    TRACEPRINTF (t, 2, "OpenL2");
 84    if (monitor)
 85      cmdEnterMonitor();
 86    else
 87      cmdOpen();
 88  }
 89  
 90  void
 91  EMI_Common::stop (bool err)
 92  {
 93    TRACEPRINTF (t, 2, "CloseL2");
 94    if (err)
 95      LowLevelFilter::stop(err);
 96    else if (monitor)
 97      cmdLeaveMonitor();
 98    else
 99      cmdClose();
100  }
101  
102  void
103  EMI_Common::send_L_Data (LDataPtr l)
104  {
105    if (state != E_idle)
106      {
107        ERRORPRINTF(t, E_ERROR | 59, "EMI_common: send while waiting (%d)", state);
108        return;
109      }
110  
111    assert (l->lsdu.size() >= 1);
112    // discard long frames, they are not supported yet
113    // TODO add support for long frames!
114    if (l->lsdu.size() > maxPacketLen())
115      {
116        TRACEPRINTF (t, 2, "Oversize (%d), discarded", l->lsdu.size());
117        LowLevelFilter::do_send_Next();
118        return;
119      }
120    CArray pdu = lData2EMI (0x11, l);
121    out = pdu;
122    retries = 0;
123    send_Data (pdu);
124  }
125  
126  void
127  EMI_Common::do_send_Next()
128  {
129    if (state == E_wait)
130      state = E_wait_confirm;
131    else if (state == E_idle)
132      LowLevelFilter::do_send_Next();
133  }
134  
135  void
136  EMI_Common::send_Data(CArray &pdu)
137  {
138    state = E_wait;
139    timeout.start(send_timeout,0);
140    iface->send_Data(pdu);
141  }
142  
143  void
144  EMI_Common::timeout_cb(ev::timer &, int)
145  {
146    if (state <= E_timed_out)
147      return;
148    if (state == E_wait)
149      {
150        state = E_timed_out;
151        stop(true);
152        return;
153      }
154    assert (state == E_wait_confirm);
155    if (++retries <= max_retries)
156      {
157        TRACEPRINTF (t, 1, "No confirm, repeating");
158        send_Data(out);
159        return;
160      }
161  
162    // TODO raise an error instead?
163    ERRORPRINTF(t, E_WARNING | 119, "EMI: No confirm, packet discarded");
164  
165    state = E_idle;
166    LowLevelFilter::do_send_Next();
167  }
168  
169  void
170  EMI_Common::recv_Data(CArray& c)
171  {
172    const uint8_t *ind = getIndTypes();
173    if (c.size() > 0 && c[0] == 0xA0)
174      {
175        TRACEPRINTF (t, 2, "got reset ind");
176        stop(true);
177      }
178    else if (c.size() > 0 && c[0] == ind[I_CONFIRM])
179      {
180        if (state == E_wait_confirm)
181          {
182            TRACEPRINTF (t, 2, "Confirmed");
183            state = E_idle;
184            timeout.stop();
185            LowLevelFilter::do_send_Next();
186          }
187        else
188          TRACEPRINTF (t, 2, "spurious Confirm %d",(int)state);
189      }
190    else if (c.size() > 0 && c[0] == ind[I_DATA] && !monitor)
191      {
192        LDataPtr p = EMI2lData (c);
193        if (p)
194          master->recv_L_Data (std::move(p));
195        else
196          t->TracePacket (2, "unparseable EMI data", c);
197      }
198    else if (c.size() > 4 && c[0] == ind[I_BUSMON] && monitor)
199      {
200        LBusmonPtr p = LBusmonPtr(new L_Busmon_PDU ());
201        p->l_status = c[1];
202        p->time_stamp = (c[2] << 24) | (c[3] << 16);
203        p->lpdu.set (c.data() + 4, c.size() - 4);
204        master->recv_L_Busmonitor (std::move(p));
205      }
206    else
207      {
208        TRACEPRINTF (t, 2, "unknown data");
209      }
210  }
211