DUDS
Distributed Update of Data from Something
DigitalPort.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2017 Jeff Jackowski
9  */
12 
13 namespace duds { namespace hardware { namespace interface {
14 
15 DigitalPort::DigitalPort(unsigned int numpins, int unsigned firstid) :
16 pins(numpins), idOffset(firstid), waiting(0) { }
17 
19 
21  std::unique_lock<std::mutex> lock(block);
22  // find existing pins' global IDs
23  std::vector<unsigned int> gids;
24  gids.reserve(pins.size());
25  PinVector::iterator iter = pins.begin();
26  for (unsigned int gid = idOffset; iter != pins.end(); ++iter, ++gid) {
27  // pin exists?
28  if (*iter) {
29  // add its ID
30  gids.push_back(gid);
31  }
32  }
33  // wait on all pins to become available
34  waitForAvailability(lock, &(gids[0]), gids.size());
35  // remove all pins
36  pins.clear();
37  // awaken any threads waiting on access
38  if (waiting) {
39  pinwait.notify_all();
40  // wait on those threads
41  do {
42  // this could throw an exception, but there seems to be no good
43  // response
44  pinwait.wait(lock);
45  assert(waiting >= 0);
46  } while (waiting);
47  }
48 }
49 
50 bool DigitalPort::exists(unsigned int gid) const {
51  std::lock_guard<std::mutex> lock(block);
52  unsigned int lid = localId(gid);
53  // check for non-existence
54  if ((lid >= pins.size()) || !pins[lid]) {
55  return false;
56  }
57  return true;
58 }
59 
60 std::vector<unsigned int> DigitalPort::localIds(
61  const std::vector<unsigned int> &globalIds
62 ) const {
63  std::vector<unsigned int> localIds;
64  localIds.reserve(globalIds.size());
65  std::vector<unsigned int>::const_iterator iter = globalIds.cbegin();
66  for (; iter != globalIds.cend(); ++iter) {
67  // gap?
68  if (*iter == -1) {
69  // preserve the gap (-1 ID)
70  localIds.push_back(-1);
71  } else {
72  localIds.push_back(localId(*iter));
73  }
74  }
75  return localIds;
76 }
77 
78 std::vector<unsigned int> DigitalPort::globalIds(
79  const std::vector<unsigned int> &localIds
80 ) const {
81  std::vector<unsigned int> globalIds;
82  globalIds.reserve(localIds.size());
83  std::vector<unsigned int>::const_iterator iter = localIds.cbegin();
84  for (; iter != localIds.cend(); ++iter) {
85  // gap?
86  if (*iter == -1) {
87  // preserve the gap (-1 ID)
88  globalIds.push_back(-1);
89  } else {
90  globalIds.push_back(localId(*iter));
91  }
92  }
93  return globalIds;
94 }
95 /*
96 void DigitalPort::addPins(unsigned int maxId) {
97 
98 }
99 
100 void DigitalPort::removePin(unsigned int localPinId) {
101 
102 }
103 */
104 bool DigitalPort::areAvailable(const unsigned int *reqpins, std::size_t len) {
105  // check for no pins -- exit condition
106  if (pins.empty()) {
107  // other threads waiting on pins?
108  if (waiting) {
109  // awaken them so that they can exit (cascades)
110  pinwait.notify_one();
111  }
112  // no pins!
114  }
115  // check for availability of requested pins
116  for (; len; len--, reqpins++) {
117  // skip -1's
118  if (*reqpins != -1) {
119  unsigned int lid = localId(*reqpins);
120  // check for non-existent pin
121  if ((lid >= pins.size()) || !pins[lid]) {
123  DigitalPortAffected(this) << PinErrorId(*reqpins)
124  );
125  }
126  // check for pin in use
127  if (pins[lid].access) {
128  // will have to wait on it
129  return false;
130  }
131  }
132  }
133  return true;
134 }
135 
137  std::unique_lock<std::mutex> &lock,
138  const unsigned int * const reqpins,
139  const std::size_t len
140 ) {
141  // check on availability
142  while (!areAvailable(reqpins, len)) {
143  // count this as a waiting thread
144  waiting++;
145  // wait for notification
146  pinwait.wait(lock);
147  // no longer waiting
148  waiting--;
149  }
150 }
151 
153 
155 
156 void DigitalPort::retiredAccess(const DigitalPinAccess &acc) noexcept { }
157 
158 void DigitalPort::retiredAccess(const DigitalPinSetAccess &acc) noexcept { }
159 
161  const unsigned int *reqpins,
162  const unsigned int len,
163  std::unique_ptr<DigitalPinAccess> *acc
164 ) {
165  if (!len) {
167  }
168  // avoid throwing ObjectDestructedError when called on an empty port
169  if (pins.empty()) {
171  }
172  // need exclusive access
173  std::unique_lock<std::mutex> lock(block);
174  // wait for pins
175  waitForAvailability(lock, reqpins, len);
176  // get the access objects for each pin
177  for (int i = len; i; --i, ++reqpins, ++acc) {
178  // skip -1's
179  if (*reqpins != -1) {
180  unsigned int lid = localId(*reqpins);
181  // check once more for availability in case a pin is specified
182  // twice in pins
183  if (pins[lid].access) {
185  PinErrorId(*reqpins)
186  );
187  }
188  // create the access object
189  *acc = std::unique_ptr<DigitalPinAccess>(
190  new DigitalPinAccess(this, *reqpins)
191  );
192  // record the access
193  pins[lid].access = acc->get();
194  try {
195  // notify implementation of the new access object
196  madeAccess(**acc);
197  } catch (...) {
198  // implementation rejected access, so revoke access
199  for (; i <= len; ++i, --acc) {
200  updateAccess(**acc, nullptr);
201  }
202  throw;
203  }
204  }
205  }
206 }
207 /*
208 void DigitalPort::access(
209  const unsigned int *reqpins,
210  const unsigned int len,
211  std::unique_ptr<DigitalPinAccess[]> &acc
212 ) {
213  // avoid throwing ObjectDestructedError when called on an empty port
214  if (pins.empty()) {
215  DUDS_THROW_EXCEPTION(PinDoesNotExist() << DigitalPortAffected(this));
216  }
217  // need exclusive access
218  std::unique_lock<std::mutex> lock(block);
219  // wait for pins
220  waitForAvailability(lock, reqpins, len);
221  // need to allocate objects?
222  if (!acc) {
223  // allocate without constructing; leave uninitialized
224  acc.reset((DigitalPinAccess*)
225  new char(sizeof(DigitalPinAccess[len])));
226  }
227  // get the access objects for each pin
228  for (int i = 0; i < len; i++, reqpins++) {
229  // skip -1's
230  if (*reqpins != -1) {
231  unsigned int lid = localId(*reqpins);
232  // check once more for availability in case a pin is specified
233  // twice in pins
234  if (pins[lid].access) {
235  DUDS_THROW_EXCEPTION(PinInUse() << DigitalPortAffected(this) <<
236  PinErrorId(*reqpins)
237  );
238  }
239  // create the access object
240  new (&(acc[i])) DigitalPinAccess(pins[lid]);
241  // provide it to the DigitalPin (privilaged access)
242  pins[lid].access = &(acc[i]);
243  } else {
244  // make access object without a pin
245  new (&(acc[i])) DigitalPinAccess();
246  }
247  }
248 }
249 */
251  const unsigned int *reqpins,
252  const unsigned int len,
253  DigitalPinAccess *acc
254 ) {
255  if (!len) {
257  }
258  // avoid throwing ObjectDestructedError when called on an empty port
259  if (pins.empty()) {
261  }
262  // need exclusive access
263  std::unique_lock<std::mutex> lock(block);
264  // wait for pins
265  waitForAvailability(lock, reqpins, len);
266  // get the access objects for each pin
267  for (int i = 0; i < len; ++i, ++reqpins) {
268  // skip -1's
269  if (*reqpins != -1) {
270  unsigned int lid = localId(*reqpins);
271  // check once more for availability in case a pin is specified
272  // twice in pins
273  if (pins[lid].access) {
275  PinErrorId(*reqpins)
276  );
277  }
278  // create the access object
279  new (acc + i) DigitalPinAccess(this, *reqpins);
280  // record the access
281  pins[lid].access = &(acc[i]);
282  try {
283  // notify implementation of the new access
284  madeAccess(*(acc + i));
285  } catch (...) {
286  // implementation rejected access, so revoke access
287  for (; i >= 0; --i) {
288  updateAccess(*(acc + i), nullptr);
289  }
290  throw;
291  }
292  } else {
293  // make access object without a pin
294  new (acc + i) DigitalPinAccess();
295  }
296  }
297 }
298 
299 std::unique_ptr<DigitalPinAccess> DigitalPort::access(const unsigned int pin) {
300  std::unique_ptr<DigitalPinAccess> result(new DigitalPinAccess());
301  access(&pin, 1, &result);
302  return result;
303 }
304 
306  const unsigned int *reqpins,
307  const unsigned int len,
309 ) {
310  if (!len) {
312  }
313  // avoid throwing ObjectDestructedError when called on an empty port
314  if (pins.empty()) {
316  }
317  // supplied access object must either not be used by a port, or used by
318  // this port
319  if (acc.port() && acc.port() != this) {
321  }
322  // the port pointer might be null
323  acc.dp = this;
324  // need exclusive access
325  std::unique_lock<std::mutex> lock(block);
326  // wait for pins
327  waitForAvailability(lock, reqpins, len);
328  // pre-allocate space for pin IDs
329  acc.reserveAdditional(len);
330  // get the access to each pin
331  for (int i = 0; i < len; i++, reqpins++) {
332  // skip -1's
333  if (*reqpins != -1) {
334  unsigned int lid = localId(*reqpins);
335  // check once more for availability in case a pin is specified
336  // twice in pins
337  if (pins[lid].access) {
339  PinErrorId(*reqpins)
340  );
341  }
342  // provide access
343  acc.pinvec.push_back(lid);
344  // record the access
345  pins[lid].access = &acc;
346  } else {
347  // spot must still be used
348  acc.pinvec.push_back(-1);
349  }
350  }
351  try {
352  // notify implementation of the new access
353  madeAccess(acc);
354  } catch (...) {
355  // implementation rejected access, so revoke access
356  updateAccess(acc, nullptr);
357  throw;
358  }
359 }
360 
361 std::unique_ptr<DigitalPinSetAccess> DigitalPort::access(
362  const std::vector<unsigned int> &pins
363 ) {
364  std::unique_ptr<DigitalPinSetAccess> result(new DigitalPinSetAccess());
365  access(&(pins[0]), pins.size(), *(result.get()));
366  return result;
367 }
368 
370  const DigitalPinAccess &oldAcc,
371  DigitalPinAccess *newAcc
372 ) {
373  // need exclusive access
374  std::lock_guard<std::mutex> lock(block);
375  // sanity check; failures should be bug in library or memory corruption
376  assert(
377  // the access object has recorded use of this port object
378  (oldAcc.port() == this) &&
379  // within range of the vector
380  (oldAcc.localId() < pins.size()) &&
381  // pin marked as existing
382  pins[oldAcc.localId()] &&
383  // recorded access object is identical to one passed in
384  (pins[oldAcc.localId()].access == &oldAcc)
385  );
386  // notifty implementation of a retirement
387  if (!newAcc) {
388  retiredAccess(oldAcc);
389  }
390  // transfer access
391  pins[oldAcc.localId()].access = newAcc;
392  // notify any threads waiting on access
393  if (!newAcc && waiting) {
394  pinwait.notify_all();
395  }
396 }
397 
399  const DigitalPinSetAccess &oldAcc,
400  DigitalPinSetAccess *newAcc
401 ) {
402  // sanity check: the access object has recorded use of this port object
403  assert(oldAcc.port() == this);
404  // need exclusive access
405  std::lock_guard<std::mutex> lock(block);
406  // notifty implementation of a retirement
407  if (!newAcc) {
408  retiredAccess(oldAcc);
409  }
410  // iterate through the pins
411  std::vector<unsigned int>::const_iterator iter = oldAcc.pinvec.begin();
412  for (; iter != oldAcc.pinvec.end(); ++iter) {
413  // skip -1's
414  if (*iter != -1) {
415  // sanity check; failures should be bug in library or memory corruption
416  assert(
417  // within range of the vector
418  (*iter < pins.size()) &&
419  // pin marked as existing
420  pins[*iter] &&
421  // recorded access object is identical to one passed in
422  (pins[*iter].access == &oldAcc)
423  );
424  // transfer access
425  pins[*iter].access = newAcc;
426  }
427  }
428  // notify any threads waiting on access
429  if (!newAcc && waiting) {
430  pinwait.notify_all();
431  }
432 }
433 
435  unsigned int lid = localId(gid);
436  // assure no changes to the vector of pins
437  std::lock_guard<std::mutex> lock(block);
438  // check for non-existant pin
439  if ((lid >= pins.size()) || !pins[lid]) {
441  DigitalPortAffected(this)
442  );
443  }
444  // return copy of the config
445  return pins[lid].conf;
446 }
447 /*
448 DigitalPinConfig &DigitalPort::proposedPinConfig(
449  std::vector<DigitalPinConfig> &proposed,
450  unsigned int localPinId
451 ) const {
452  // find the proposed config object
453  DigitalPinConfig &cfg = proposed[localPinId];
454  // does it claim no change? this is construction default in configure()
455  if (cfg.options == DigitalPinConfig::OperationNoChange) {
456  // copy the current config to avoid querying it again
457  cfg = pins[localPinId].conf;
458  }
459  // return the proposed config
460  return cfg;
461 }
462 */
463 
464 DigitalPinCap DigitalPort::capabilities(unsigned int gid) const {
465  unsigned int lid = localId(gid);
466  // assure no changes to the pins from other threads
467  std::lock_guard<std::mutex> lock(block);
468  if ((lid >= pins.size()) || !pins[lid]) {
469  // no pin
471  DigitalPortAffected(this) << PinErrorId(gid)
472  );
473  }
474  return pins[lid].cap;
475 }
476 
477 std::vector<DigitalPinCap> DigitalPort::capabilities() const {
478  // assure no changes to the pins from other threads
479  std::lock_guard<std::mutex> lock(block);
480  std::vector<DigitalPinCap> caps;
481  caps.reserve(pins.size());
482  PinVector::const_iterator pin = pins.cbegin();
483  for (; pin != pins.cend(); ++pin) {
484  caps.push_back(pin->cap);
485  }
486  return caps;
487 }
488 
489 std::vector<DigitalPinCap> DigitalPort::capabilities(
490  const std::vector<unsigned int> &pvec, bool global
491 ) const {
492  // assure no changes to the pins from other threads
493  std::lock_guard<std::mutex> lock(block);
494  // allocate
495  std::vector<DigitalPinCap> res;
496  res.reserve(pins.size());
497  std::vector<unsigned int>::const_iterator iter = pvec.begin();
498  for (; iter != pvec.cend(); ++iter) {
499  // handle -1's
500  if (*iter == -1) {
501  res.push_back(NonexistentDigitalPin);
502  } else {
503  unsigned int lid;
504  if (global) {
505  lid = localId(*iter);
506  } else {
507  lid = *iter;
508  }
509  // non-existence check
510  if (lid >= pins.size()) {
511  // no pin
514  global ? *iter : globalId(*iter)
515  )
516  );
517  }
518  // push the capabilities
519  res.push_back(pins[lid].cap);
520  }
521  }
522  return res;
523 }
524 
525 std::vector<DigitalPinConfig> DigitalPort::configurationImpl() const {
526  std::vector<DigitalPinConfig> conf;
527  conf.reserve(pins.size());
528  PinVector::const_iterator pin = pins.cbegin();
529  for (; pin != pins.cend(); ++pin) {
530  conf.push_back(pin->conf);
531  }
532  return conf;
533 }
534 
535 std::vector<DigitalPinConfig> DigitalPort::configuration() const {
536  // assure no changes to the pins from other threads
537  std::lock_guard<std::mutex> lock(block);
538  return configurationImpl();
539 }
540 
541 std::vector<DigitalPinConfig> DigitalPort::configuration(
542  const std::vector<unsigned int> &pvec, bool global
543 ) const {
544  // assure no changes to the pins from other threads
545  std::lock_guard<std::mutex> lock(block);
546  // allocate
547  std::vector<DigitalPinConfig> res;
548  res.reserve(pins.size());
549  std::vector<unsigned int>::const_iterator iter = pvec.begin();
550  for (; iter != pvec.cend(); ++iter) {
551  // handle -1's
552  if (*iter == -1) {
554  } else {
555  unsigned int lid;
556  if (global) {
557  lid = localId(*iter);
558  } else {
559  lid = *iter;
560  }
561  // non-existence check
562  if (lid >= pins.size()) {
563  // no pin
566  global ? *iter : globalId(*iter)
567  )
568  );
569  }
570  // push the config
571  res.push_back(pins[lid].conf);
572  }
573  }
574  return res;
575 }
576 
578  unsigned int gid,
579  DigitalPinConfig &pconf,
580  DigitalPinConfig &iconf
581 ) const {
582  // assure no changes to the pins from other threads
583  std::unique_lock<std::mutex> lock(block);
584  return proposeConfigImpl(gid, pconf, iconf);
585 }
586 
588  const std::vector<unsigned int> &pins,
589  std::vector<DigitalPinConfig> &propConf,
590  std::vector<DigitalPinConfig> &initConf,
591  std::function<void(DigitalPinRejectedConfiguration::Reason)> insertReason
592 ) const {
593  // got global IDs, but need locals
594  std::vector<unsigned int> local = localIds(pins);
595  // assure no changes to the pins from other threads
596  std::lock_guard<std::mutex> lock(block);
597  return proposeConfigImpl(local, propConf, initConf, insertReason);
598 }
599 
601  const std::vector<unsigned int> &pins,
602  std::vector<DigitalPinConfig> &propConf,
603  std::vector<DigitalPinConfig> &initConf,
604  std::function<void(DigitalPinRejectedConfiguration::Reason)> insertReason
605 ) const {
606  // assure no changes to the pins from other threads
607  std::lock_guard<std::mutex> lock(block);
608  return proposeConfigImpl(pins, propConf, initConf, insertReason);
609 }
610 
612  std::vector<DigitalPinConfig> &propConf,
613  std::vector<DigitalPinConfig> &initConf,
614  std::function<void(DigitalPinRejectedConfiguration::Reason)> insertReason
615 ) const {
616  // assure no changes to the pins from other threads
617  std::lock_guard<std::mutex> lock(block);
618  return proposeFullConfigImpl(propConf, initConf, insertReason);
619 }
620 
622  unsigned int gid,
623  const DigitalPinConfig &cfg,
625 ) {
626  // assure no changes to the pins from other threads
627  std::lock_guard<std::mutex> lock(block);
628  // check range
629  unsigned int lid = localId(gid);
630  if ((lid >= pins.size()) || !pins[lid]) {
631  // no pin
633  PinErrorId(gid) << DigitalPortAffected(this) <<
634  // reason included because it matches the result of proposeConfig()
637  )
638  );
639  }
640  const DigitalPinConfig &iconf = pins[lid].conf;
641  DigitalPinConfig actcfg = DigitalPinConfig::combine(iconf, cfg);
642  // If this should be speedy and unsafe, call configurePort here and
643  // skip the rest, except for pins[lid].conf = cfg;
644  // test compatability - may throw
646  pins[lid].cap.compatible(actcfg);
647  if (err) {
649  PinErrorId(gid) << DigitalPortAffected(this) <<
651  DigitalPinCapInfo(pins[lid].cap) <<
652  DigitalPinConfigInfo(actcfg)
653  );
654  }
655  // check for an independent configuration
656  if (independentConfig(gid, actcfg, iconf)) {
657  // make changes
658  configurePort(lid, actcfg, pdata);
659  // record the new config
660  pins[lid].conf = actcfg;
661  } else {
662  // prepare to reconfigure whole port
663  std::vector<DigitalPinConfig> initConf = configurationImpl();
664  std::vector<DigitalPinConfig> propConf(initConf);
665  propConf[lid] = actcfg;
666  // do it -- implementation used by both modifyConfig functions
667  modifyFullConfig(propConf, initConf, pdata);
668  }
669  return actcfg;
670 }
671 
673  std::vector<DigitalPinConfig> &propConf,
674  std::vector<DigitalPinConfig> &initConf,
676 ) {
677  // prepare data for config check
678  std::vector<DigitalPinRejectedConfiguration::Reason> errs;
679  // generate complete config and validate the config; check for error
681  propConf,
682  initConf,
684  errs.push_back(e);
685  }
686  )) {
687  // badness!
690  DigitalPortAffected(this)
691  );
692  }
693  // apply config
694  configurePort(propConf, pdata);
695  // record new config
696  PinVector::iterator pin = pins.begin();
697  std::vector<DigitalPinConfig>::const_iterator conf = propConf.cbegin();
698  for (; conf != propConf.cend(); ++conf, ++pin) {
699  pin->conf = *conf;
700  }
701 }
702 
704  std::vector<DigitalPinConfig> &cfgs,
706 ) {
707  // assure no changes to the pins from other threads
708  std::lock_guard<std::mutex> lock(block);
709  // prepare data for config check
710  std::vector<DigitalPinConfig> initConf = configurationImpl();
711  // do it -- implementation used by both modifyConfig functions
712  modifyFullConfig(cfgs, initConf, pdata);
713 }
714 
716  const std::vector<unsigned int> &pvec,
717  std::vector<DigitalPinConfig> &cfgs,
719 ) {
720  // inputs must match size and not be empty
721  if (cfgs.empty() || (cfgs.size() != pvec.size())) {
723  DigitalPortAffected(this)
724  );
725  }
726  // assure no changes to the pins from other threads
727  std::lock_guard<std::mutex> lock(block);
728  // produce a config for the whole port
729  std::vector<DigitalPinConfig> initConf = configurationImpl();
730  std::vector<DigitalPinConfig> propConf = initConf;
731  std::vector<unsigned int>::const_iterator piter = pvec.begin();
732  std::vector<DigitalPinConfig>::const_iterator citer = cfgs.begin();
733  for (; piter != pvec.end(); ++citer, ++piter) {
734  // range & existence check
735  if ((*piter != -1) && (*piter < pins.size()) && pins[*piter]) {
736  // produce finalized configuration
737  propConf[*piter].combine(*citer);
738  }
739  }
740  // do it -- implementation used by both modifyConfig functions
741  modifyFullConfig(propConf, initConf, pdata);
742 }
743 
744 bool DigitalPort::input(unsigned int gid, DigitalPinAccessBase::PortData *pdata) {
745  unsigned int lid = localId(gid);
746  // assure no changes to the pins from other threads
747  std::lock_guard<std::mutex> lock(block);
748  // out-of-range & non-existence check
750  if ((lid >= pins.size()) || !pins[lid]) {
752  << PinErrorId(gid)
753  );
754  }
755  // check for a non-input configuration
756  if (!(pins[lid].conf & DigitalPinConfig::DirInput)) {
758  DigitalPortAffected(this) << PinErrorId(gid)
759  );
760  }
761  // passed error checks; do the output
762  return inputImpl(gid, pdata);
763 }
764 
765 std::vector<bool> DigitalPort::input(
766  const std::vector<unsigned int> &pvec,
768 ) {
769  // assure no changes to the pins from other threads
770  std::lock_guard<std::mutex> lock(block);
771  // check all pins existence and input config
772  std::vector<unsigned int>::const_iterator piter = pvec.cbegin();
773  for (; piter != pvec.cend(); ++piter) {
774  // out-of-range & non-existence check
776  if ((*piter >= pins.size()) || !pins[*piter]) {
778  << PinErrorId(globalId(*piter))
779  );
780  }
781  // check for a non-input configuration
782  if (!(pins[*piter].conf & DigitalPinConfig::DirInput)) {
784  DigitalPortAffected(this) << PinErrorId(globalId(*piter)) <<
785  DigitalPinConfigInfo(pins[*piter].conf)
786  );
787  }
788  }
789  // passed error checks; do the input
790  return inputImpl(pvec, pdata);
791 }
792 
793 std::vector<bool> DigitalPort::inputImpl(
794  const std::vector<unsigned int> &pvec,
796 ) {
797  // using this implementation only makes sense if simultaneous operations
798  // are not supported
799  assert(!simultaneousOperations());
800  std::vector<bool> res;
801  res.reserve(pvec.size());
802  std::vector<unsigned int>::const_iterator iter = pvec.cbegin();
803  for (auto pid : pvec) {
804  res.push_back(inputImpl(globalId(pid), pdata));
805  }
806  return res;
807 }
808 
810  unsigned int gid,
811  bool state,
813 ) {
814  unsigned int lid = localId(gid);
815  // assure no changes to the pins from other threads
816  std::lock_guard<std::mutex> lock(block);
817  // out-of-range & non-existence check
819  if ((lid >= pins.size()) || !pins[lid]) {
821  << PinErrorId(gid)
822  );
823  }
824  // no output capability check
825  if (!pins[lid].cap.canOutput()) {
827  DigitalPortAffected(this) << PinErrorId(gid) <<
828  DigitalPinCapInfo(pins[lid].cap)
829  );
830  }
831  // passed error checks; do the output
832  outputImpl(lid, state, pdata);
833 }
834 
836  const std::vector<unsigned int> &pvec,
837  const std::vector<bool> &state,
839 ) {
840  // the inputs must be the same size
841  if (pvec.size() != state.size()) {
843  DigitalPortAffected(this)
844  );
845  }
846  // assure no changes to the pins from other threads
847  std::lock_guard<std::mutex> lock(block);
848  // check all pins existence and output capability
849  std::vector<unsigned int>::const_iterator piter = pvec.cbegin();
850  for (; piter != pvec.cend(); ++piter) {
851  // out-of-range & non-existence check
853  if ((*piter >= pins.size()) || !pins[*piter]) {
855  << PinErrorId(globalId(*piter))
856  );
857  }
858  // no output capability check
859  if (!pins[*piter].cap.canOutput()) {
861  DigitalPortAffected(this) << PinErrorId(globalId(*piter))
862  );
863  }
864  }
865  // passed error checks; do the output
866  outputImpl(pvec, state, pdata);
867 }
868 
870  const std::vector<unsigned int> &pvec,
871  const std::vector<bool> &state,
873 ) {
874  // using this implementation only makes sense if simultaneous operations
875  // are not supported
876  assert(!simultaneousOperations());
877  // loop through the pins to handle each in turn
878  std::vector<unsigned int>::const_iterator piter = pvec.cbegin();
879  std::vector<bool>::const_iterator biter = state.cbegin();
880  for (; piter != pvec.cend(); ++biter, ++piter) {
881  outputImpl(*piter, *biter, pdata);
882  }
883 }
884 
885 } } }
bool proposeFullConfig(std::vector< DigitalPinConfig > &propConf, std::vector< DigitalPinConfig > &initConf, std::function< void(DigitalPinRejectedConfiguration::Reason)> insertReason=std::function< void(DigitalPinRejectedConfiguration::Reason)>()) const
unsigned int localId() const
Returns the local pin ID of the accessed pin.
virtual ~DigitalPort()
Derived classes should call shutdown() early in their destructors.
Definition: DigitalPort.cpp:18
boost::error_info< struct Info_DigitalPinConfig, DigitalPinConfig > DigitalPinConfigInfo
virtual bool simultaneousOperations() const =0
True if the implementation supports operating on multiple pins simultaneously.
Provides access to multiple pins on a DigitalPort.
bool input(unsigned int gid, DigitalPinAccessBase::PortData *pdata)
Does error checking in advance of calling inputImpl(unsigned int) to read the input of the given pin...
DigitalPort * port() const
Returns a pointer to the port that controls the pin(s) that are operated through this object...
std::vector< unsigned int > localIds(const std::vector< unsigned int > &globalIds) const
Converts the provided global pin IDs to local pin IDs.
Definition: DigitalPort.cpp:60
static constexpr Flags DirInput
Configure the pin for input.
Defines the configuration for a digital general purpose I/O pin.
constexpr DigitalPinCap NonexistentDigitalPin
The capabilities of a non-existent pin.
bool areAvailable(const unsigned int *reqpins, std::size_t len)
Checks a set of pins to see if they are all currently available.
unsigned int localId(unsigned int globalId) const
Returns the local ID for a pin given the global ID.
boost::error_info< struct Info_DigitalPinCap, DigitalPinCap > DigitalPinCapInfo
DigitalPinConfig modifyConfig(unsigned int globalPinId, const DigitalPinConfig &cfg, DigitalPinAccessBase::PortData *pdata)
Modifies the configuration of a single pin with an independent configuration.
virtual bool independentConfig() const =0
Returns true if all pins always have an independent configuration from all other pins.
A request was made to access zero pins.
Definition: PinErrors.hpp:81
bool exists(unsigned int gid) const
Returns true if the pin exists in this port.
Definition: DigitalPort.cpp:50
constexpr Reason Unsupported
Completely unsupported.
unsigned int globalId(unsigned int localId) const
Returns the global ID for a pin given the local ID.
virtual void configurePort(unsigned int localPinId, const DigitalPinConfig &cfg, DigitalPinAccessBase::PortData *pdata)=0
Changes the hardware configuration for a single pin.
void access(const unsigned int *pins, const unsigned int len, std::unique_ptr< DigitalPinAccess > *acc)
Obtain access objects to use a set of pins.
A pin required for the operation does not exist or is unavailable to the process. ...
Definition: PinErrors.hpp:70
boost::error_info< struct Info_PinId, unsigned int > PinErrorId
The pin global ID involved in the error.
Definition: PinErrors.hpp:97
A type for holding arbitrary port-specific data within a DigitalPinAccess or DigitalPinSetAccess obje...
An error indicating an attempt to use an already destructed object.
Definition: Errors.hpp:85
virtual void madeAccess(DigitalPinAccess &acc)
Called after a new access object is made to allow a port implementation to take further action...
boost::error_info< struct Info_DigitalPortAffected, const DigitalPort * > DigitalPortAffected
Added to exceptions thrown by DigitalPort objects.
void modifyFullConfig(std::vector< DigitalPinConfig > &propConf, std::vector< DigitalPinConfig > &initConf, DigitalPinAccessBase::PortData *pdata)
Does the work of the modifyConfig() functions in the case that the whole port configuration must be c...
boost::error_info< struct Info_DigitalPinRejectedConfiguration, Reason > ReasonInfo
Allows attaching the configuration rejection flags to an exception.
Indicates an error with the digital pin&#39;s configuration.
std::mutex block
Used to serialize access to internal data for thread-safe operation.
Definition: DigitalPort.hpp:55
bool proposeConfigLocalIds(const std::vector< unsigned int > &localPinIds, std::vector< DigitalPinConfig > &propConf, std::vector< DigitalPinConfig > &initConf, std::function< void(DigitalPinRejectedConfiguration::Reason)> insertReason=std::function< void(DigitalPinRejectedConfiguration::Reason)>()) const
virtual bool proposeFullConfigImpl(std::vector< DigitalPinConfig > &propConf, std::vector< DigitalPinConfig > &initConf, std::function< void(DigitalPinRejectedConfiguration::Reason)> insertReason=std::function< void(DigitalPinRejectedConfiguration::Reason)>()) const =0
void reserveAdditional(unsigned int len)
Reserves additional space in pins so that upcoming pushes onto the vector will not cause multiple mem...
virtual void retiredAccess(const DigitalPinAccess &acc) noexcept
Called just before an access object is retired to allow a port implementation to take further action...
unsigned int idOffset
An offset used to translate pin identification numbers between global scope and local scope...
void shutdown()
Waits for access to all pins so that any user of access objects may finish with their operation...
Definition: DigitalPort.cpp:20
std::vector< unsigned int > globalIds(const std::vector< unsigned int > &localIds) const
Converts the provided local pin IDs to global pin IDs.
Definition: DigitalPort.cpp:78
An attempt was made to use a DigitalPinSetAccess object with two different DigitalPort objects...
Definition: PinErrors.hpp:76
std::vector< DigitalPinCap > capabilities() const
Returns the capabilities of all pins in the port.
void updateAccess(const DigitalPinAccess &oldAcc, DigitalPinAccess *newAcc)
Transfers or relinquishes access to pins.
Provides access to a single pin on a DigitalPort.
int waiting
A count of the threads waiting to access pins.
A type-safe bit flag storage class.
Definition: BitFlags.hpp:101
void combine(const DigitalPinConfig &newCfg)
Combines this configuration with a newer configuration taking into account requests to not change cer...
virtual void outputImpl(unsigned int lid, bool state, DigitalPinAccessBase::PortData *pdata)=0
Changes the output state of the given pin.
void output(unsigned int gid, bool state, DigitalPinAccessBase::PortData *pdata)
Does error checking in advance of calling outputImpl(unsigned int, bool) to change the output of the ...
std::condition_variable pinwait
Used to efficently wait for resources to become available.
Definition: DigitalPort.hpp:59
Indicates that the specified configuration data includes too many or too few items, or has parallel data structures of inconsistent sizes.
Indicates that a request to configure a pin to output was made of a pin that cannot output...
DigitalPort(unsigned int numpins, unsigned int firstid)
Initializes internal data.
Definition: DigitalPort.cpp:15
A DigitalPinAccess or DigitalPinSetAccess object cannot be obtained because one already exists with a...
Definition: PinErrors.hpp:58
DigitalPinRejectedConfiguration::Reason proposeConfig(unsigned int gid, DigitalPinConfig &pconf, DigitalPinConfig &iconf) const
Proposes a configuration change for a single pin.
Defines the capabilites of a digital general purpose I/O pin.
virtual bool inputImpl(unsigned int gid, DigitalPinAccessBase::PortData *pdata)=0
Reads input from the given pin.
static constexpr Flags OperationNoChange
No change to any pin operation.
The requested operation requires the use of the wrong, or not the current, I/O direction.
Definition: PinErrors.hpp:44
std::vector< DigitalPinConfig > configurationImpl() const
Returns the configuration of all pins in the port.
PinVector pins
Data on each pin handled by the port.
std::vector< unsigned int > pinvec
The port local pin IDs this object may use.
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
std::vector< DigitalPinConfig > configuration() const
Returns the configuration of all pins in the port.
virtual DigitalPinRejectedConfiguration::Reason proposeConfigImpl(unsigned int gid, DigitalPinConfig &pconf, DigitalPinConfig &iconf) const =0
void waitForAvailability(std::unique_lock< std::mutex > &lock, const unsigned int *const reqpins, const std::size_t len)
Waits for all pins to become available.
boost::error_info< struct Info_DigitalPinRejectedConfigurationVector, std::vector< Reason >> ReasonVectorInfo