MySensors Library & Examples  2.3.2-62-ge298769
PJONSimpleSwitch.h
1 
2 /*-O//\ __ __
3  |-gfo\ |__| | | | |\ | ®
4  |!y°o:\ | __| |__| | \| 13.0
5  |y"s§+`\ multi-master, multi-media bus network protocol
6  /so+:-..`\ Copyright 2010-2020 by Giovanni Blu Mitolo [email protected]
7  |+/:ngr-*.`\
8  |5/:%&-a3f.:;\
9  \+//u/+g%{osv,,\
10  \=+&/osw+olds.\\
11  \:/+-.-°-:+oss\
12  | | \oy\\
13  > <
14 ______-| |-__________________________________________________________________
15 
16 PJONSimpleSwitch has been contributed by Fred Larsen.
17 
18 It routes packets between buses with different bus ids, and also between
19 local buses with no bus ids. It is limited to one single strategy in contrast
20 to its descendant PJONSwitch.
21 
22 A default gateway can be specified, identifying one of the attached buses to
23 receive packets to other target buses than any of the attached buses. It is
24 possible to use a PJONSimpleSwitch to handle leaf buses in a tree structure.
25 
26 NAT (network address translation) support is present, allowing a local bus
27 to communicate with shared buses. To do this, the local bus must have the
28 bus id set to a public/NAT bus id with which it can be reached from other
29 buses, but be set to local mode and not shared mode.
30 Any incoming packet to the public bus id will be forwarded into the local bus
31 with the receiver bus id changed to 0.0.0.0 which is considered equivalent
32 with a local bus address.
33 Outgoing packets must be sent in shared mode with a sender bus id of 0.0.0.0,
34 which will be replaced with the NAT address when forwarded by the switch,
35 enabling receivers on shared buses to reply back to the local bus.
36 _____________________________________________________________________________
37 
38 This software is experimental and it is distributed "AS IS" without any
39 warranty, use it at your own risk.
40 
41 Copyright 2010-2020 by Giovanni Blu Mitolo [email protected]
42 
43 Licensed under the Apache License, Version 2.0 (the "License");
44 you may not use this file except in compliance with the License.
45 You may obtain a copy of the License at
46 
47  http://www.apache.org/licenses/LICENSE-2.0
48 
49 Unless required by applicable law or agreed to in writing, software
50 distributed under the License is distributed on an "AS IS" BASIS,
51 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
52 See the License for the specific language governing permissions and
53 limitations under the License. */
54 
55 #pragma once
56 #include "PJON.h"
57 
58 #ifndef PJON_ROUTER_MAX_BUSES
59 #define PJON_ROUTER_MAX_BUSES 5
60 #endif
61 
62 template<class Strategy>
64 {
65 protected:
66  uint8_t bus_count = 0;
67  uint8_t default_gateway = PJON_NOT_ASSIGNED;
68  uint8_t current_bus = PJON_NOT_ASSIGNED;
69  PJON<Strategy> *buses[PJON_ROUTER_MAX_BUSES];
70 
71  void connect(
72  uint8_t bus_count_in,
73  PJON<Strategy> * const buses_in[],
74  uint8_t default_gateway_in,
75  void *custom_pointer,
76  PJON_Receiver receiver,
77  PJON_Error error
78  )
79  {
80  bus_count = (bus_count_in > PJON_ROUTER_MAX_BUSES) ?
81  PJON_ROUTER_MAX_BUSES : bus_count_in;
82  default_gateway = default_gateway_in;
83  for(uint8_t i = 0; i < bus_count; i++) {
84  buses[i] = buses_in[i];
85  buses[i]->set_receiver(receiver);
86  buses[i]->set_error(error);
87  buses[i]->set_custom_pointer(custom_pointer);
88  buses[i]->set_router(true);
89  }
90  };
91 
92  uint8_t find_attached_bus_with_id(
93  const uint8_t *bus_id,
94  const uint8_t /*device_id*/,
95  uint8_t &start_bus
96  )
97  {
98  for(uint8_t i=start_bus; i<bus_count; i++) {
99  if(memcmp(bus_id, buses[i]->tx.bus_id, 4) == 0) {
100  start_bus = i + 1; // Continue searching for more matches after this
101  return i; // Explicit bus id match
102  }
103  }
104  start_bus = PJON_NOT_ASSIGNED;
105  return PJON_NOT_ASSIGNED;
106  };
107 
108 #ifdef PJON_ROUTER_NEED_INHERITANCE
109  virtual
110 #endif
111  void send_packet(const uint8_t *payload, const uint16_t length,
112  const uint8_t receiver_bus, const uint8_t sender_bus,
113  bool &ack_sent, const PJON_Packet_Info &packet_info)
114  {
115  // Send an ACK once to notify that the packet will be delivered
116  if(
117  !ack_sent &&
118  (packet_info.header & PJON_ACK_REQ_BIT) &&
119  (packet_info.rx.id != PJON_BROADCAST)
120  ) {
121  buses[sender_bus]->strategy.send_response(PJON_ACK);
122  ack_sent = true;
123  }
124 
125  /* Set current_bus to receiver bus before potentially calling error
126  callback for that bus */
127  uint8_t send_bus = current_bus;
128  current_bus = receiver_bus;
129 
130  /* NAT support: If a shared packet comes from a local bus destined to a
131  non-local receiver, then put the NAT address of the bus as the sender
132  bus id so that replies can find the route back via NAT. */
133  PJON_Packet_Info p_info = packet_info;
134  if ((packet_info.header & PJON_MODE_BIT) &&
135  !(buses[sender_bus]->config & PJON_MODE_BIT) &&
136  memcmp(buses[sender_bus]->tx.bus_id, PJONTools::localhost(), 4)!=0 &&
137  memcmp(packet_info.tx.bus_id, PJONTools::localhost(), 4)==0) {
138  // Replace sender bus id with public/NAT bus id in the packet
139  memcpy(&p_info.tx.bus_id, buses[sender_bus]->tx.bus_id, 4);
140  }
141 
142  /* NAT support: If a shared packet comes with receiver bus id matching the
143  NAT address of a local bus, then change the receiver bus id to 0.0.0.0
144  before forwarding the shared packet to the local bus. */
145  if ((packet_info.header & PJON_MODE_BIT) &&
146  !(buses[receiver_bus]->config & PJON_MODE_BIT) &&
147  memcmp(buses[receiver_bus]->tx.bus_id, PJONTools::localhost(), 4)!=0 &&
148  memcmp(packet_info.rx.bus_id, buses[receiver_bus]->tx.bus_id, 4)==0) {
149  // Replace receiver bus id with 0.0.0.0 when sending to local bus
150  memcpy(p_info.rx.bus_id, PJONTools::localhost(), 4);
151  }
152 
153  uint16_t result =
154 #if PJON_MAX_PACKETS == 0
155  buses[receiver_bus]->forward_blocking(
156  p_info,
157  (const uint8_t *)payload,
158  length
159  );
160  /* Call error function explicitly, because it is not called by
161  forward_blocking */
162  if(result == PJON_FAIL) {
163  dynamic_error_function(PJON_CONNECTION_LOST, 0);
164  }
165 #else
166  buses[receiver_bus]->forward(
167  p_info,
168  (const uint8_t *)payload,
169  length
170  );
171 #endif
172  current_bus = send_bus;
173  }
174 
175  void forward_packet(
176  const uint8_t *payload,
177  const uint16_t length,
178  const uint8_t receiver_bus,
179  const uint8_t sender_bus,
180  bool &ack_sent,
181  const PJON_Packet_Info &packet_info
182  )
183  {
184  // If receiving bus matches and not equal to sending bus, then route packet
185  if(receiver_bus != PJON_NOT_ASSIGNED && receiver_bus != sender_bus)
186  send_packet(
187  payload,
188  length,
189  receiver_bus,
190  sender_bus,
191  ack_sent,
192  packet_info
193  );
194  }
195 
196 #ifdef PJON_ROUTER_NEED_INHERITANCE
197  virtual
198 #endif
199  uint8_t find_bus_with_id(
200  const uint8_t bus_id[],
201  const uint8_t device_id,
202  uint8_t &start_bus
203  )
204  {
205  return find_attached_bus_with_id(bus_id, device_id, start_bus);
206  };
207 
208 #ifdef PJON_ROUTER_NEED_INHERITANCE
209  virtual
210 #endif
211  void dynamic_receiver_function(
212  uint8_t *payload,
213  uint16_t length,
214  const PJON_Packet_Info &packet_info
215  )
216  {
217  uint8_t start_search = 0;
218  bool ack_sent = false; // Send ACK once even if delivering to multiple buses
219  do {
220  uint8_t receiver_bus = find_bus_with_id((const uint8_t*)
221  ((packet_info.header & PJON_MODE_BIT) != 0 ?
222  packet_info.rx.bus_id : PJONTools::localhost()),
223  packet_info.rx.id, start_search
224  );
225 
226  /* The NAT case:
227  A. A shared packet comes in destined to the "public" bus id registered
228  on the local bus. It will be found normally, and send_packet will
229  modify the receiver bus id to 0.0.0.0 before sending.
230  B. A shared packet comes in from the local bus, with sender bus id
231  0.0.0.0 and a valid receiver bus id. The receiver bus id will be
232  found normally, and send_packet will modify the sender bus id
233  from 0.0.0.0 to the registered public/NAT bus id of the local bus.
234  */
235 
236  if(receiver_bus == PJON_NOT_ASSIGNED) {
237  receiver_bus = default_gateway;
238  }
239 
240  forward_packet(
241  payload,
242  length,
243  receiver_bus,
244  current_bus,
245  ack_sent,
246  packet_info
247  );
248 
249  } while(start_search != PJON_NOT_ASSIGNED);
250  };
251 
252 #ifdef PJON_ROUTER_NEED_INHERITANCE
253  virtual
254 #endif
255  void dynamic_error_function(uint8_t code, uint16_t data) { }
256 
257 public:
258 
259  PJONSimpleSwitch() {};
260 
262  uint8_t bus_count,
263  PJON<Strategy> * const buses[],
264  uint8_t default_gateway = PJON_NOT_ASSIGNED
265  )
266  {
267  connect_buses(bus_count, buses, default_gateway);
268  };
269 
270  // Specialized constructor to simplify syntax when using 2 buses
272  PJON<Strategy> &bus0,
273  PJON<Strategy> &bus1,
274  uint8_t default_gateway = PJON_NOT_ASSIGNED
275  )
276  {
277  PJON<Strategy> *buses[2] = { &bus0, &bus1 };
278  connect_buses(2, buses, default_gateway);
279  };
280 
281  // Specialized constructor to simplify syntax when using 3 buses
283  PJON<Strategy> &bus0,
284  PJON<Strategy> &bus1,
285  PJON<Strategy> &bus2,
286  uint8_t default_gateway = PJON_NOT_ASSIGNED
287  )
288  {
289  PJON<Strategy> *buses[3] = { &bus0, &bus1, &bus2 };
290  connect_buses(3, buses, default_gateway);
291  };
292 
293  void begin()
294  {
295  for(uint8_t i = 0; i < bus_count; i++) {
296  buses[i]->begin();
297  }
298  };
299 
300  void loop()
301  {
302  for(current_bus = 0; current_bus < bus_count; current_bus++) {
303  uint16_t code =
304  buses[current_bus]->receive(
305  buses[current_bus]->strategy.get_receive_time()
306  );
307  if(PJON_MAX_PACKETS < bus_count && code == PJON_ACK) {
308  break;
309  }
310  }
311  for(current_bus = 0; current_bus < bus_count; current_bus++) {
312  buses[current_bus]->update();
313  }
314  current_bus = PJON_NOT_ASSIGNED;
315  };
316 
317  void connect_buses(
318  uint8_t bus_count_in,
319  PJON<Strategy> * const buses_in[],
320  uint8_t default_gateway_in = PJON_NOT_ASSIGNED
321  )
322  {
323  connect(
324  bus_count_in,
325  buses_in,
326  default_gateway_in,
327  this,
330  );
331  };
332 
333  // Return the position of the bus currently calling a callback.
334  // (It may return PJON_NOT_ASSIGNED if not doing a callback.)
335  uint8_t get_callback_bus() const
336  {
337  return current_bus;
338  }
339 
340  // Return one of the buses, in the same order as sent to the constructor
341  PJON<Strategy> &get_bus(const uint8_t ix)
342  {
343  return *(buses[ix]);
344  }
345 
346  static void receiver_function(
347  uint8_t *payload,
348  uint16_t length,
349  const PJON_Packet_Info &packet_info
350  )
351  {
352  (
353  (PJONSimpleSwitch<Strategy>*)packet_info.custom_pointer
354  )->dynamic_receiver_function(
355  payload,
356  length,
357  packet_info
358  );
359  }
360 
361  static void error_function(uint8_t code, uint16_t data, void *custom_pointer)
362  {
363  ((PJONSimpleSwitch<Strategy>*)custom_pointer)->dynamic_error_function(
364  code,
365  data
366  );
367  }
368 };
data
char data[MAX_PAYLOAD_SIZE+1]
Buffer for raw payload data.
Definition: MyMessage.h:654
PJON_Packet_Info
Definition: PJONDefines.h:207
PJON
Definition: PJON.h:77
PJONSimpleSwitch
Definition: PJONSimpleSwitch.h:63