Compare commits

...

108 Commits

Author SHA1 Message Date
Kevin O'Connor
0e46592ad1 Don't send DS errors if no DS.
Change-Id: I765d8c21371e772ec09bf696f6dd2fff43f8ed24
2015-02-23 16:19:16 -05:00
Brad Miller (WPI)
d2edb80da2 Merge "Use PCM Device ID when getting status. Fixes artf4014." 2015-02-23 13:15:26 -08:00
Brad Miller
6159fde98e Set POV values on error to return -1 rather than 0 AND bump the version number (artf4005)
Change-Id: I6fffb693a8e58427086b9f9a673cd70bebdbca33
2015-02-20 18:35:38 -05:00
Joe Ross
7bdd91a058 Use PCM Device ID when getting status. Fixes artf4014.
Change-Id: I0e1f478d1b96d20b97dec8d0ffe251c8fd0f6897
2015-02-15 16:43:09 -08:00
Kevin O'Connor
605bb45e0c Fix endian order of buffers in AnalogTrigger.java
Change-Id: Ica262494c4b8169d812295006e24f9cb440de078
2015-02-13 11:16:55 -05:00
Brad Miller
215002e487 Fix analog trigger bug with MXP mapping (artf4010)
Add the interrupt code for MXP mapping and analog triggers
Took out the unneeded static definition
Change-Id: I9a3483ee8f806b46b4349845e7a189f497c36916
2015-02-12 17:33:31 -05:00
Brad Miller
964475c724 Bump the version number to 1.1 for the network table changes
Change-Id: Id3ed20c88de5744acfe95ed0916c3c7f3fc0e386
2015-02-10 17:21:02 -05:00
Fredric Silberberg
bfa4bbaf78 Final move of Dustins network tables patches
Change-Id: I7d351d87e63c8174b62f4ec09f805f685c80019c
2015-02-09 14:22:44 -05:00
Mitchell Wills
39158754d7 Partial merge of patch from James Killian
Narrows scope of locks in write manager
added delay to incomming stream monitor loop

Change-Id: I6ced23c11cd3c2102041a73cb94fd1f1d351c7a4
2015-02-07 19:56:12 -05:00
Mitchell Wills
048b02e6cd Applied patch from Dustin Spicuzza to fix robot hang
Change-Id: I273feafcad5c95de04a11bab64c2d5596f248662
2015-02-07 19:56:11 -05:00
Fred Silberberg (WPI)
130319b771 Merge "Fixes artf3989: Java SerialPort no longer ignores return value of serialRead" 2015-01-21 14:58:09 -08:00
Thomas Clark (WPI)
0b414b1bde Merge "Disable Encoder console spam introduced in e13720b." 2015-01-21 14:05:01 -08:00
Thomas Clark (WPI)
8a244cc9dc Merge "Make index encoder constructors work." 2015-01-21 14:03:17 -08:00
Brad Miller (WPI)
397162390b Merge "Correct energy to be in Joules rather then millijoules. Fixes artf3940" 2015-01-21 12:15:23 -08:00
Joe Ross
bbc216f998 Disable Encoder console spam introduced in e13720b.
Change-Id: I10501f9bb9e8f8e0607291209f9a44a8dc3fa6b8
2015-01-18 18:57:34 -08:00
Joe Ross
72a56e6a8a Make index encoder constructors work.
Change-Id: I50e1262128197e7d65ed773c998d820efde152fe
2015-01-17 22:28:02 -08:00
Colby Skeggs
102c992e50 Fixes artf3989: Java SerialPort no longer ignores return value of serialRead
Change-Id: I5d522111189372a3c95b90804abf05ae1d0b553e
2015-01-18 01:30:19 +00:00
Joe Ross
9f057fddfd Correct energy to be in Joules rather then millijoules. Fixes artf3940
Note: power and thus energy will still be wrong until PDP firmware 1.39
or later is used. See artf3939.
Change-Id: Ic68b8a28c78f1b5d524fa5fb45e3bc0d9cd4a76a
2015-01-17 09:59:25 -08:00
Brad Miller
e614217d41 New netconsole-host that overwrites the old one on every deploy
Change-Id: I9f9e94a67810b1b40b354a2bfc511f44a05b858a
2015-01-16 21:03:52 -05:00
Kevin O'Connor
9b7042a51a Fix errors in Vision examples.
Change-Id: I752a99ff7b8872a0fc8926e3633c2b521383bbd0
2015-01-16 15:35:48 -05:00
Brad Miller (WPI)
081d7f7361 Merge "Applied updated DIOJNI file from Ron Rossbach. Fixes artf3982." 2015-01-16 07:36:17 -08:00
Brad Miller (WPI)
352c021b17 Merge "Updated the Java CameraServer to use the new USBCamera and prevent large camera data copies. Fixed a small bug in USBCamera to prevent reinitializing a camera if it is already initialized. Also fixed some issues with the getJpegSize function to correct for Java incorrectly casting 0xff to an integer, and the byte buffer limit being set incorrectly." 2015-01-16 07:35:32 -08:00
Brad Miller (WPI)
5f90bf167e Merge "Enable nivision Java wrappers to be generated with Python 3." 2015-01-16 07:35:17 -08:00
Brad Miller (WPI)
445eafefb3 Merge "More Java nivision wrapper fixes." 2015-01-16 07:34:33 -08:00
Brad Miller (WPI)
a917f63e7d Merge "2015 Color and Retroreflective vision samples for C++ and Java" 2015-01-16 07:33:21 -08:00
Brad Miller (WPI)
2228361f39 Merge "Add SetCANJaguarSyncGroup to RobotDrive" 2015-01-16 07:32:57 -08:00
Fredric Silberberg
aaa599fa28 Updated the Java CameraServer to use the new USBCamera and prevent large
camera data copies. Fixed a small bug in USBCamera to prevent
reinitializing a camera if it is already initialized. Also fixed some
issues with the getJpegSize function to correct for Java incorrectly
casting 0xff to an integer, and the byte buffer limit being set incorrectly.

Change-Id: I184efd265c617b02523dd9c5d347cc7ca5b4a77b
2015-01-15 19:27:00 -05:00
Peter Johnson
5d95bbb33f Enable nivision Java wrappers to be generated with Python 3.
Change-Id: I8ce57874e4730ed10dd0093c147b14c22f141201
2015-01-15 00:04:23 -08:00
Peter Johnson
d0258923e8 More Java nivision wrapper fixes.
- Fix sliceByteBuffer, getBytes, and putBytes implementations, which had
  functional errors.  Also, getBytes and putBytes now use the ByteBuffer
  get/put byte[] functions, which should improve performance.

- Don't generate wrappers for functions that are not available in the
  shared library.

Change-Id: Iaf45814b34720d3fdcd58adf99ad9c3ff2703bc3
2015-01-14 23:49:37 -08:00
Kevin O'Connor
91ccc5d10b 2015 Color and Retroreflective vision samples for C++ and Java
Change-Id: Id95925ced100b25d591c40995bb016780737312d
2015-01-14 12:45:17 -05:00
Brad Miller (WPI)
9fda920055 Merge "Add @file annotation so files are picked up in doxygen" 2015-01-14 07:54:34 -08:00
Brad Miller (WPI)
e9dd72ac77 Merge changes I0ec40c14,I4d099e62
* changes:
  nivision Java wrappers: implement our own error text function.
  Fix a few major nivision Java wrapper issues.
2015-01-14 07:53:48 -08:00
Brad Miller (WPI)
cce1bbc1f2 Merge "Update to point to Gazebo3.2 header location." 2015-01-14 07:41:18 -08:00
Peter Johnson
9fc79c41d1 nivision Java wrappers: implement our own error text function.
Change-Id: I0ec40c143b1c5a5fff8495643f83ad494ab32e57
2015-01-14 01:08:29 -08:00
Joe Ross
1dba916887 Add @file annotation so files are picked up in doxygen
Change-Id: Ie2e0bdd23d29dbf91600a25e3c0ae0228519b41a
2015-01-13 22:44:54 -08:00
Peter Johnson
ce7a56284f Fix a few major nivision Java wrapper issues.
- sliceByteBuffer() was not setting native order on the duplicated buffer.
  This caused all array-copyin functions to generate bad values.
- Correctly handle unsigned byte and unsigned short values.  These could
  read/write to bad locations previously.
- Implement custom version of imaqReadFile() to always pass in NULL for
  the colorTable.  Eventually a more-complete version should be written.

Also this works around a crash in imaqGetErrorText() by not calling it from
throwJavaException().  It's not clear why imaqGetErrorText() is crashing at
present (my best guess is there's still something fishy with multiple C++
lib versions getting loaded somehow), as this used to work.  Instead,
the exception now just gives the error code without the error message,
which is not user friendly but at least doesn't crash.  This will be fixed
in a future commit by creating our own version of imaqGetErrorText() based
on the information available in the header file.

Change-Id: I4d099e62ee41f8e2a50089806561be191cb5d9d7
2015-01-13 22:18:17 -08:00
Joe Ross
34d515debb Make sure Utility file is included in doxygen output.
Fixes http://www.chiefdelphi.com/forums/showpost.php?p=1427109&postcount=3

Change-Id: I716cd6be21980945c4a7846f5c32e50e040be3e9
2015-01-12 22:44:02 -08:00
Brad Miller (WPI)
cff103d15e Merge "Add encoder indexing in Java" 2015-01-12 12:48:39 -08:00
Brad Miller (WPI)
1b5cabc0d1 Merge "Add encoder indexing support in C++" 2015-01-12 12:48:23 -08:00
Brad Miller (WPI)
9fcfc5b3b7 Merge "Added the C++ implementation of Peter Johnson's UsbCamera. Rewrote CameraServer to use the new USBCamera implementation and get rid of some unnecessary copying of the entire image." 2015-01-12 10:45:57 -08:00
Brad Miller (WPI)
f181c70134 Merge "Add USBCamera class." 2015-01-12 10:45:41 -08:00
Brad Miller (WPI)
46c40da539 Merge "NIVision: Add dx functions." 2015-01-12 10:45:20 -08:00
Brad Miller (WPI)
1c05a982e1 Merge "Synchronize CANTalon documentation between languages." 2015-01-12 10:43:37 -08:00
Thomas Clark
ba7f56833b Add SetCANJaguarSyncGroup to RobotDrive
Manually specifying a sync group causes that value to be
passed in all of the speed controller Set() calls, and
CANJaguar::UpdateSyncGroup() to be called afterwards.

Change-Id: I365216463e3dd57b3fafb1bed898fb0da4b08793
2015-01-12 01:27:53 -05:00
Fredric Silberberg
7600473da6 Applied updated DIOJNI file from Ron Rossbach. Fixes artf3982.
Change-Id: Ibf20e833d7ab5b1ca80239ad5b8bb116c1624fca
2015-01-11 14:34:26 -05:00
Peter Johnson
9f04b79580 Fix Timer.hasPeriodPassed().
It was incorrectly using seconds instead of milliseconds.

Change-Id: Iecd2802c548caee4a5185f00a2b785143d6b1b16
2015-01-11 01:55:33 -08:00
Fredric Silberberg
2282a11a79 Added the C++ implementation of Peter Johnson's UsbCamera.
Rewrote CameraServer to use the new USBCamera implementation and get rid
of some unnecessary copying of the entire image.

Change-Id: I877750e990b6159c0aaf829df62b253a171fbada
2015-01-09 14:39:47 -05:00
Peter Johnson
1a2344bddc Add USBCamera class.
Change-Id: Ie7dfa679d9eeb7d55613ac1694a63ce70161d76c
2015-01-08 21:14:32 -08:00
Fred Silberberg (WPI)
ae9a7d19f1 Merge "Miscellaneous AnalogTrigger fixes" 2015-01-08 14:07:45 -08:00
Fred Silberberg (WPI)
e1141b9f27 Merge "Disable C optimize setting so better C++ doc is generated" 2015-01-08 13:47:10 -08:00
Fred Silberberg (WPI)
ff71aca572 Merge "Update docs to add missing channel numbers" 2015-01-08 13:44:32 -08:00
Fred Silberberg (WPI)
837671e95f Merge "Document that PDP must be address 0 and add/fix measurement units" 2015-01-08 13:43:36 -08:00
Fred Silberberg (WPI)
38c0308fc7 Merge "Correct path of sfx so that it can be launched from eclipse menu" 2015-01-08 13:42:43 -08:00
Thomas Clark
f21e4a9da5 Miscellaneous AnalogTrigger fixes
AnalogTrigger.createOutput is now public.  It was mistakenly made
package local before.

The AnalogTriggerType enum now uses the same naming convension
as other enumerations in WPILib.  There's no way that this breaks
existing code, since AnalogTrigger.createOutput wasn't public before
this commit.

Change-Id: I9a2b921a996012f61dac0e395602f8cc429d7611
2015-01-08 00:31:36 -05:00
Thomas Clark
e13720bb94 Add encoder indexing in Java
Change-Id: I7d6a7da4301703aafab9d63ce67b6c88c62647c9
2015-01-08 00:23:32 -05:00
Joe Ross
0cf7d6f457 Update docs to add missing channel numbers
Change-Id: I2ffcc84c4801c5bd1062b74f36dd35e06b4834e6
2015-01-07 20:15:26 -08:00
Thomas Clark
a2dfffeddc Add encoder indexing support in C++
Java will be done tonight or tomorrow

Change-Id: I3a3287a197b6f071c261172eb8ec930693e8b3c7
2015-01-07 00:06:45 -05:00
Brad Miller
87e1df068c Fixed a null pointer exception in the mecanum drive sample program (Fixes artf3974)
Change-Id: I9518cd49ca815b9e1dc3ab63b7a4ee7034e5b444
2015-01-05 17:34:13 -05:00
Peter Johnson
f19c290fde NIVision: Add dx functions.
This includes getters, setters, video mode enumerate, and get image data.

These are necessary to improve the CameraServer code performance.

Also clean up unused return generation.

Change-Id: I3e7365e046d0ea9370586c5158138a5b07e20218
2015-01-05 01:11:49 -08:00
Joe Ross
5c342a8e1a Document that PDP must be address 0 and add/fix measurement units
Change-Id: I4864e3242d8a19ad3dfb7740f23632e2b939ca90
2015-01-02 16:08:19 -08:00
Joe Ross
f7117aac4b Correct path of sfx so that it can be launched from eclipse menu
Change-Id: I54f35d480afbe1d356ee5a55e8c04e62f94ee757
2015-01-02 15:41:04 -08:00
Alex Henning
05cd0627b2 Update to point to Gazebo3.2 header location.
Change-Id: Ifebb31a375e8b7a6136115748c3634757fb732b8
2015-01-02 18:34:35 -05:00
Joe Ross
2bc83bd80a Disable C optimize setting so better C++ doc is generated
Change-Id: I9cc98c83f47013e174835a6e418c49305ef360e5
2014-12-31 20:03:44 -08:00
Joe Ross
79fbbbc0dd Synchronize CANTalon documentation between languages.
Still lots of documentation needed.

Change-Id: I122d00fb0f4a9f57f3d479749ec02b2249856d2b
2014-12-31 19:18:52 -08:00
Brad Miller (WPI)
359a155915 Merge "Make 3 axis Accelerometers work in LiveWindow. Fixes artf3902." 2014-12-30 21:05:26 -08:00
Brad Miller
fb7f5e9de8 Add methods to retreive the FPGA index for counters and encoders and return the encoding type as an integer
Change-Id: Iaa4062ec124b968b27e7c812cc754032fcb354d2

Add methods to retrieve the FPGA index for counters and encoders and return the encoding type as an integer

Change-Id: If04dc291f39a9b331495d2b97a8b87d3ded71280
2014-12-30 23:23:04 -05:00
Joe Ross
7db63055f6 Make 3 axis Accelerometers work in LiveWindow. Fixes artf3902.
Change-Id: Iced12b3c408b0d7b8fe822a9f17a7992293bae7d
2014-12-30 19:59:25 -08:00
Thomas Clark (WPI)
b94341a23e Merge "Update nivision JNI generator to correctly gen Priv_ReadJPEGString_C." 2014-12-30 14:18:00 -08:00
Peter Johnson
afbd70d393 Update nivision JNI generator to correctly gen Priv_ReadJPEGString_C.
Change-Id: I206da9f3f660b4d567a3b535702b70e5f967b071
2014-12-30 13:24:30 -08:00
Joe Ross
ceccd95084 Add PDP to LiveWindow for C++
Change-Id: I45967739b81090e809b9e8f1c31ad7ff58554ec1
2014-12-30 12:31:35 -08:00
Brad Miller (WPI)
91b9812482 Merge "Add PDP LiveWindow code" 2014-12-30 11:57:28 -08:00
Brad Miller
0dbc40e515 Added missing .java file
Change-Id: I96aa235960bc59f435b7b1b55a29462f8f8052e4
2014-12-30 14:35:06 -05:00
Brad Miller
5ce89b3382 Added a Java AxisCamera sample program
Change-Id: Iba0eb9f06a91f577323cefd695f8dd601a96db97
2014-12-30 10:27:20 -05:00
Thomas Clark
8ccf3d9c14 Add AxisCamera Java class
The API is basically the same as the C++ one.

The JNI function for Priv_ReadJPEGString_C was manually renamed, since the
python scripts don't name the C++ functions correctly, causing an
UnsatisfiedLinkError at runtime. If further changes are made to the bindings,
either the method will have to be manually renamed again after the code is
regenerated, or the python scripts will have to be updated.

The old ignored edu.wpi.first.wpilibj.camera package was removed.

Change-Id: Icd37fc15c7bb41061568c3b2f580c6765cbf0300
2014-12-30 02:17:03 -05:00
Joe Ross
230fa2d168 Add PDP LiveWindow code
Change-Id: Ia3f4d8118d9068d021994fd2e5aca676d7dfa979
2014-12-29 21:10:21 -08:00
Joe Ross
d165f57e6e Clean up misc documentation
Change-Id: Ib0a4a7a389fb657d78d8b8bce4fcaeed67c3693d
2014-12-29 15:10:41 -08:00
Brad Miller (WPI)
2d15b6b03e Merge "Update Java install URL to the new URL with the java installer." 2014-12-29 13:38:47 -08:00
Brad Miller (WPI)
5d9baa44ee Merge changes Ie40db6c9,I0b5e752e
* changes:
  Write lib version from Java (fixes artf3788)
  Update Javadocs for Java (fixes artf3761 and artf3953)
2014-12-29 13:35:52 -08:00
Brad Miller (WPI)
822416d2f7 Merge "Write lib version from C++ (fixes artf3788)" 2014-12-29 13:35:04 -08:00
Brad Miller (WPI)
3fe726d851 Merge "Update docs for C++ (fixes artf3761 and artf3953)" 2014-12-29 13:26:13 -08:00
Kevin O'Connor
4c0b2c18ab Write lib version from Java (fixes artf3788)
Change-Id: Ie40db6c993d12aadb66b07fcfdc0a62b87a2e2ed
2014-12-29 16:06:11 -05:00
Kevin O'Connor
b4c5a3af77 Update Javadocs for Java (fixes artf3761 and artf3953)
Change-Id: I0b5e752e8f6377770163dcdc61aa79ad4c7cbe94
2014-12-29 16:02:07 -05:00
Kevin O'Connor
eddb0b20b0 Write lib version from C++ (fixes artf3788)
Change-Id: Ia11c2dab2050ffe4299d1ccd68c986573f0a05e3
2014-12-29 14:15:55 -05:00
Kevin O'Connor
6d8e782f53 Update docs for C++ (fixes artf3761 and artf3953)
Change-Id: Ic0c4ac8494cadff13461d9bb8b5943cd87619f0a
2014-12-29 14:09:37 -05:00
Joe Ross
0368322385 Update Camera templates to make C++ and Java more similar.
Include delays and template code from other examples to show how to use
these examples in a full robot program. Change Java example in example
finder to Simple Vision to match C++. Add comments about how to find cam
number and change default to cam0.

Change-Id: I85846ccfaf016c538a750b057a7fd766cdff9447
2014-12-29 13:48:11 -05:00
Joe Ross
19e05ff52b Update Java install URL to the new URL with the java installer.
Change-Id: Id554d0e2251d9efd7f06466bd2e093602911dc54
2014-12-28 20:23:48 -08:00
Brad Miller (WPI)
a6aef54ef4 Merge "Axis camera C++ example program" 2014-12-28 13:15:54 -08:00
Brad Miller
32f3bea465 Axis camera C++ example program
Change-Id: I60a14ce91fc09831b497ccdaaa7bb1bb26c8d945
2014-12-28 15:47:08 -05:00
Brad Miller (WPI)
9aacf5eb29 Merge "Artifact artf3925 : PCM : Can't find any user facing java/C++ API for getting/clearing PCM faults" 2014-12-28 10:58:12 -08:00
Thomas Clark
f3484686c9 Fix some typos in AxixCamera.cpp
There were some mistakes in comments left over from copying and pasting
comments from pre-2015 code, a couple formatting issues, and one getter
that wasn't implemented

Change-Id: I2e9d19be15445717cce175902c42e7e0932b56ad
2014-12-28 02:31:17 -05:00
Brad Miller (WPI)
b78b14bf5f Merge "Add AxisCamera C++ class" 2014-12-27 19:37:49 -08:00
Brad Miller (WPI)
c08dc98650 Merge "Add the 2014 C++ vision classes back in" 2014-12-27 19:37:29 -08:00
Brad Miller (WPI)
9fbffd88cb Merge "Change the java intermediate vision filename to match c++" 2014-12-27 18:38:11 -08:00
Brad Miller
6c15a3600a Change the java intermediate vision filename to match c++
Change-Id: Iadd173a6ea6089203103f0ca9c220a0b4ff5b286
2014-12-27 21:35:33 -05:00
Omar Zrien
548941dd99 Artifact artf3925 : PCM : Can't find any user facing java/C++ API for getting/clearing PCM faults
Change-Id: If5cb5b08f685158c5317233c4d9bc8e688138df7
2014-12-26 19:40:39 -05:00
Thomas Clark
9d8418c14e Add AxisCamera C++ class
Change-Id: I927334753027b519214d7541497f6c7f6d698163
2014-12-26 16:36:55 -05:00
Brad Miller (WPI)
326bf4fc9c Merge "Updates the InterrupatableSensorBase documentation to reflect the bug described in artf3623." 2014-12-26 13:28:20 -08:00
Fredric Silberberg
3c4a1d9a1a NIVision Forward Port
Ported the NIVision libraries from the old NIVision implementation to
the new autogenerated JNI bindings.

Change-Id: I7c68ca6abef1185d59a9787e9a269d720c825e2f
2014-12-24 16:39:46 -05:00
Fredric Silberberg
7687028269 Updates the InterrupatableSensorBase documentation to reflect the bug
described in artf3623.

Change-Id: I5f93dea65202cf30ba67c063eb8c90bedbfe43ae
2014-12-24 14:19:01 -05:00
Brad Miller
a55f34646d Encoder/Counter Fixes
This fixes all encoder variants with the getRate and getPeriod methods.
The clock speed in the HAL was updated, as was the scaling factors for setting
the stall periods. A default of .5 seconds is now set for the max period.
Additionally, a long standing bug was fixed with Java 2x encoders.

Changed tests to take into account the increased default timeout on encoders

Change-Id: I8b54c07ea467154be94d7ae7e9ada1775933dee4
2014-12-23 20:13:41 -05:00
Thomas Clark
53473e21be Add the 2014 C++ vision classes back in
Most of the old vision code still compiles with minimal changes. I haven't
tested it extensively, since the AxisCamera class isn't done, but it
should work, as it was barely modified.

Java bindings are still TODO, since they used some JNA stuff that probably
won't work now.

Change-Id: I8edf991410cb30b932379f277cee9d329ee35009
2014-12-23 17:42:14 -05:00
Brad Miller (WPI)
b4097fbd58 Merge "ClearStickyFaults() should not take a param, just clears sticky flags in a one shot manner. GetCompressorFault() => GetCompressorCurrentTooHighFault() There are now three compressor related faults so getters needed to be more verbose. New getters()... GetCompressorCurrentTooHighStickyFault GetCompressorShortedStickyFault GetCompressorShortedFault GetCompressorNotConnectedStickyFault GetCompressorNotConnectedFault" 2014-12-22 09:20:31 -08:00
Brad Miller (WPI)
3b72114555 Merge "Fix imports on vision examples so they compile. Fix Typo." 2014-12-22 09:16:42 -08:00
Brad Miller (WPI)
cc36454b90 Merge "Add C interface for CanTalonSRX::SetModeSelect(modeSelect, demand)." 2014-12-22 08:45:10 -08:00
Omar Zrien
aae20ddbff Artifact artf3945 : CANTalon : Motor Safety Object never feed
Change-Id: I6894399d0eb7c36ff698639862fa6ad045a70db8
2014-12-21 16:47:03 -05:00
Joe Ross
ffd9061766 Fix imports on vision examples so they compile. Fix Typo.
Change-Id: I1d6ebf47c824fa93d8a312882cddc778f340009e
2014-12-20 20:47:09 -08:00
Peter Johnson
9ffdea188b Add C interface for CanTalonSRX::SetModeSelect(modeSelect, demand).
Change-Id: Ic8305ec283ddb89afc60d6bc5226411b1f8cc18b
2014-12-20 12:46:05 -08:00
Omar Zrien
198e2eed14 ClearStickyFaults() should not take a param, just clears sticky flags in a one shot manner.
GetCompressorFault() => GetCompressorCurrentTooHighFault()
	There are now three compressor related faults so getters needed to be more verbose.
New getters()...
	GetCompressorCurrentTooHighStickyFault
	GetCompressorShortedStickyFault
	GetCompressorShortedFault
	GetCompressorNotConnectedStickyFault
	GetCompressorNotConnectedFault

Fire and forget api added. Likely not used any time soon, BUT having it ready in HAL is harmless.
	FireOneShotSolenoid()
	SetOneShotDurationMs()

Solenoid fuse and compressor current too high fault bits were flipped in decoder

Change-Id: Ib47dddddd8e4cc22149de1b583968d99919b00af
2014-12-17 15:05:46 -05:00
211 changed files with 12493 additions and 7016 deletions

2
.gitignore vendored
View File

@@ -15,6 +15,8 @@ wpilibj/wpilibJavaJNI/nivision/*.cpp
wpilibj/wpilibJavaJNI/nivision/*.s
wpilibj/wpilibJavaJNI/nivision/*_arm.ini
wpilibj/wpilibJavaJNI/nivision/*.java
wpilibj/wpilibJavaJNI/nivision/nivision_funcs.txt
wpilibj/wpilibJavaJNI/nivision/imaqdx_funcs.txt
# Created by the jenkins test script
test-reports

View File

@@ -34,7 +34,7 @@ public class RunSFXDashboardAction implements IWorkbenchWindowActionDelegate {
* @see IWorkbenchWindowActionDelegate#run
*/
public void run(IAction action) {
File dir = new File(WPILibCore.getDefault().getWPILibBaseDir()+File.separator+"tools");
File dir = new File(WPILibCore.getDefault().getWPILibBaseDir()+File.separator+"tools"+File.separator+"sfx-2014.11.01");
File[] files = dir.listFiles(new FilenameFilter() {
@Override public boolean accept(File dir, String name) {
return name.startsWith("sfx") && name.endsWith(".jar");

View File

@@ -81,7 +81,7 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/src&quot;"/>
<listOptionValue builtIn="false" value="${WPILIB}/cpp/current/sim/include"/>
<listOptionValue builtIn="false" value="/usr/include"/>
<listOptionValue builtIn="false" value="/usr/include/gazebo-3.1"/>
<listOptionValue builtIn="false" value="/usr/include/gazebo-3.2"/>
<listOptionValue builtIn="false" value="/usr/include/sdformat-2.2"/>
</option>
<option id="gnu.cpp.compiler.option.optimization.level.1648211502" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>

View File

@@ -0,0 +1,237 @@
#include "WPILib.h"
#include <vector>
#include <cmath>
/**
* Example of finding yellow totes based on color.
* This example utilizes an image file, which you need to copy to the roboRIO
* To use a camera you will have to integrate the appropriate camera details with this example.
* To use a USB camera instead, see the IntermediateVision example for details
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
* using an Axis Camera.
*
* Sample images can be found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
*/
class VisionColor2015Sample : public SampleRobot
{
//A structure to hold measurements of a particle
struct ParticleReport {
double PercentAreaToImageArea;
double Area;
double ConvexHullArea;
double BoundingRectLeft;
double BoundingRectTop;
double BoundingRectRight;
double BoundingRectBottom;
};
//Structure to represent the scores for the various tests used for target identification
struct Scores {
double Trapezoid;
double LongAspect;
double ShortAspect;
double AreaToConvexHullArea;
};
//Images
Image *frame;
Image *binaryFrame;
int imaqError;
//Constants
Range TOTE_HUE_RANGE = {24, 49}; //Default hue range for yellow tote
Range TOTE_SAT_RANGE = {67, 255}; //Default saturation range for yellow tote
Range TOTE_VAL_RANGE = {49, 255}; //Default value range for yellow tote
double AREA_MINIMUM = 2; //Default Area minimum for particle as a percentage of total image area
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
ParticleFilterCriteria2 criteria[1];
ParticleFilterOptions2 filterOptions = {0,0,1,1};
Scores scores;
public:
void RobotInit() override {
// create images
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
binaryFrame = imaqCreateImage(IMAQ_IMAGE_U8, 0);
//Put default values to SmartDashboard so fields will appear
SmartDashboard::PutNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
SmartDashboard::PutNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
SmartDashboard::PutNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
SmartDashboard::PutNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
SmartDashboard::PutNumber("Tote val min", TOTE_VAL_RANGE.minValue);
SmartDashboard::PutNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
SmartDashboard::PutNumber("Area min %", AREA_MINIMUM);
}
void Autonomous() override {
while (IsAutonomous() && IsEnabled())
{
//read file in from disk. For this example to run you need to copy image20.jpg from the SampleImages folder to the
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
imaqError = imaqReadFile(frame, "//home//lvuser//SampleImages//image20.jpg", NULL, NULL);
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
TOTE_HUE_RANGE.minValue = SmartDashboard::GetNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
TOTE_HUE_RANGE.maxValue = SmartDashboard::GetNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
TOTE_SAT_RANGE.minValue = SmartDashboard::GetNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
TOTE_SAT_RANGE.maxValue = SmartDashboard::GetNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
TOTE_VAL_RANGE.minValue = SmartDashboard::GetNumber("Tote val min", TOTE_VAL_RANGE.minValue);
TOTE_VAL_RANGE.maxValue = SmartDashboard::GetNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
//Threshold the image looking for yellow (tote color)
imaqError = imaqColorThreshold(binaryFrame, frame, 255, IMAQ_HSV, &TOTE_HUE_RANGE, &TOTE_SAT_RANGE, &TOTE_VAL_RANGE);
//Send particle count to dashboard
int numParticles = 0;
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
SmartDashboard::PutNumber("Masked particles", numParticles);
//Send masked image to dashboard to assist in tweaking mask.
SendToDashboard(binaryFrame, imaqError);
//filter out small particles
float areaMin = SmartDashboard::GetNumber("Area min %", AREA_MINIMUM);
criteria[0] = {IMAQ_MT_AREA_BY_IMAGE_AREA, areaMin, 100, false, false};
imaqError = imaqParticleFilter4(binaryFrame, binaryFrame, criteria, 1, &filterOptions, NULL, NULL);
//Send particle count after filtering to dashboard
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
SmartDashboard::PutNumber("Filtered particles", numParticles);
if(numParticles > 0)
{
//Measure particles and sort by particle size
std::vector<ParticleReport> particles;
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
{
ParticleReport par;
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &(par.PercentAreaToImageArea));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA, &(par.Area));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_CONVEX_HULL_AREA, &(par.ConvexHullArea));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_TOP, &(par.BoundingRectTop));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &(par.BoundingRectLeft));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_BOTTOM, &(par.BoundingRectBottom));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_RIGHT, &(par.BoundingRectRight));
particles.push_back(par);
}
sort(particles.begin(), particles.end(), CompareParticleSizes);
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
//for the reader. Note that the long and short side scores expect a single tote and will not work for a stack of 2 or more totes.
//Modification of the code to accommodate 2 or more stacked totes is left as an exercise for the reader.
scores.Trapezoid = TrapezoidScore(particles.at(0));
SmartDashboard::PutNumber("Trapezoid", scores.Trapezoid);
scores.LongAspect = LongSideScore(particles.at(0));
SmartDashboard::PutNumber("Long Aspect", scores.LongAspect);
scores.ShortAspect = ShortSideScore(particles.at(0));
SmartDashboard::PutNumber("Short Aspect", scores.ShortAspect);
scores.AreaToConvexHullArea = ConvexHullAreaScore(particles.at(0));
SmartDashboard::PutNumber("Convex Hull Area", scores.AreaToConvexHullArea);
bool isTote = scores.Trapezoid > SCORE_MIN && (scores.LongAspect > SCORE_MIN || scores.ShortAspect > SCORE_MIN) && scores.AreaToConvexHullArea > SCORE_MIN;
bool isLong = scores.LongAspect > scores.ShortAspect;
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
SmartDashboard::PutBoolean("IsTote", isTote);
SmartDashboard::PutNumber("Distance", computeDistance(binaryFrame, particles.at(0), isLong));
} else {
SmartDashboard::PutBoolean("IsTote", false);
}
Wait(0.005); // wait for a motor update time
}
}
void OperatorControl() override {
while(IsOperatorControl() && IsEnabled()) {
Wait(0.005); // wait for a motor update time
}
}
//Send image to dashboard if IMAQ has not thrown an error
void SendToDashboard(Image *image, int error)
{
if(error < ERR_SUCCESS) {
DriverStation::ReportError("Send To Dashboard error: " + std::to_string((long)imaqError) + "\n");
} else {
CameraServer::GetInstance()->SetImage(image);
}
}
//Comparator function for sorting particles. Returns true if particle 1 is larger
static bool CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
{
//we want descending sort order
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
}
/**
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
*/
double ratioToScore(double ratio)
{
return (fmax(0, fmin(100*(1-fabs(1-ratio)), 100)));
}
/**
* Method to score convex hull area. This scores how "complete" the particle is. Particles with large holes will score worse than a filled in shape
*/
double ConvexHullAreaScore(ParticleReport report)
{
return ratioToScore((report.Area/report.ConvexHullArea)*1.18);
}
/**
* Method to score if the particle appears to be a trapezoid. Compares the convex hull (filled in) area to the area of the bounding box.
* The expectation is that the convex hull area is about 95.4% of the bounding box area for an ideal tote.
*/
double TrapezoidScore(ParticleReport report)
{
return ratioToScore(report.ConvexHullArea/((report.BoundingRectRight-report.BoundingRectLeft)*(report.BoundingRectBottom-report.BoundingRectTop)*.954));
}
/**
* Method to score if the aspect ratio of the particle appears to match the long side of a tote.
*/
double LongSideScore(ParticleReport report)
{
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/LONG_RATIO);
}
/**
* Method to score if the aspect ratio of the particle appears to match the short side of a tote.
*/
double ShortSideScore(ParticleReport report){
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/SHORT_RATIO);
}
/**
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
*
* @param image The image to use for measuring the particle estimated rectangle
* @param report The Particle Analysis Report for the particle
* @param isLong Boolean indicating if the target is believed to be the long side of a tote
* @return The estimated distance to the target in feet.
*/
double computeDistance (Image *image, ParticleReport report, bool isLong) {
double normalizedWidth, targetWidth;
int xRes, yRes;
imaqGetImageSize(image, &xRes, &yRes);
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/xRes;
SmartDashboard::PutNumber("Width", normalizedWidth);
targetWidth = isLong ? 26.9 : 16.9;
return targetWidth/(normalizedWidth*12*tan(VIEW_ANGLE*M_PI/(180*2)));
}
};
START_ROBOT_CLASS(VisionColor2015Sample);

View File

@@ -0,0 +1,210 @@
#include "WPILib.h"
#include <vector>
#include <cmath>
/**
* Example of finding yellow totes based on retroreflective target.
* This example utilizes an image file, which you need to copy to the roboRIO
* To use a camera you will have to integrate the appropriate camera details with this example.
* To use a USB camera instead, see the IntermediateVision example for details
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
* using an Axis Camera.
*
* Sample images can be found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
*/
class VisionRetro2015Sample : public SampleRobot
{
//A structure to hold measurements of a particle
struct ParticleReport {
double PercentAreaToImageArea;
double Area;
double BoundingRectLeft;
double BoundingRectTop;
double BoundingRectRight;
double BoundingRectBottom;
};
//Structure to represent the scores for the various tests used for target identification
struct Scores {
double Area;
double Aspect;
};
//Images
Image *frame;
Image *binaryFrame;
int imaqError;
//Constants
Range RING_HUE_RANGE = {101, 64}; //Default hue range for ring light
Range RING_SAT_RANGE = {88, 255}; //Default saturation range for ring light
Range RING_VAL_RANGE = {134, 255}; //Default value range for ring light
double AREA_MINIMUM = 0.5; //Default Area minimum for particle as a percentage of total image area
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
ParticleFilterCriteria2 criteria[1];
ParticleFilterOptions2 filterOptions = {0,0,1,1};
Scores scores;
public:
void RobotInit() override {
// create images
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
binaryFrame = imaqCreateImage(IMAQ_IMAGE_U8, 0);
//Put default values to SmartDashboard so fields will appear
SmartDashboard::PutNumber("Tote hue min", RING_HUE_RANGE.minValue);
SmartDashboard::PutNumber("Tote hue max", RING_HUE_RANGE.maxValue);
SmartDashboard::PutNumber("Tote sat min", RING_SAT_RANGE.minValue);
SmartDashboard::PutNumber("Tote sat max", RING_SAT_RANGE.maxValue);
SmartDashboard::PutNumber("Tote val min", RING_VAL_RANGE.minValue);
SmartDashboard::PutNumber("Tote val max", RING_VAL_RANGE.maxValue);
SmartDashboard::PutNumber("Area min %", AREA_MINIMUM);
}
void Autonomous() override {
while (IsAutonomous() && IsEnabled())
{
//read file in from disk. For this example to run you need to copy image.jpg from the SampleImages folder to the
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
imaqError = imaqReadFile(frame, "//home//lvuser//SampleImages//image.jpg", NULL, NULL);
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
RING_HUE_RANGE.minValue = SmartDashboard::GetNumber("Tote hue min", RING_HUE_RANGE.minValue);
RING_HUE_RANGE.maxValue = SmartDashboard::GetNumber("Tote hue max", RING_HUE_RANGE.maxValue);
RING_SAT_RANGE.minValue = SmartDashboard::GetNumber("Tote sat min", RING_SAT_RANGE.minValue);
RING_SAT_RANGE.maxValue = SmartDashboard::GetNumber("Tote sat max", RING_SAT_RANGE.maxValue);
RING_VAL_RANGE.minValue = SmartDashboard::GetNumber("Tote val min", RING_VAL_RANGE.minValue);
RING_VAL_RANGE.maxValue = SmartDashboard::GetNumber("Tote val max", RING_VAL_RANGE.maxValue);
//Threshold the image looking for ring light color
imaqError = imaqColorThreshold(binaryFrame, frame, 255, IMAQ_HSV, &RING_HUE_RANGE, &RING_SAT_RANGE, &RING_VAL_RANGE);
//Send particle count to dashboard
int numParticles = 0;
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
SmartDashboard::PutNumber("Masked particles", numParticles);
//Send masked image to dashboard to assist in tweaking mask.
SendToDashboard(binaryFrame, imaqError);
//filter out small particles
float areaMin = SmartDashboard::GetNumber("Area min %", AREA_MINIMUM);
criteria[0] = {IMAQ_MT_AREA_BY_IMAGE_AREA, areaMin, 100, false, false};
imaqError = imaqParticleFilter4(binaryFrame, binaryFrame, criteria, 1, &filterOptions, NULL, NULL);
//Send particle count after filtering to dashboard
imaqError = imaqCountParticles(binaryFrame, 1, &numParticles);
SmartDashboard::PutNumber("Filtered particles", numParticles);
if(numParticles > 0) {
//Measure particles and sort by particle size
std::vector<ParticleReport> particles;
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
{
ParticleReport par;
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA_BY_IMAGE_AREA, &(par.PercentAreaToImageArea));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_AREA, &(par.Area));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_TOP, &(par.BoundingRectTop));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_LEFT, &(par.BoundingRectLeft));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_BOTTOM, &(par.BoundingRectBottom));
imaqMeasureParticle(binaryFrame, particleIndex, 0, IMAQ_MT_BOUNDING_RECT_RIGHT, &(par.BoundingRectRight));
particles.push_back(par);
}
sort(particles.begin(), particles.end(), CompareParticleSizes);
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
//for the reader. Note that this scores and reports information about a single particle (single L shaped target). To get accurate information
//about the location of the tote (not just the distance) you will need to correlate two adjacent targets in order to find the true center of the tote.
scores.Aspect = AspectScore(particles.at(0));
SmartDashboard::PutNumber("Aspect", scores.Aspect);
scores.Area = AreaScore(particles.at(0));
SmartDashboard::PutNumber("Area", scores.Area);
bool isTarget = scores.Area > SCORE_MIN && scores.Aspect > SCORE_MIN;
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
SmartDashboard::PutBoolean("IsTarget", isTarget);
SmartDashboard::PutNumber("Distance", computeDistance(binaryFrame, particles.at(0)));
} else {
SmartDashboard::PutBoolean("IsTarget", false);
}
Wait(0.005); // wait for a motor update time
}
}
void OperatorControl() override {
while(IsOperatorControl() && IsEnabled()) {
Wait(0.005); // wait for a motor update time
}
}
//Send image to dashboard if IMAQ has not thrown an error
void SendToDashboard(Image *image, int error)
{
if(error < ERR_SUCCESS) {
DriverStation::ReportError("Send To Dashboard error: " + std::to_string((long)imaqError) + "\n");
} else {
CameraServer::GetInstance()->SetImage(binaryFrame);
}
}
//Comparator function for sorting particles. Returns true if particle 1 is larger
static bool CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
{
//we want descending sort order
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
}
/**
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
*/
double ratioToScore(double ratio)
{
return (fmax(0, fmin(100*(1-fabs(1-ratio)), 100)));
}
double AreaScore(ParticleReport report)
{
double boundingArea = (report.BoundingRectBottom - report.BoundingRectTop) * (report.BoundingRectRight - report.BoundingRectLeft);
//Tape is 7" edge so 49" bounding rect. With 2" wide tape it covers 24" of the rect.
return ratioToScore((49/24)*report.Area/boundingArea);
}
/**
* Method to score if the aspect ratio of the particle appears to match the retro-reflective target. Target is 7"x7" so aspect should be 1
*/
double AspectScore(ParticleReport report)
{
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop)));
}
/**
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
*
* @param image The image to use for measuring the particle estimated rectangle
* @param report The Particle Analysis Report for the particle
* @return The estimated distance to the target in feet.
*/
double computeDistance (Image *image, ParticleReport report) {
double normalizedWidth, targetWidth;
int xRes, yRes;
imaqGetImageSize(image, &xRes, &yRes);
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/xRes;
SmartDashboard::PutNumber("Width", normalizedWidth);
targetWidth = 7;
return targetWidth/(normalizedWidth*12*tan(VIEW_ANGLE*M_PI/(180*2)));
}
};
START_ROBOT_CLASS(VisionRetro2015Sample);

View File

@@ -0,0 +1,37 @@
#include "WPILib.h"
/**
* Uses AxisCamera class to manually acquire a new image each frame, and annotate the image by drawing
* a circle on it, and show it on the FRC Dashboard.
*/
class AxisCameraSample : public SampleRobot
{
IMAQdxSession session;
Image *frame;
IMAQdxError imaqError;
AxisCamera *camera;
public:
void RobotInit() override {
// create an image
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
// open the camera at the IP address assigned. This is the IP address that the camera
// can be accessed through the web interface.
camera = new AxisCamera("10.1.91.103");
}
void OperatorControl() override {
// grab an image, draw the circle, and provide it for the camera server which will
// in turn send it to the dashboard.
while(IsOperatorControl() && IsEnabled()) {
camera->GetImage(frame);
imaqDrawShapeOnImage(frame, frame, { 10, 10, 100, 100 }, DrawMode::IMAQ_DRAW_VALUE, ShapeMode::IMAQ_SHAPE_OVAL, 0.0f);
CameraServer::GetInstance()->SetImage(frame);
Wait(0.05);
}
}
};
START_ROBOT_CLASS(AxisCameraSample);

View File

@@ -14,7 +14,7 @@ public:
void RobotInit() override {
// create an image
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
// open the camera
//the camera name (ex "cam0") can be found through the roborio web interface
imaqError = IMAQdxOpenCamera("cam0", IMAQdxCameraControlModeController, &session);
if(imaqError != IMAQdxErrorSuccess) {
DriverStation::ReportError("IMAQdxOpenCamera error: " + std::to_string((long)imaqError) + "\n");
@@ -39,6 +39,7 @@ public:
imaqDrawShapeOnImage(frame, frame, { 10, 10, 100, 100 }, DrawMode::IMAQ_DRAW_VALUE, ShapeMode::IMAQ_SHAPE_OVAL, 0.0f);
CameraServer::GetInstance()->SetImage(frame);
}
Wait(0.005); // wait for a motor update time
}
// stop image acquisition
IMAQdxStopAcquisition(session);

View File

@@ -10,11 +10,18 @@ class QuickVisionRobot : public SampleRobot
{
public:
void RobotInit() override {
CameraServer::GetInstance()->SetQuality(75);
CameraServer::GetInstance()->StartAutomaticCapture();
CameraServer::GetInstance()->SetQuality(50);
//the camera name (ex "cam0") can be found through the roborio web interface
CameraServer::GetInstance()->StartAutomaticCapture("cam0");
}
void OperatorControl() override {
void OperatorControl()
{
while (IsOperatorControl() && IsEnabled())
{
/** robot code here! **/
Wait(0.005); // wait for a motor update time
}
}
};

View File

@@ -281,7 +281,7 @@
</example>
<example>
<name>Simple vision program</name>
<name>Simple Vision</name>
<description>The minimal program to acquire images from an attached USB camera on the robot
and send them to the dashboard.</description>
<tags>
@@ -298,7 +298,7 @@
</example>
<example>
<name>Intermediate vision</name>
<name>Intermediate Vision</name>
<description>An example program that acquires images from an attached USB camera and adds some
annotation to the image as you might do for showing operators the result of some image
recognition, and sends it to the dashboard for display.
@@ -316,6 +316,65 @@
</files>
</example>
<example>
<name>Axis Camera Sample</name>
<description>An example program that acquires images from an Axis network camera and adds some
annotation to the image as you might do for showing operators the result of some image
recognition, and sends it to the dashboard for display. This demonstrates the use of the
AxisCamera class.
</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src</package>
</packages>
<files>
<file source="examples/AxisCameraSample/src/Robot.cpp"
destination="src/Robot.cpp"></file>
</files>
</example>
<example>
<name>2015 Vision Color Sample</name>
<description>An example program that demonstrates image processing to locate Yellow totes by color.
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
camera example;
</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src</package>
</packages>
<files>
<file source="examples/2015Vision/Color_src/Robot.cpp"
destination="src/Robot.cpp"></file>
</files>
</example>
<example>
<name>2015 Vision Retro Sample</name>
<description>An example program that demonstrates image processing to locate Yellow totes by the retroreflective target.
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
camera example;
</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src</package>
</packages>
<files>
<file source="examples/2015Vision/Retro_src/Robot.cpp"
destination="src/Robot.cpp"></file>
</files>
</example>
<example>
<name>GearsBot</name>

View File

@@ -64,6 +64,15 @@
<echo>[athena-deploy] Copying code over.</echo>
<scp file="${out.exe}" sftp="true" todir="${username}@${target}:${deploy.dir}" password="${password}" trust="true"/>
<sshexec host="${target}"
username="admin"
password="${password}"
trust="true"
failonerror="false"
command="killall netconsole-host"/>
<scp file="${wpilib.ant.dir}/netconsole-host" todir="admin@${target}:/usr/local/frc/bin" password="${password}" trust="true"/>
<scp file="${wpilib.ant.dir}/robotCommand" todir="${username}@${target}:/home/lvuser/" password="${password}" trust="true"/>
<echo>[athena-deploy] Starting program.</echo>

View File

@@ -0,0 +1,240 @@
package $package;
import java.lang.Math;
import java.util.Comparator;
import java.util.Vector;
import com.ni.vision.NIVision;
import com.ni.vision.NIVision.Image;
import com.ni.vision.NIVision.ImageType;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.SampleRobot;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
/**
* Example of finding yellow totes based on color.
* This example utilizes an image file, which you need to copy to the roboRIO
* To use a camera you will have to integrate the appropriate camera details with this example.
* To use a USB camera instead, see the SimpelVision and AdvancedVision examples for details
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
* using an Axis Camera.
*
* Sample omages can be found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
*/
public class Robot extends SampleRobot {
//A structure to hold measurements of a particle
public class ParticleReport implements Comparator<ParticleReport>, Comparable<ParticleReport>{
double PercentAreaToImageArea;
double Area;
double ConvexHullArea;
double BoundingRectLeft;
double BoundingRectTop;
double BoundingRectRight;
double BoundingRectBottom;
public int compareTo(ParticleReport r)
{
return (int)(r.Area - this.Area);
}
public int compare(ParticleReport r1, ParticleReport r2)
{
return (int)(r1.Area - r2.Area);
}
};
//Structure to represent the scores for the various tests used for target identification
public class Scores {
double Trapezoid;
double LongAspect;
double ShortAspect;
double AreaToConvexHullArea;
};
//Images
Image frame;
Image binaryFrame;
int imaqError;
//Constants
NIVision.Range TOTE_HUE_RANGE = new NIVision.Range(24, 49); //Default hue range for yellow tote
NIVision.Range TOTE_SAT_RANGE = new NIVision.Range(67, 255); //Default saturation range for yellow tote
NIVision.Range TOTE_VAL_RANGE = new NIVision.Range(49, 255); //Default value range for yellow tote
double AREA_MINIMUM = 0.5; //Default Area minimum for particle as a percentage of total image area
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
NIVision.ParticleFilterCriteria2 criteria[] = new NIVision.ParticleFilterCriteria2[1];
NIVision.ParticleFilterOptions2 filterOptions = new NIVision.ParticleFilterOptions2(0,0,1,1);
Scores scores = new Scores();
public void robotInit() {
// create images
frame = NIVision.imaqCreateImage(ImageType.IMAGE_RGB, 0);
binaryFrame = NIVision.imaqCreateImage(ImageType.IMAGE_U8, 0);
criteria[0] = new NIVision.ParticleFilterCriteria2(NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA, AREA_MINIMUM, 100.0, 0, 0);
//Put default values to SmartDashboard so fields will appear
SmartDashboard.putNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
SmartDashboard.putNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
SmartDashboard.putNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
SmartDashboard.putNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
SmartDashboard.putNumber("Tote val min", TOTE_VAL_RANGE.minValue);
SmartDashboard.putNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
SmartDashboard.putNumber("Area min %", AREA_MINIMUM);
}
public void autonomous() {
while (isAutonomous() && isEnabled())
{
//read file in from disk. For this example to run you need to copy image20.jpg from the SampleImages folder to the
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
NIVision.imaqReadFile(frame, "/home/lvuser/SampleImages/image20.jpg");
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
TOTE_HUE_RANGE.minValue = (int)SmartDashboard.getNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
TOTE_HUE_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
TOTE_SAT_RANGE.minValue = (int)SmartDashboard.getNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
TOTE_SAT_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
TOTE_VAL_RANGE.minValue = (int)SmartDashboard.getNumber("Tote val min", TOTE_VAL_RANGE.minValue);
TOTE_VAL_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
//Threshold the image looking for yellow (tote color)
NIVision.imaqColorThreshold(binaryFrame, frame, 255, NIVision.ColorMode.HSV, TOTE_HUE_RANGE, TOTE_SAT_RANGE, TOTE_VAL_RANGE);
//Send particle count to dashboard
int numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
SmartDashboard.putNumber("Masked particles", numParticles);
//Send masked image to dashboard to assist in tweaking mask.
CameraServer.getInstance().setImage(binaryFrame);
//filter out small particles
float areaMin = (float)SmartDashboard.getNumber("Area min %", AREA_MINIMUM);
criteria[0].lower = areaMin;
imaqError = NIVision.imaqParticleFilter4(binaryFrame, binaryFrame, criteria, filterOptions, null);
//Send particle count after filtering to dashboard
numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
SmartDashboard.putNumber("Filtered particles", numParticles);
if(numParticles > 0)
{
//Measure particles and sort by particle size
Vector<ParticleReport> particles = new Vector<ParticleReport>();
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
{
ParticleReport par = new ParticleReport();
par.PercentAreaToImageArea = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA);
par.Area = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA);
par.ConvexHullArea = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_CONVEX_HULL_AREA);
par.BoundingRectTop = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_TOP);
par.BoundingRectLeft = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_LEFT);
par.BoundingRectBottom = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_BOTTOM);
par.BoundingRectRight = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_RIGHT);
particles.add(par);
}
particles.sort(null);
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
//for the reader. Note that the long and short side scores expect a single tote and will not work for a stack of 2 or more totes.
//Modification of the code to accommodate 2 or more stacked totes is left as an exercise for the reader.
scores.Trapezoid = TrapezoidScore(particles.elementAt(0));
SmartDashboard.putNumber("Trapezoid", scores.Trapezoid);
scores.LongAspect = LongSideScore(particles.elementAt(0));
SmartDashboard.putNumber("Long Aspect", scores.LongAspect);
scores.ShortAspect = ShortSideScore(particles.elementAt(0));
SmartDashboard.putNumber("Short Aspect", scores.ShortAspect);
scores.AreaToConvexHullArea = ConvexHullAreaScore(particles.elementAt(0));
SmartDashboard.putNumber("Convex Hull Area", scores.AreaToConvexHullArea);
boolean isTote = scores.Trapezoid > SCORE_MIN && (scores.LongAspect > SCORE_MIN || scores.ShortAspect > SCORE_MIN) && scores.AreaToConvexHullArea > SCORE_MIN;
boolean isLong = scores.LongAspect > scores.ShortAspect;
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
SmartDashboard.putBoolean("IsTote", isTote);
SmartDashboard.putNumber("Distance", computeDistance(binaryFrame, particles.elementAt(0), isLong));
} else {
SmartDashboard.putBoolean("IsTote", false);
}
Timer.delay(0.005); // wait for a motor update time
}
}
public void operatorControl() {
while(isOperatorControl() && isEnabled()) {
Timer.delay(0.005); // wait for a motor update time
}
}
//Comparator function for sorting particles. Returns true if particle 1 is larger
static boolean CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
{
//we want descending sort order
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
}
/**
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
*/
double ratioToScore(double ratio)
{
return (Math.max(0, Math.min(100*(1-Math.abs(1-ratio)), 100)));
}
/**
* Method to score convex hull area. This scores how "complete" the particle is. Particles with large holes will score worse than a filled in shape
*/
double ConvexHullAreaScore(ParticleReport report)
{
return ratioToScore((report.Area/report.ConvexHullArea)*1.18);
}
/**
* Method to score if the particle appears to be a trapezoid. Compares the convex hull (filled in) area to the area of the bounding box.
* The expectation is that the convex hull area is about 95.4% of the bounding box area for an ideal tote.
*/
double TrapezoidScore(ParticleReport report)
{
return ratioToScore(report.ConvexHullArea/((report.BoundingRectRight-report.BoundingRectLeft)*(report.BoundingRectBottom-report.BoundingRectTop)*.954));
}
/**
* Method to score if the aspect ratio of the particle appears to match the long side of a tote.
*/
double LongSideScore(ParticleReport report)
{
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/LONG_RATIO);
}
/**
* Method to score if the aspect ratio of the particle appears to match the short side of a tote.
*/
double ShortSideScore(ParticleReport report){
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop))/SHORT_RATIO);
}
/**
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
*
* @param image The image to use for measuring the particle estimated rectangle
* @param report The Particle Analysis Report for the particle
* @param isLong Boolean indicating if the target is believed to be the long side of a tote
* @return The estimated distance to the target in feet.
*/
double computeDistance (Image image, ParticleReport report, boolean isLong) {
double normalizedWidth, targetWidth;
NIVision.GetImageSizeResult size;
size = NIVision.imaqGetImageSize(image);
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/size.width;
targetWidth = isLong ? 26.0 : 16.9;
return targetWidth/(normalizedWidth*12*Math.tan(VIEW_ANGLE*Math.PI/(180*2)));
}
}

View File

@@ -0,0 +1,214 @@
package $package;
import java.lang.Math;
import java.util.Comparator;
import java.util.Vector;
import com.ni.vision.NIVision;
import com.ni.vision.NIVision.Image;
import com.ni.vision.NIVision.ImageType;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.SampleRobot;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
/**
* Example of finding yellow totes based on retroreflective target.
* This example utilizes an image file, which you need to copy to the roboRIO
* To use a camera you will have to integrate the appropriate camera details with this example.
* To use a USB camera instead, see the SimpelVision and AdvancedVision examples for details
* on using the USB camera. To use an Axis Camera, see the AxisCamera example for details on
* using an Axis Camera.
*
* Sample images can found here: http://wp.wpi.edu/wpilib/2015/01/16/sample-images-for-vision-projects/
*/
public class Robot extends SampleRobot {
//A structure to hold measurements of a particle
public class ParticleReport implements Comparator<ParticleReport>, Comparable<ParticleReport>{
double PercentAreaToImageArea;
double Area;
double BoundingRectLeft;
double BoundingRectTop;
double BoundingRectRight;
double BoundingRectBottom;
public int compareTo(ParticleReport r)
{
return (int)(r.Area - this.Area);
}
public int compare(ParticleReport r1, ParticleReport r2)
{
return (int)(r1.Area - r2.Area);
}
};
//Structure to represent the scores for the various tests used for target identification
public class Scores {
double Area;
double Aspect;
};
//Images
Image frame;
Image binaryFrame;
int imaqError;
//Constants
NIVision.Range TOTE_HUE_RANGE = new NIVision.Range(101, 64); //Default hue range for yellow tote
NIVision.Range TOTE_SAT_RANGE = new NIVision.Range(88, 255); //Default saturation range for yellow tote
NIVision.Range TOTE_VAL_RANGE = new NIVision.Range(134, 255); //Default value range for yellow tote
double AREA_MINIMUM = 0.5; //Default Area minimum for particle as a percentage of total image area
double LONG_RATIO = 2.22; //Tote long side = 26.9 / Tote height = 12.1 = 2.22
double SHORT_RATIO = 1.4; //Tote short side = 16.9 / Tote height = 12.1 = 1.4
double SCORE_MIN = 75.0; //Minimum score to be considered a tote
double VIEW_ANGLE = 49.4; //View angle fo camera, set to Axis m1011 by default, 64 for m1013, 51.7 for 206, 52 for HD3000 square, 60 for HD3000 640x480
NIVision.ParticleFilterCriteria2 criteria[] = new NIVision.ParticleFilterCriteria2[1];
NIVision.ParticleFilterOptions2 filterOptions = new NIVision.ParticleFilterOptions2(0,0,1,1);
Scores scores = new Scores();
public void robotInit() {
// create images
frame = NIVision.imaqCreateImage(ImageType.IMAGE_RGB, 0);
binaryFrame = NIVision.imaqCreateImage(ImageType.IMAGE_U8, 0);
criteria[0] = new NIVision.ParticleFilterCriteria2(NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA, AREA_MINIMUM, 100.0, 0, 0);
//Put default values to SmartDashboard so fields will appear
SmartDashboard.putNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
SmartDashboard.putNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
SmartDashboard.putNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
SmartDashboard.putNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
SmartDashboard.putNumber("Tote val min", TOTE_VAL_RANGE.minValue);
SmartDashboard.putNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
SmartDashboard.putNumber("Area min %", AREA_MINIMUM);
}
public void autonomous() {
while (isAutonomous() && isEnabled())
{
//read file in from disk. For this example to run you need to copy image.jpg from the SampleImages folder to the
//directory shown below using FTP or SFTP: http://wpilib.screenstepslive.com/s/4485/m/24166/l/282299-roborio-ftp
NIVision.imaqReadFile(frame, "/home/lvuser/SampleImages/image.jpg");
//Update threshold values from SmartDashboard. For performance reasons it is recommended to remove this after calibration is finished.
TOTE_HUE_RANGE.minValue = (int)SmartDashboard.getNumber("Tote hue min", TOTE_HUE_RANGE.minValue);
TOTE_HUE_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote hue max", TOTE_HUE_RANGE.maxValue);
TOTE_SAT_RANGE.minValue = (int)SmartDashboard.getNumber("Tote sat min", TOTE_SAT_RANGE.minValue);
TOTE_SAT_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote sat max", TOTE_SAT_RANGE.maxValue);
TOTE_VAL_RANGE.minValue = (int)SmartDashboard.getNumber("Tote val min", TOTE_VAL_RANGE.minValue);
TOTE_VAL_RANGE.maxValue = (int)SmartDashboard.getNumber("Tote val max", TOTE_VAL_RANGE.maxValue);
//Threshold the image looking for yellow (tote color)
NIVision.imaqColorThreshold(binaryFrame, frame, 255, NIVision.ColorMode.HSV, TOTE_HUE_RANGE, TOTE_SAT_RANGE, TOTE_VAL_RANGE);
//Send particle count to dashboard
int numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
SmartDashboard.putNumber("Masked particles", numParticles);
//Send masked image to dashboard to assist in tweaking mask.
CameraServer.getInstance().setImage(binaryFrame);
//filter out small particles
float areaMin = (float)SmartDashboard.getNumber("Area min %", AREA_MINIMUM);
criteria[0].lower = areaMin;
imaqError = NIVision.imaqParticleFilter4(binaryFrame, binaryFrame, criteria, filterOptions, null);
//Send particle count after filtering to dashboard
numParticles = NIVision.imaqCountParticles(binaryFrame, 1);
SmartDashboard.putNumber("Filtered particles", numParticles);
if(numParticles > 0)
{
//Measure particles and sort by particle size
Vector<ParticleReport> particles = new Vector<ParticleReport>();
for(int particleIndex = 0; particleIndex < numParticles; particleIndex++)
{
ParticleReport par = new ParticleReport();
par.PercentAreaToImageArea = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA_BY_IMAGE_AREA);
par.Area = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_AREA);
par.BoundingRectTop = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_TOP);
par.BoundingRectLeft = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_LEFT);
par.BoundingRectBottom = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_BOTTOM);
par.BoundingRectRight = NIVision.imaqMeasureParticle(binaryFrame, particleIndex, 0, NIVision.MeasurementType.MT_BOUNDING_RECT_RIGHT);
particles.add(par);
}
particles.sort(null);
//This example only scores the largest particle. Extending to score all particles and choosing the desired one is left as an exercise
//for the reader. Note that this scores and reports information about a single particle (single L shaped target). To get accurate information
//about the location of the tote (not just the distance) you will need to correlate two adjacent targets in order to find the true center of the tote.
scores.Aspect = AspectScore(particles.elementAt(0));
SmartDashboard.putNumber("Aspect", scores.Aspect);
scores.Area = AreaScore(particles.elementAt(0));
SmartDashboard.putNumber("Area", scores.Area);
boolean isTote = scores.Aspect > SCORE_MIN && scores.Area > SCORE_MIN;
//Send distance and tote status to dashboard. The bounding rect, particularly the horizontal center (left - right) may be useful for rotating/driving towards a tote
SmartDashboard.putBoolean("IsTote", isTote);
SmartDashboard.putNumber("Distance", computeDistance(binaryFrame, particles.elementAt(0)));
} else {
SmartDashboard.putBoolean("IsTote", false);
}
Timer.delay(0.005); // wait for a motor update time
}
}
public void operatorControl() {
while(isOperatorControl() && isEnabled()) {
Timer.delay(0.005); // wait for a motor update time
}
}
//Comparator function for sorting particles. Returns true if particle 1 is larger
static boolean CompareParticleSizes(ParticleReport particle1, ParticleReport particle2)
{
//we want descending sort order
return particle1.PercentAreaToImageArea > particle2.PercentAreaToImageArea;
}
/**
* Converts a ratio with ideal value of 1 to a score. The resulting function is piecewise
* linear going from (0,0) to (1,100) to (2,0) and is 0 for all inputs outside the range 0-2
*/
double ratioToScore(double ratio)
{
return (Math.max(0, Math.min(100*(1-Math.abs(1-ratio)), 100)));
}
double AreaScore(ParticleReport report)
{
double boundingArea = (report.BoundingRectBottom - report.BoundingRectTop) * (report.BoundingRectRight - report.BoundingRectLeft);
//Tape is 7" edge so 49" bounding rect. With 2" wide tape it covers 24" of the rect.
return ratioToScore((49/24)*report.Area/boundingArea);
}
/**
* Method to score if the aspect ratio of the particle appears to match the retro-reflective target. Target is 7"x7" so aspect should be 1
*/
double AspectScore(ParticleReport report)
{
return ratioToScore(((report.BoundingRectRight-report.BoundingRectLeft)/(report.BoundingRectBottom-report.BoundingRectTop)));
}
/**
* Computes the estimated distance to a target using the width of the particle in the image. For more information and graphics
* showing the math behind this approach see the Vision Processing section of the ScreenStepsLive documentation.
*
* @param image The image to use for measuring the particle estimated rectangle
* @param report The Particle Analysis Report for the particle
* @param isLong Boolean indicating if the target is believed to be the long side of a tote
* @return The estimated distance to the target in feet.
*/
double computeDistance (Image image, ParticleReport report) {
double normalizedWidth, targetWidth;
NIVision.GetImageSizeResult size;
size = NIVision.imaqGetImageSize(image);
normalizedWidth = 2*(report.BoundingRectRight - report.BoundingRectLeft)/size.width;
targetWidth = 7;
return targetWidth/(normalizedWidth*12*Math.tan(VIEW_ANGLE*Math.PI/(180*2)));
}
}

View File

@@ -0,0 +1,55 @@
package $package;
import com.ni.vision.NIVision;
import com.ni.vision.NIVision.DrawMode;
import com.ni.vision.NIVision.Image;
import com.ni.vision.NIVision.ShapeMode;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.SampleRobot;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.vision.AxisCamera;
/**
* This demo shows the use of the AxisCamera class.
* Uses AxisCamera class to manually acquire a new image each frame, and annotate the image by drawing
* a circle on it, and show it on the FRC Dashboard.
*/
public class Robot extends SampleRobot {
int session;
Image frame;
AxisCamera camera;
public void robotInit() {
frame = NIVision.imaqCreateImage(NIVision.ImageType.IMAGE_RGB, 0);
// open the camera at the IP address assigned. This is the IP address that the camera
// can be accessed through the web interface.
camera = new AxisCamera("10.1.91.100");
}
public void operatorControl() {
/**
* grab an image from the camera, draw the circle, and provide it for the camera server
* which will in turn send it to the dashboard.
*/
NIVision.Rect rect = new NIVision.Rect(10, 10, 100, 100);
while (isOperatorControl() && isEnabled()) {
camera.getImage(frame);
NIVision.imaqDrawShapeOnImage(frame, frame, rect,
DrawMode.DRAW_VALUE, ShapeMode.SHAPE_OVAL, 0.0f);
CameraServer.getInstance().setImage(frame);
/** robot code here! **/
Timer.delay(0.005); // wait for a motor update time
}
}
public void test() {
}
}

View File

@@ -4,7 +4,10 @@ import com.ni.vision.NIVision;
import com.ni.vision.NIVision.DrawMode;
import com.ni.vision.NIVision.Image;
import com.ni.vision.NIVision.ShapeMode;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.SampleRobot;
import edu.wpi.first.wpilibj.Timer;
/**
* This is a demo program showing the use of the NIVision class to do vision processing.
@@ -20,10 +23,8 @@ public class Robot extends SampleRobot {
frame = NIVision.imaqCreateImage(NIVision.ImageType.IMAGE_RGB, 0);
/**
* the camera name (ex "cam1") can be found through the roborio web interface
*/
session = NIVision.IMAQdxOpenCamera("cam1",
// the camera name (ex "cam0") can be found through the roborio web interface
session = NIVision.IMAQdxOpenCamera("cam0",
NIVision.IMAQdxCameraControlMode.CameraControlModeController);
NIVision.IMAQdxConfigureGrab(session);
}
@@ -44,6 +45,9 @@ public class Robot extends SampleRobot {
DrawMode.DRAW_VALUE, ShapeMode.SHAPE_OVAL, 0.0f);
CameraServer.getInstance().setImage(frame);
/** robot code here! **/
Timer.delay(0.005); // wait for a motor update time
}
NIVision.IMAQdxStopAcquisition(session);
}

View File

@@ -25,10 +25,10 @@ public class Robot extends SampleRobot {
final int joystickChannel = 0;
public Robot() {
robotDrive.setExpiration(0.1);
robotDrive = new RobotDrive(frontLeftChannel, rearLeftChannel, frontRightChannel, rearRightChannel);
robotDrive.setInvertedMotor(MotorType.kFrontLeft, true); // invert the left side motors
robotDrive.setInvertedMotor(MotorType.kRearLeft, true); // you may need to change or remove this to match your robot
robotDrive.setExpiration(0.1);
stick = new Joystick(joystickChannel);
}

View File

@@ -1,14 +1,13 @@
package $package;
import edu.wpi.first.wpilibj.CameraServer;
import edu.wpi.first.wpilibj.SampleRobot;
import edu.wpi.first.wpilibj.RobotDrive;
import edu.wpi.first.wpilibj.Joystick;
import edu.wpi.first.wpilibj.Timer;
/**
* This is a demo program showing the use of the CameraServer class.
* With start automatic capture, there is no opertunity to process the image.
* Look at the AdvancedVision sample for how to process the image before sending it to the FRC PC Dashboard.
* With start automatic capture, there is no opportunity to process the image.
* Look at the IntermediateVision sample for how to process the image before sending it to the FRC PC Dashboard.
*/
public class Robot extends SampleRobot {
@@ -17,6 +16,8 @@ public class Robot extends SampleRobot {
public Robot() {
server = CameraServer.getInstance();
server.setQuality(50);
//the camera name (ex "cam0") can be found through the roborio web interface
server.startAutomaticCapture("cam0");
}
/**
@@ -25,10 +26,9 @@ public class Robot extends SampleRobot {
*/
public void operatorControl() {
server.startAutomaticCapture("cam1");
while (isOperatorControl() && isEnabled()) {
/** robot code here! **/
Timer.delay(0.005); // wait for a motor update time
}
}

View File

@@ -92,6 +92,7 @@
teleoperated routines.</description>
<tags>
<tag>Getting Started with Java</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src/$package-dir</package>
@@ -294,10 +295,11 @@
</example>
<example>
<name>Quick Vision</name>
<name>Simple Vision</name>
<description>Demonstrate the use of the CameraServer class to stream from a USB Webcam without processing the images.</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src/$package-dir</package>
@@ -309,18 +311,79 @@
</example>
<example>
<name>Advanced Vision</name>
<name>Intermediate Vision</name>
<description>Demonstrate the use of the NIVision class to capture image from a Webcam, process them, and then send them to the dashboard.</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src/$package-dir</package>
</packages>
<files>
<file source="examples/AdvancedVision/src/org/usfirst/frc/team190/robot/Robot.java"
<file source="examples/IntermediateVision/src/org/usfirst/frc/team190/robot/Robot.java"
destination="src/$package-dir/Robot.java"></file>
</files>
</example>
<example>
<name>2015 Vision Color Sample</name>
<description>An example program that demonstrates image processing to locate Yellow totes by color.
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
camera example;
</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src/$package-dir</package>
</packages>
<files>
<file source="examples/2015Vision/Color_src/Robot.java"
destination="src/$package-dir/Robot.java"></file>
</files>
</example>
<example>
<name>2015 Vision Retro Sample</name>
<description>An example program that demonstrates image processing to locate Yellow totes by the retroreflective target.
This example uses a file which must be copied over to the roboRIO via FTP to demonstrate processing.
To use this code with a camera, you must integrate the code for image acquisition from the appropriate
camera example;
</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src/$package-dir</package>
</packages>
<files>
<file source="examples/2015Vision/Retro_src/Robot.java"
destination="src/$package-dir/Robot.java"></file>
</files>
</example>
<example>
<name>Axis Camera Sample</name>
<description>An example program that acquires images from an Axis network camera and adds some
annotation to the image as you might do for showing operators the result of some image
recognition, and sends it to the dashboard for display. This demonstrates the use of the
AxisCamera class.
</description>
<tags>
<tag>Vision</tag>
<tag>Complete List</tag>
</tags>
<packages>
<package>src/$package-dir</package>
</packages>
<files>
<file source="examples/AxisCameraSample/src/org/usfirst/frc/team190/robot/Robot.java"
destination="src/$package-dir/Robot.java"></file>
</files>
</example>
</examples>

View File

@@ -104,7 +104,16 @@
<target name="deploy" depends="clean,jar,get-target-ip,dependencies" description="Deploy the jar and start the program running.">
<echo>[athena-deploy] Copying code over.</echo>
<scp file="${dist.jar}" todir="${username}@${target}:${deploy.dir}" password="${password}" trust="true"/>
<scp file="${wpilib.ant.dir}/robotCommand" todir="${username}@${target}:${command.dir}" password="${password}" trust="true"/>
<sshexec host="${target}"
username="admin"
password="${password}"
trust="true"
failonerror="false"
command="killall netconsole-host"/>
<scp file="${wpilib.ant.dir}/netconsole-host" todir="admin@${target}:/usr/local/frc/bin" password="${password}" trust="true"/>
<scp file="${wpilib.ant.dir}/robotCommand" todir="${username}@${target}:${command.dir}" password="${password}" trust="true"/>
<echo>[athena-deploy] Starting program.</echo>
<sshexec host="${target}"
@@ -221,7 +230,7 @@
</bool>
</assert>
<echo>roboRIO image version validated</echo>
<echo>Checking for JRE. If this fails install the JRE using these instructions: http://wpilib.screenstepslive.com/s/4485/m/13809/l/243933-installing-java-8-on-the-roborio-java-only</echo>
<echo>Checking for JRE. If this fails install the JRE using these instructions: https://wpilib.screenstepslive.com/s/4485/m/13503/l/288822-installing-java-8-on-the-roborio-using-the-frc-roborio-java-installer-java-only</echo>
<sshexec host="${target}"
username="${username}"
password="${password}"

View File

@@ -23,6 +23,14 @@ extern "C" {
bool getPressureSwitch(void *pcm_pointer, int32_t *status);
float getCompressorCurrent(void *pcm_pointer, int32_t *status);
bool getCompressorCurrentTooHighFault(void *pcm_pointer, int32_t *status);
bool getCompressorCurrentTooHighStickyFault(void *pcm_pointer, int32_t *status);
bool getCompressorShortedStickyFault(void *pcm_pointer, int32_t *status);
bool getCompressorShortedFault(void *pcm_pointer, int32_t *status);
bool getCompressorNotConnectedStickyFault(void *pcm_pointer, int32_t *status);
bool getCompressorNotConnectedFault(void *pcm_pointer, int32_t *status);
void clearAllPCMStickyFaults(void *pcm_pointer, int32_t *status);
}
#endif

View File

@@ -86,6 +86,8 @@ extern "C"
void setEncoderSamplesToAverage(void* encoder_pointer, uint32_t samplesToAverage,
int32_t *status);
uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status);
void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh,
bool edgeSensitive, int32_t *status);
uint16_t getLoopTiming(int32_t *status);

View File

@@ -13,4 +13,9 @@ extern "C"
bool getSolenoid(void* solenoid_port_pointer, int32_t *status);
void setSolenoid(void* solenoid_port_pointer, bool value, int32_t *status);
int getPCMSolenoidBlackList(void* solenoid_port_pointer, int32_t *status);
bool getPCMSolenoidVoltageStickyFault(void* solenoid_port_pointer, int32_t *status);
bool getPCMSolenoidVoltageFault(void* solenoid_port_pointer, int32_t *status);
void clearAllPCMStickyFaults_sol(void *solenoid_port_pointer, int32_t *status);
}

View File

@@ -61,4 +61,56 @@ float getCompressorCurrent(void *pcm_pointer, int32_t *status) {
return value;
}
bool getCompressorCurrentTooHighFault(void *pcm_pointer, int32_t *status) {
PCM *module = (PCM *)pcm_pointer;
bool value;
*status = module->GetCompressorCurrentTooHighFault(value);
return value;
}
bool getCompressorCurrentTooHighStickyFault(void *pcm_pointer, int32_t *status) {
PCM *module = (PCM *)pcm_pointer;
bool value;
*status = module->GetCompressorCurrentTooHighStickyFault(value);
return value;
}
bool getCompressorShortedStickyFault(void *pcm_pointer, int32_t *status) {
PCM *module = (PCM *)pcm_pointer;
bool value;
*status = module->GetCompressorShortedStickyFault(value);
return value;
}
bool getCompressorShortedFault(void *pcm_pointer, int32_t *status) {
PCM *module = (PCM *)pcm_pointer;
bool value;
*status = module->GetCompressorShortedFault(value);
return value;
}
bool getCompressorNotConnectedStickyFault(void *pcm_pointer, int32_t *status) {
PCM *module = (PCM *)pcm_pointer;
bool value;
*status = module->GetCompressorNotConnectedStickyFault(value);
return value;
}
bool getCompressorNotConnectedFault(void *pcm_pointer, int32_t *status) {
PCM *module = (PCM *)pcm_pointer;
bool value;
*status = module->GetCompressorNotConnectedFault(value);
return value;
}
void clearAllPCMStickyFaults(void *pcm_pointer, int32_t *status) {
PCM *module = (PCM *)pcm_pointer;
*status = module->ClearStickyFaults();
}

View File

@@ -632,6 +632,25 @@ void setCounterAverageSize(void* counter_pointer, int32_t size, int32_t *status)
counter->counter->writeTimerConfig_AverageSize(size, status);
}
/**
* remap the digital source pin and set the module.
* If it's an analog trigger, determine the module from the high order routing channel
* else do normal digital input remapping based on pin number (MXP)
*/
void remapDigitalSource(bool analogTrigger, uint32_t &pin, uint8_t &module) {
if (analogTrigger) {
module = pin >> 4;
} else {
if(pin >= kNumHeaders) {
pin = remapMXPChannel(pin);
module = 1;
} else {
module = 0;
}
}
}
/**
* Set the source object that causes the counter to count up.
* Set the up counting DigitalSource.
@@ -641,12 +660,7 @@ void setCounterUpSource(void* counter_pointer, uint32_t pin, bool analogTrigger,
uint8_t module;
if(pin >= kNumHeaders) {
pin = remapMXPChannel(pin);
module = 1;
} else {
module = 0;
}
remapDigitalSource(analogTrigger, pin, module);
counter->counter->writeConfig_UpSource_Module(module, status);
counter->counter->writeConfig_UpSource_Channel(pin, status);
@@ -696,12 +710,7 @@ void setCounterDownSource(void* counter_pointer, uint32_t pin, bool analogTrigge
uint8_t module;
if(pin >= kNumHeaders) {
pin = remapMXPChannel(pin);
module = 1;
} else {
module = 0;
}
remapDigitalSource(analogTrigger, pin, module);
counter->counter->writeConfig_DownSource_Module(module, status);
counter->counter->writeConfig_DownSource_Channel(pin, status);
@@ -838,7 +847,7 @@ double getCounterPeriod(void* counter_pointer, int32_t *status) {
// output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
period = (double)(output.Period << 1) / (double)output.Count;
}
return period * 1.0e-6;
return period * 2.5e-8; // result * timebase (currently 40ns)
}
/**
@@ -850,7 +859,7 @@ double getCounterPeriod(void* counter_pointer, int32_t *status) {
*/
void setCounterMaxPeriod(void* counter_pointer, double maxPeriod, int32_t *status) {
Counter* counter = (Counter*) counter_pointer;
counter->counter->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 1.0e6), status);
counter->counter->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8), status);
}
/**
@@ -925,15 +934,8 @@ void* initializeEncoder(uint8_t port_a_module, uint32_t port_a_pin, bool port_a_
// Initialize encoder structure
Encoder* encoder = new Encoder();
if(port_a_pin >= kNumHeaders) {
port_a_pin = remapMXPChannel(port_a_pin);
port_a_module = 1;
}
if(port_b_pin >= kNumHeaders) {
port_b_pin = remapMXPChannel(port_b_pin);
port_b_module = 1;
}
remapDigitalSource(port_a_analog_trigger, port_a_pin, port_a_module);
remapDigitalSource(port_b_analog_trigger, port_b_pin, port_b_module);
Resource::CreateResourceObject(&quadEncoders, tEncoder::kNumSystems);
encoder->index = quadEncoders->Allocate("4X Encoder");
@@ -1000,7 +1002,7 @@ double getEncoderPeriod(void* encoder_pointer, int32_t *status) {
// output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits)
value = (double)(output.Period << 1) / (double)output.Count;
}
double measuredPeriod = value * 1.0e-6;
double measuredPeriod = value * 2.5e-8;
return measuredPeriod / DECODING_SCALING_FACTOR;
}
@@ -1018,7 +1020,7 @@ double getEncoderPeriod(void* encoder_pointer, int32_t *status) {
*/
void setEncoderMaxPeriod(void* encoder_pointer, double maxPeriod, int32_t *status) {
Encoder* encoder = (Encoder*) encoder_pointer;
encoder->encoder->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 1.0e6 * DECODING_SCALING_FACTOR), status);
encoder->encoder->writeTimerConfig_StallPeriod((uint32_t)(maxPeriod * 4.0e8 * DECODING_SCALING_FACTOR), status);
}
/**
@@ -1078,6 +1080,20 @@ uint32_t getEncoderSamplesToAverage(void* encoder_pointer, int32_t *status) {
return encoder->encoder->readTimerConfig_AverageSize(status);
}
/**
* Set an index source for an encoder, which is an input that resets the
* encoder's count.
*/
void setEncoderIndexSource(void *encoder_pointer, uint32_t pin, bool analogTrigger, bool activeHigh,
bool edgeSensitive, int32_t *status) {
Encoder* encoder = (Encoder*) encoder_pointer;
encoder->encoder->writeConfig_IndexSource_Channel((unsigned char)pin, status);
encoder->encoder->writeConfig_IndexSource_Module((unsigned char)0, status);
encoder->encoder->writeConfig_IndexSource_AnalogTrigger(analogTrigger, status);
encoder->encoder->writeConfig_IndexActiveHigh(activeHigh, status);
encoder->encoder->writeConfig_IndexEdgeSensitive(edgeSensitive, status);
}
/**
* Get the loop timing of the PWM system
*

View File

@@ -1,6 +1,8 @@
#include "HAL/Interrupts.hpp"
#include "ChipObject.h"
extern void remapDigitalSource(bool analogTrigger, uint32_t &pin, uint8_t &module);
struct Interrupt // FIXME: why is this internal?
{
tInterrupt *anInterrupt;
@@ -99,6 +101,7 @@ void requestInterrupts(void* interrupt_pointer, uint8_t routing_module, uint32_t
{
Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
anInterrupt->anInterrupt->writeConfig_WaitForAck(false, status);
remapDigitalSource(routing_analog_trigger, routing_pin, routing_module);
anInterrupt->anInterrupt->writeConfig_Source_AnalogTrigger(routing_analog_trigger, status);
anInterrupt->anInterrupt->writeConfig_Source_Channel(routing_pin, status);
anInterrupt->anInterrupt->writeConfig_Source_Module(routing_module, status);

View File

@@ -52,3 +52,33 @@ void setSolenoid(void* solenoid_port_pointer, bool value, int32_t *status) {
port->module->SetSolenoid(port->pin, value);
}
int getPCMSolenoidBlackList(void* solenoid_port_pointer, int32_t *status){
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
UINT8 value;
*status = port->module->GetSolenoidBlackList(value);
return value;
}
bool getPCMSolenoidVoltageStickyFault(void* solenoid_port_pointer, int32_t *status){
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
bool value;
*status = port->module->GetSolenoidStickyFault(value);
return value;
}
bool getPCMSolenoidVoltageFault(void* solenoid_port_pointer, int32_t *status){
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
bool value;
*status = port->module->GetSolenoidFault(value);
return value;
}
void clearAllPCMStickyFaults_sol(void *solenoid_port_pointer, int32_t *status){
solenoid_port_t* port = (solenoid_port_t*) solenoid_port_pointer;
*status = port->module->ClearStickyFaults();
}

View File

@@ -1222,6 +1222,10 @@ CTR_Code c_TalonSRX_SetModeSelect(void *handle, int param)
{
return ((CanTalonSRX*)handle)->SetModeSelect(param);
}
CTR_Code c_TalonSRX_SetModeSelect2(void *handle, int modeSelect, int demand)
{
return ((CanTalonSRX*)handle)->SetModeSelect(modeSelect, demand);
}
CTR_Code c_TalonSRX_SetProfileSlotSelect(void *handle, int param)
{
return ((CanTalonSRX*)handle)->SetProfileSlotSelect(param);

View File

@@ -366,6 +366,7 @@ extern "C" {
CTR_Code c_TalonSRX_SetRevMotDuringCloseLoopEn(void *handle, int param);
CTR_Code c_TalonSRX_SetOverrideBrakeType(void *handle, int param);
CTR_Code c_TalonSRX_SetModeSelect(void *handle, int param);
CTR_Code c_TalonSRX_SetModeSelect2(void *handle, int modeSelect, int demand);
CTR_Code c_TalonSRX_SetProfileSlotSelect(void *handle, int param);
CTR_Code c_TalonSRX_SetRampThrottle(void *handle, int param);
CTR_Code c_TalonSRX_SetRevFeedbackSensor(void *handle, int param);

View File

@@ -13,11 +13,13 @@ static const INT32 kCANPeriod = 20;
#define STATUS_DEBUG 0x9041480
#define EXPECTED_RESPONSE_TIMEOUT_MS (50)
#define GET_PCM_STATUS() CtreCanNode::recMsg<PcmStatus_t> rx = GetRx<PcmStatus_t> (STATUS_1,EXPECTED_RESPONSE_TIMEOUT_MS)
#define GET_PCM_SOL_FAULTS() CtreCanNode::recMsg<PcmStatusFault_t> rx = GetRx<PcmStatusFault_t> (STATUS_SOL_FAULTS,EXPECTED_RESPONSE_TIMEOUT_MS)
#define GET_PCM_DEBUG() CtreCanNode::recMsg<PcmDebug_t> rx = GetRx<PcmDebug_t> (STATUS_DEBUG,EXPECTED_RESPONSE_TIMEOUT_MS)
#define GET_PCM_STATUS() CtreCanNode::recMsg<PcmStatus_t> rx = GetRx<PcmStatus_t> (STATUS_1|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS)
#define GET_PCM_SOL_FAULTS() CtreCanNode::recMsg<PcmStatusFault_t> rx = GetRx<PcmStatusFault_t> (STATUS_SOL_FAULTS|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS)
#define GET_PCM_DEBUG() CtreCanNode::recMsg<PcmDebug_t> rx = GetRx<PcmDebug_t> (STATUS_DEBUG|GetDeviceNumber(),EXPECTED_RESPONSE_TIMEOUT_MS)
#define CONTROL_1 0x09041C00
#define CONTROL_1 0x09041C00 /* PCM_Control */
#define CONTROL_2 0x09041C40 /* PCM_SupplemControl */
#define CONTROL_3 0x09041C80 /* PcmControlSetOneShotDur_t */
/* encoder/decoders */
typedef struct _PcmStatus_t{
@@ -27,8 +29,8 @@ typedef struct _PcmStatus_t{
unsigned compressorOn:1;
unsigned stickyFaultFuseTripped:1;
unsigned stickyFaultCompCurrentTooHigh:1;
unsigned faultCompCurrentTooHigh:1;
unsigned faultFuseTripped:1;
unsigned faultCompCurrentTooHigh:1;
unsigned faultHardwareFailure:1;
unsigned isCloseloopEnabled:1;
unsigned pressureSwitchEn:1;
@@ -40,7 +42,8 @@ typedef struct _PcmStatus_t{
unsigned compressorCurrentTop6:6;
unsigned solenoidVoltageBtm2:2;
/* Byte 5 */
unsigned reserved:2;
unsigned StickyFault_dItooHigh :1;
unsigned Fault_dItooHigh :1;
unsigned moduleEnabled:1;
unsigned closedLoopOutput:1;
unsigned compressorCurrentBtm4:4;
@@ -63,19 +66,28 @@ typedef struct _PcmControl_t{
unsigned compressorOn:1;
unsigned closedLoopEnable:1;
unsigned clearStickyFaults:1;
/* Byte 4 */
unsigned OneShotField_h8:8;
/* Byte 5 */
unsigned OneShotField_l8:8;
}PcmControl_t;
typedef struct _PcmControlSetOneShotDur_t{
uint8_t sol10MsPerUnit[8];
}PcmControlSetOneShotDur_t;
typedef struct _PcmStatusFault_t{
/* Byte 0 */
unsigned SolenoidBlacklist:8;
/* Byte 1 */
unsigned reserved1:8;
unsigned reserved2:8;
unsigned reserved3:8;
unsigned reserved4:8;
unsigned reserved5:8;
unsigned reserved6:8;
unsigned reserved7:8;
unsigned reserved_bit0 :1;
unsigned reserved_bit1 :1;
unsigned reserved_bit2 :1;
unsigned reserved_bit3 :1;
unsigned StickyFault_CompNoCurrent :1;
unsigned Fault_CompNoCurrent :1;
unsigned StickyFault_SolenoidJumper :1;
unsigned Fault_SolenoidJumper :1;
}PcmStatusFault_t;
typedef struct _PcmDebug_t{
@@ -135,12 +147,13 @@ CTR_Code PCM::SetSolenoid(unsigned char idx, bool en)
*
* @Param - clr - Clear / do not clear faults
*/
CTR_Code PCM::ClearStickyFaults(bool clr)
CTR_Code PCM::ClearStickyFaults()
{
CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
if(toFill.IsEmpty())return CTR_UnexpectedArbId;
toFill->clearStickyFaults = clr;
FlushTx(toFill);
int32_t status = 0;
uint8_t pcmSupplemControl[] = { 0, 0, 0, 0x80 }; /* only bit set is ClearStickyFaults */
FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_2 | GetDeviceNumber(), pcmSupplemControl, sizeof(pcmSupplemControl), 0, &status);
if(status)
return CTR_TxFailed;
return CTR_OKAY;
}
@@ -158,6 +171,59 @@ CTR_Code PCM::SetClosedLoopControl(bool en)
FlushTx(toFill);
return CTR_OKAY;
}
/* Get solenoid Blacklist status
* @Return - CTR_Code - Error code (if any)
* @Param - idx - ID of solenoid [0,7] to fire one shot pulse.
*/
CTR_Code PCM::FireOneShotSolenoid(UINT8 idx)
{
CtreCanNode::txTask<PcmControl_t> toFill = GetTx<PcmControl_t>(CONTROL_1 | GetDeviceNumber());
if(toFill.IsEmpty())return CTR_UnexpectedArbId;
/* grab field as it is now */
uint16_t oneShotField;
oneShotField = toFill->OneShotField_h8;
oneShotField <<= 8;
oneShotField |= toFill->OneShotField_l8;
/* get the caller's channel */
uint16_t shift = 2*idx;
uint16_t mask = 3; /* two bits wide */
uint8_t chBits = (oneShotField >> shift) & mask;
/* flip it */
chBits = (chBits)%3 + 1;
/* clear out 2bits for this channel*/
oneShotField &= ~(mask << shift);
/* put new field in */
oneShotField |= chBits << shift;
/* apply field as it is now */
toFill->OneShotField_h8 = oneShotField >> 8;
toFill->OneShotField_l8 = oneShotField;
FlushTx(toFill);
return CTR_OKAY;
}
/* Configure the pulse width of a solenoid channel for one-shot pulse.
* Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
* @Return - CTR_Code - Error code (if any)
* @Param - idx - ID of solenoid [0,7] to configure.
* @Param - durMs - pulse width in ms.
*/
CTR_Code PCM::SetOneShotDurationMs(UINT8 idx,uint32_t durMs)
{
/* sanity check caller's param */
if(idx > 8)
return CTR_InvalidParamValue;
/* get latest tx frame */
CtreCanNode::txTask<PcmControlSetOneShotDur_t> toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
if(toFill.IsEmpty()){
/* only send this out if caller wants to do one-shots */
RegisterTx(CONTROL_3 | _deviceNumber, kCANPeriod);
/* grab it */
toFill = GetTx<PcmControlSetOneShotDur_t>(CONTROL_3 | GetDeviceNumber());
}
toFill->sol10MsPerUnit[idx] = std::min(durMs/10,(uint32_t)0xFF);
/* apply the new data bytes */
FlushTx(toFill);
return CTR_OKAY;
}
/* Get solenoid state
*
@@ -248,12 +314,36 @@ CTR_Code PCM::GetHardwareFault(bool &status)
*
* @Return - True/False - True if shorted compressor detected, false if otherwise
*/
CTR_Code PCM::GetCompressorFault(bool &status)
CTR_Code PCM::GetCompressorCurrentTooHighFault(bool &status)
{
GET_PCM_STATUS();
status = rx->faultCompCurrentTooHigh;
return rx.err;
}
CTR_Code PCM::GetCompressorShortedStickyFault(bool &status)
{
GET_PCM_STATUS();
status = rx->StickyFault_dItooHigh;
return rx.err;
}
CTR_Code PCM::GetCompressorShortedFault(bool &status)
{
GET_PCM_STATUS();
status = rx->Fault_dItooHigh;
return rx.err;
}
CTR_Code PCM::GetCompressorNotConnectedStickyFault(bool &status)
{
GET_PCM_SOL_FAULTS();
status = rx->StickyFault_CompNoCurrent;
return rx.err;
}
CTR_Code PCM::GetCompressorNotConnectedFault(bool &status)
{
GET_PCM_SOL_FAULTS();
status = rx->Fault_CompNoCurrent;
return rx.err;
}
/* Get solenoid fault value
*
@@ -271,7 +361,7 @@ CTR_Code PCM::GetSolenoidFault(bool &status)
* @Return - True/False - True if solenoid had previously been shorted
* (and sticky fault was not cleared), false if otherwise
*/
CTR_Code PCM::GetCompressorStickyFault(bool &status)
CTR_Code PCM::GetCompressorCurrentTooHighStickyFault(bool &status)
{
GET_PCM_STATUS();
status = rx->stickyFaultCompCurrentTooHigh;
@@ -369,7 +459,7 @@ extern "C" {
return ((PCM*) handle)->SetClosedLoopControl(param);
}
CTR_Code c_ClearStickyFaults(void * handle, INT8 param) {
return ((PCM*) handle)->ClearStickyFaults(param);
return ((PCM*) handle)->ClearStickyFaults();
}
CTR_Code c_GetSolenoid(void * handle, UINT8 idx, INT8 * status) {
bool bstatus;
@@ -410,7 +500,7 @@ extern "C" {
}
CTR_Code c_GetCompressorFault(void * handle, INT8*status) {
bool bstatus;
CTR_Code retval = ((PCM*) handle)->GetCompressorFault(bstatus);
CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighFault(bstatus);
*status = bstatus;
return retval;
}
@@ -422,7 +512,7 @@ extern "C" {
}
CTR_Code c_GetCompressorStickyFault(void * handle, INT8*status) {
bool bstatus;
CTR_Code retval = ((PCM*) handle)->GetCompressorStickyFault(bstatus);
CTR_Code retval = ((PCM*) handle)->GetCompressorCurrentTooHighStickyFault(bstatus);
*status = bstatus;
return retval;
}

View File

@@ -26,9 +26,8 @@ public:
/* Clears PCM sticky faults (indicators of past faults
* @Return - CTR_Code - Error code (if any) for setting solenoid
* @Param - clr - Clear / do not clear faults
*/
CTR_Code ClearStickyFaults(bool clr);
CTR_Code ClearStickyFaults();
/* Get solenoid state
*
@@ -76,9 +75,9 @@ public:
/* Get compressor fault value
* @Return - CTR_Code - Error code (if any)
* @Param - status - True if shorted compressor detected, false if otherwise
* @Param - status - True if abnormally high compressor current detected, false if otherwise
*/
CTR_Code GetCompressorFault(bool &status);
CTR_Code GetCompressorCurrentTooHighFault(bool &status);
/* Get solenoid fault value
* @Return - CTR_Code - Error code (if any)
@@ -91,7 +90,29 @@ public:
* @Param - status - True if solenoid had previously been shorted
* (and sticky fault was not cleared), false if otherwise
*/
CTR_Code GetCompressorStickyFault(bool &status);
CTR_Code GetCompressorCurrentTooHighStickyFault(bool &status);
/* Get compressor shorted sticky fault value
* @Return - CTR_Code - Error code (if any)
* @Param - status - True if compressor output is shorted, false if otherwise
*/
CTR_Code GetCompressorShortedStickyFault(bool &status);
/* Get compressor shorted fault value
* @Return - CTR_Code - Error code (if any)
* @Param - status - True if compressor output is shorted, false if otherwise
*/
CTR_Code GetCompressorShortedFault(bool &status);
/* Get compressor is not connected sticky fault value
* @Return - CTR_Code - Error code (if any)
* @Param - status - True if compressor current is too low,
* indicating compressor is not connected, false if otherwise
*/
CTR_Code GetCompressorNotConnectedStickyFault(bool &status);
/* Get compressor is not connected fault value
* @Return - CTR_Code - Error code (if any)
* @Param - status - True if compressor current is too low,
* indicating compressor is not connected, false if otherwise
*/
CTR_Code GetCompressorNotConnectedFault(bool &status);
/* Get solenoid sticky fault value
* @Return - CTR_Code - Error code (if any)
@@ -146,6 +167,21 @@ public:
* @Param - status - Returns TRUE if PCM is enabled, FALSE if disabled
*/
CTR_Code isModuleEnabled(bool &status);
/* Get solenoid Blacklist status
* @Return - CTR_Code - Error code (if any)
* @Param - idx - ID of solenoid [0,7] to fire one shot pulse.
*/
CTR_Code FireOneShotSolenoid(UINT8 idx);
/* Configure the pulse width of a solenoid channel for one-shot pulse.
* Preprogrammed pulsewidth is 10ms resolute and can be between 20ms and 5.1s.
* @Return - CTR_Code - Error code (if any)
* @Param - idx - ID of solenoid [0,7] to configure.
* @Param - durMs - pulse width in ms.
*/
CTR_Code SetOneShotDurationMs(UINT8 idx,uint32_t durMs);
};
//------------------ C interface --------------------------------------------//
extern "C" {

View File

@@ -178,7 +178,8 @@ CTR_Code PDP::GetTotalEnergy(double &energyJoules)
raw <<= 8;
raw |= rx->Energy_125mWPerUnitXTmeas_l8;
energyJoules = 0.125 * raw; /* mW integrated every TmeasMs */
energyJoules *= rx->TmeasMs_likelywillbe20ms_; /* multiplied by TmeasMs = joules */
energyJoules *= 0.001; /* convert from mW to W */
energyJoules *= rx->TmeasMs_likelywillbe20ms_; /* multiplied by TmeasMs = joules */
return rx.err;
}
/* Clear sticky faults.

View File

@@ -234,7 +234,7 @@ TCL_SUBST =
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored

View File

@@ -35,6 +35,8 @@ public:
class AbstractNetworkTableEntryStore : public IncomingEntryReceiver{
protected:
NTReentrantSemaphore block_namedEntries;
std::map<EntryId,NetworkTableEntry*> idEntries;
std::map<std::string,NetworkTableEntry*> namedEntries;
TableListenerManager& listenerManager;
@@ -45,10 +47,10 @@ protected:
OutgoingEntryReceiver* incomingReceiver;
virtual bool addEntry(NetworkTableEntry* entry) = 0;
virtual bool updateEntry(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value) = 0;
public:
virtual ~AbstractNetworkTableEntryStore();
@@ -62,14 +64,14 @@ public:
void SetOutgoingReceiver(OutgoingEntryReceiver* receiver);
void SetIncomingReceiver(OutgoingEntryReceiver* receiver);
void PutOutgoing(std::string& name, NetworkTableEntryType* type, EntryValue value);
void PutOutgoing(NetworkTableEntry* tableEntry, EntryValue value);
void offerIncomingAssignment(NetworkTableEntry* entry);
void offerIncomingUpdate(NetworkTableEntry* entry, EntryId sequenceNumber, EntryValue value);
void notifyEntries(ITable* table, ITableListener* listener);
};

View File

@@ -42,6 +42,7 @@ public:
NetworkTableEntry(std::string& name, NetworkTableEntryType* type, EntryValue value);
NetworkTableEntry(EntryId id, std::string& name, SequenceNumber sequenceNumber, NetworkTableEntryType* type, EntryValue value);
NetworkTableEntry(const NetworkTableEntry &);
virtual ~NetworkTableEntry();
EntryId GetId();

View File

@@ -15,6 +15,7 @@
#include <iolib.h>
#else
#include <unistd.h>
#include <fcntl.h>
#endif
#include <stdio.h>
@@ -24,6 +25,13 @@ FDIOStream::FDIOStream(int _fd){
// f = fdopen(_fd, "rbwb");
// if(f==NULL)
// throw IOException("Could not open stream from file descriptor", errno);
// Set the TCP socket to be non-blocking
int flags = fcntl(fd, F_GETFL, 0);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
{
::close(fd);
throw IOException("Could not set socket to non-blocking mode");
}
}
FDIOStream::~FDIOStream(){
close();
@@ -66,14 +74,35 @@ int FDIOStream::read(void* ptr, int numbytes){
return totalRead;
}
int FDIOStream::write(const void* ptr, int numbytes){
int numWrote = ::write(fd, (char*)ptr, numbytes);//TODO: this is bad
//int numWrote = fwrite(ptr, 1, numbytes, f);
if(numWrote==numbytes)
return numWrote;
perror("write error: ");
fflush(stderr);
throw IOException("Could not write all bytes to fd stream");
int numWrote = ::write(fd, (char*)ptr, numbytes);
if(numWrote==numbytes)
return numWrote;
if (numWrote == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
{
// see if write timeout expires
struct timeval timeout;
fd_set fdSet;
FD_ZERO(&fdSet);
FD_SET(fd, &fdSet);
timeout.tv_sec = 1; // wait 1 second for the other side to connect
timeout.tv_usec = 0;
int select_result = select(FD_SETSIZE, NULL, &fdSet, NULL, &timeout);
if ( select_result < 0)
throw IOException("Select returned an error on write");
if (FD_ISSET(fd, &fdSet)) {
numWrote = ::write(fd, (char*)ptr, numbytes);
if(numWrote==numbytes)
return numWrote;
}
}
perror("write error: ");
fflush(stderr);
throw IOException("Could not write all bytes to fd stream");
}
void FDIOStream::flush(){
//if(fflush(f)==EOF)

View File

@@ -1,6 +1,8 @@
#include "stdafx.h"
#include "OSAL/Task.h"
#include "NetworkCommunication/UsageReporting.h"
#include "WPIErrors.h"
#include <string.h>
#include <Windows.h>
@@ -55,7 +57,7 @@ DWORD thread_proc( void *p_ptr )
//This sets the name of the thread, which can help to identify threads in win32
#define MS_VC_EXCEPTION 0x406D1388
static void set_thread_name( const char *p_thread_name, DWORD ID )
{
{
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{ DWORD dwType; // Must be 0x1000.
@@ -162,7 +164,7 @@ bool NTTask::Stop()
::TerminateThread( m_Handle, 0 );
// Free the memory
::VirtualFree( Info_.AllocationBase, 0, MEM_RELEASE );
::VirtualFree( Info_.AllocationBase, 0, MEM_RELEASE );
}
//if (Verify())
@@ -289,19 +291,19 @@ bool NTTask::HandleError(STATUS results)
//case S_objLib_OBJ_ID_ERROR:
// wpi_setWPIErrorWithContext(TaskIDError, m_taskName);
// break;
//
//
//case S_objLib_OBJ_DELETED:
// wpi_setWPIErrorWithContext(TaskDeletedError, m_taskName);
// break;
//
//
//case S_taskLib_ILLEGAL_OPTIONS:
// wpi_setWPIErrorWithContext(TaskOptionsError, m_taskName);
// break;
//
//
//case S_memLib_NOT_ENOUGH_MEMORY:
// wpi_setWPIErrorWithContext(TaskMemoryError, m_taskName);
// break;
//
//
//case S_taskLib_ILLEGAL_PRIORITY:
// wpi_setWPIErrorWithContext(TaskPriorityError, m_taskName);
// break;
@@ -312,4 +314,3 @@ bool NTTask::HandleError(STATUS results)
//}
return false;
}

View File

@@ -20,6 +20,13 @@
FDIOStream::FDIOStream(int _fd){
fd = _fd;
// Set the TCP socket to be non-blocking
u_long on = 1;
if (ioctlsocket(fd, FIONBIO, &on))
{
::close(fd);
throw IOException("Could not set socket to non-blocking mode");
}
}
FDIOStream::~FDIOStream(){
@@ -79,13 +86,35 @@ int Send( int sockfd,char* Data, size_t sizeData )
int FDIOStream::write(const void* ptr, int numbytes)
{
int numWrote = Send(fd,(char *)ptr,numbytes);
if(numWrote==numbytes)
return numWrote;
perror("write error: ");
fflush(stderr);
throw IOException("Could not write all bytes to fd stream");
int numWrote = ::write(fd, (char*)ptr, numbytes);
if(numWrote==numbytes)
return numWrote;
if (numWrote == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
{
// see if write timeout expires
struct timeval timeout;
fd_set fdSet;
FD_ZERO(&fdSet);
FD_SET(fd, &fdSet);
timeout.tv_sec = 1; // wait 1 second for the other side to connect
timeout.tv_usec = 0;
int select_result = select(FD_SETSIZE, NULL, &fdSet, NULL, &timeout);
if ( select_result < 0)
throw IOException("Select returned an error on write");
if (FD_ISSET(fd, &fdSet)) {
numWrote = ::write(fd, (char*)ptr, numbytes);
if(numWrote==numbytes)
return numWrote;
}
}
perror("write error: ");
fflush(stderr);
throw IOException("Could not write all bytes to fd stream");
}
void FDIOStream::flush(){

View File

@@ -12,10 +12,10 @@
#include <iostream>
#include <stdio.h>
AbstractNetworkTableEntryStore::AbstractNetworkTableEntryStore(TableListenerManager& lstnManager):
AbstractNetworkTableEntryStore::AbstractNetworkTableEntryStore(TableListenerManager& lstnManager):
listenerManager(lstnManager){
}
AbstractNetworkTableEntryStore::~AbstractNetworkTableEntryStore(){
LOCK.take();
std::map<std::string, NetworkTableEntry*>::iterator itr;
@@ -24,15 +24,15 @@
namedEntries.erase(itr++);
}
}
/**
* Get an entry based on it's name
* @param name the name of the entry to look for
* @return the entry or null if the entry does not exist
*/
NetworkTableEntry* AbstractNetworkTableEntryStore::GetEntry(std::string& name){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
std::map<std::string, NetworkTableEntry*>::iterator value_itr = namedEntries.find(name);
if(value_itr != namedEntries.end()) {
return value_itr->second;
@@ -40,11 +40,11 @@
return NULL;
}
}
NetworkTableEntry* AbstractNetworkTableEntryStore::GetEntry(EntryId entryId){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
std::map<EntryId, NetworkTableEntry*>::iterator value_itr = idEntries.find(entryId);
if(value_itr != idEntries.end()) {
return value_itr->second;
@@ -52,74 +52,74 @@
return NULL;
}
}
std::vector<std::string>* AbstractNetworkTableEntryStore::keys(){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
std::vector<std::string>* keys = new std::vector<std::string>();
std::map<std::string, NetworkTableEntry*>::iterator itr;
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
std::string key = (*itr).first;
keys->push_back(key);
}
return (keys);
}
}
/**
* Remove all entries
* NOTE: This method should not be used with applications which cache entries which would lead to unknown results
* This method is for use in testing only
*/
void AbstractNetworkTableEntryStore::clearEntries(){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
namedEntries.clear();
idEntries.clear();
}
}
/**
* clear the id's of all entries
*/
void AbstractNetworkTableEntryStore::clearIds(){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
std::map<std::string, NetworkTableEntry*>::iterator itr;
idEntries.clear();
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
((NetworkTableEntry*)(*itr).second)->ClearId();
}
}
}
void AbstractNetworkTableEntryStore::SetOutgoingReceiver(OutgoingEntryReceiver* receiver){
outgoingReceiver = receiver;
}
void AbstractNetworkTableEntryStore::SetIncomingReceiver(OutgoingEntryReceiver* receiver){
incomingReceiver = receiver;
}
/**
* Stores the given value under the given name and queues it for
* Stores the given value under the given name and queues it for
* transmission to the server.
*
*
* @param name The name under which to store the given value.
* @param type The type of the given value.
* @param value The value to store.
* @throws TableKeyExistsWithDifferentTypeException Thrown if an
* @throws TableKeyExistsWithDifferentTypeException Thrown if an
* entry already exists with the given name and is of a different type.
*/
void AbstractNetworkTableEntryStore::PutOutgoing(std::string& name, NetworkTableEntryType* type, EntryValue value){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
std::map<std::string, NetworkTableEntry*>::iterator index = namedEntries.find(name);
NetworkTableEntry* tableEntry;
if(index == namedEntries.end())//if the name does not exist in the current entries
@@ -137,21 +137,21 @@
if(tableEntry->GetType()->id != type->id){
throw TableKeyExistsWithDifferentTypeException(name, tableEntry->GetType());
}
EntryValue oldValue = tableEntry->GetValue();
if(!type->areEqual(value, oldValue)){
if(updateEntry(tableEntry, (SequenceNumber)(tableEntry->GetSequenceNumber() + 1), value)){
outgoingReceiver->offerOutgoingUpdate(tableEntry);
}
tableEntry->FireListener(listenerManager);
}
}
}
}
void AbstractNetworkTableEntryStore::PutOutgoing(NetworkTableEntry* tableEntry, EntryValue value){
{
{
NTSynchronized sync(LOCK);
NetworkTableEntryType* type = tableEntry->GetType();
EntryValue oldValue = tableEntry->GetValue();
@@ -159,15 +159,15 @@
if(updateEntry(tableEntry, (SequenceNumber)(tableEntry->GetSequenceNumber() + 1), value)){
outgoingReceiver->offerOutgoingUpdate(tableEntry);
}
tableEntry->FireListener(listenerManager);
}
}
}
void AbstractNetworkTableEntryStore::offerIncomingAssignment(NetworkTableEntry* entry){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
std::map<std::string, NetworkTableEntry*>::iterator itr = namedEntries.find(entry->name);
NetworkTableEntry* tableEntry;
if(addEntry(entry)){
@@ -177,7 +177,7 @@
else{
tableEntry = entry;
}
tableEntry->FireListener(listenerManager);//if we didnt have a pointer, then the copy of the version in the list would call this method, however with the pointer we are updating the version in the list
incomingReceiver->offerOutgoingAssignment(tableEntry);
}
@@ -185,9 +185,9 @@
delete entry;
}
}
void AbstractNetworkTableEntryStore::offerIncomingUpdate(NetworkTableEntry* entry, SequenceNumber squenceNumber, EntryValue value){
{
{
NTSynchronized sync(LOCK);
if(updateEntry(entry, squenceNumber, value)){
entry->FireListener(listenerManager);
@@ -195,15 +195,15 @@
}
}
}
/**
* Called to say that a listener should notify the listener manager of all of the entries
* @param listener
* @param table
* @param table
*/
void AbstractNetworkTableEntryStore::notifyEntries(ITable* table, ITableListener* listener){
{
NTSynchronized sync(LOCK);
{
NTSynchronized sync(block_namedEntries);
std::map<std::string, NetworkTableEntry*>::iterator itr;
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{

View File

@@ -20,6 +20,17 @@ NetworkTableEntry::NetworkTableEntry(EntryId _id, std::string& _name, SequenceNu
m_isDirty = false;
}
NetworkTableEntry::NetworkTableEntry(const NetworkTableEntry &other) :
name(other.name),
id(other.id),
sequenceNumber(other.sequenceNumber),
type(other.type),
m_isNew(other.m_isNew),
m_isDirty(other.m_isDirty)
{
value = type->copyValue(other.value);
}
NetworkTableEntry::~NetworkTableEntry(){
type->deleteValue(value);
}

View File

@@ -54,10 +54,15 @@ void WriteManager::stop(){
void WriteManager::offerOutgoingAssignment(NetworkTableEntry* entry) {
{
NTSynchronized sync(transactionsLock);
((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->push(entry);
if(((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->size()>=queueSize){
bool test_queue_overflow=false;
{
NTSynchronized sync(transactionsLock);
((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->push(entry);
test_queue_overflow=(((std::queue<NetworkTableEntry*>*)incomingAssignmentQueue)->size()>=queueSize);
}
if (test_queue_overflow)
{
run();
writeWarning("assignment queue overflowed. decrease the rate at which you create new entries or increase the write buffer size");
}
@@ -67,9 +72,15 @@ void WriteManager::offerOutgoingAssignment(NetworkTableEntry* entry) {
void WriteManager::offerOutgoingUpdate(NetworkTableEntry* entry) {
{
NTSynchronized sync(transactionsLock);
((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->push(entry);
if(((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->size()>=queueSize){
bool test_queue_overflow=false;
{
NTSynchronized sync(transactionsLock);
((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->push(entry);
test_queue_overflow=(((std::queue<NetworkTableEntry*>*)incomingUpdateQueue)->size()>=queueSize);
}
if (test_queue_overflow)
{
run();
writeWarning("update queue overflowed. decrease the rate at which you update entries or increase the write buffer size");
}
@@ -97,10 +108,17 @@ void WriteManager::run() {
entry = ((std::queue<NetworkTableEntry*>*)outgoingAssignmentQueue)->front();
((std::queue<NetworkTableEntry*>*)outgoingAssignmentQueue)->pop();
{
NTSynchronized sync(entryStore.LOCK);
entry->MakeClean();
wrote = true;
receiver.offerOutgoingAssignment(entry);
NetworkTableEntry * entryCopy;
{
NTSynchronized sync(entryStore.LOCK);
entry->MakeClean();
wrote = true;
entryCopy = new NetworkTableEntry(*entry);
}
receiver.offerOutgoingAssignment(entryCopy);
delete entryCopy;
}
}
@@ -108,10 +126,17 @@ void WriteManager::run() {
entry = ((std::queue<NetworkTableEntry*>*)outgoingUpdateQueue)->front();
((std::queue<NetworkTableEntry*>*)outgoingUpdateQueue)->pop();
{
NTSynchronized sync(entryStore.LOCK);
entry->MakeClean();
wrote = true;
receiver.offerOutgoingUpdate(entry);
NetworkTableEntry * entryCopy;
{
NTSynchronized sync(entryStore.LOCK);
entry->MakeClean();
wrote = true;
entryCopy = new NetworkTableEntry(*entry);
}
receiver.offerOutgoingUpdate(entryCopy);
delete entryCopy;
}
}

View File

@@ -17,7 +17,7 @@ ClientNetworkTableEntryStore::~ClientNetworkTableEntryStore(){}
bool ClientNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry){
{
NTSynchronized sync(LOCK);
NTSynchronized sync(block_namedEntries);
NetworkTableEntry* entry = (NetworkTableEntry*)namedEntries[newEntry->name];
if(entry!=NULL){
@@ -28,7 +28,7 @@ bool ClientNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry){
idEntries[newEntry->GetId()] = entry;
}
}
entry->ForcePut(newEntry->GetSequenceNumber(), newEntry->GetType(), newEntry->GetValue());
}
else{
@@ -41,7 +41,7 @@ bool ClientNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry){
}
bool ClientNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, SequenceNumber sequenceNumber, EntryValue value) {
{
{
NTSynchronized sync(LOCK);
entry->ForcePut(sequenceNumber, value);
if(entry->GetId()==NetworkTableEntry::UNKNOWN_ID){
@@ -57,7 +57,7 @@ bool ClientNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, Sequenc
* @throws IOException
*/
void ClientNetworkTableEntryStore::sendUnknownEntries(NetworkTableConnection& connection) {
{
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator itr;
for(itr = namedEntries.begin(); itr != namedEntries.end(); itr++)

View File

@@ -1,6 +1,6 @@
/**
* An abstraction for the NetworkTable protocol
*
*
* @author mwills
*
*/
@@ -19,6 +19,7 @@ NetworkTableConnection::~NetworkTableConnection(){
void NetworkTableConnection::SetIOStream(IOStream* stream)
{
isValid=(stream!=NULL);
ioStream->SetIOStream(stream); //just passing through
}
@@ -102,7 +103,7 @@ void NetworkTableConnection::sendEntryUpdate(NetworkTableEntry& entry) {
}
void NetworkTableConnection::read(ConnectionAdapter& adapter) {
int messageType = ioStream->readByte();
int messageType = (isValid)?ioStream->readByte():(int)KEEP_ALIVE;
switch (messageType) {
case KEEP_ALIVE:
adapter.keepAlive();

View File

@@ -17,10 +17,10 @@ NetworkTableServer::NetworkTableServer(IOStreamProvider& _streamProvider, Networ
connectionList(&incomingStreamMonitor),
writeManager(connectionList, threadManager, GetEntryStore(), ULONG_MAX),
continuingReceiver(writeManager){
GetEntryStore().SetIncomingReceiver(&continuingReceiver);
GetEntryStore().SetOutgoingReceiver(&continuingReceiver);
incomingStreamMonitor.start();
writeManager.start();
}
@@ -35,6 +35,9 @@ NetworkTableServer::~NetworkTableServer(){
void NetworkTableServer::Close(){
try{
//Note: streamProvider must come before the incomingStreamMonitor so the that task can complete first for the thread to close
// [9/1/2013 Terminator]
streamProvider.close();
incomingStreamMonitor.stop();
writeManager.stop();
connectionList.closeAll();

View File

@@ -36,6 +36,9 @@ void ServerConnectionAdapter::badMessage(BadMessageException& e) {
gotoState(new ServerConnectionState_Error(e));
adapterListener.close(*this, true);
m_IsAdapterListenerClosed=true;
if (readThread) {
readThread->stop();
}
}
void ServerConnectionAdapter::ioException(IOException& e) {
@@ -46,6 +49,9 @@ void ServerConnectionAdapter::ioException(IOException& e) {
else
gotoState(new ServerConnectionState_Error(e));
adapterListener.close(*this, false);
if (readThread) {
readThread->stop();
}
m_IsAdapterListenerClosed=true;
}

View File

@@ -7,6 +7,7 @@
#include "networktables2/server/ServerIncomingStreamMonitor.h"
#include "networktables2/stream/IOStream.h"
#include "networktables2/util/System.h"
ServerIncomingStreamMonitor::ServerIncomingStreamMonitor(IOStreamProvider& _streamProvider, ServerNetworkTableEntryStore& _entryStore,
ServerIncomingConnectionListener& _incomingListener, ServerAdapterManager& _adapterListener, NetworkTableEntryTypeManager& _typeManager,
@@ -69,6 +70,7 @@ void ServerIncomingStreamMonitor::run()
ServerConnectionAdapter* connectionAdapter = new ServerConnectionAdapter(newStream, entryStore, entryStore, adapterListener, typeManager, threadManager);
incomingListener.OnNewConnection(*connectionAdapter);
}
sleep_ms(100); //avoid busy wait
}
}
catch (IOException& e)

View File

@@ -18,9 +18,9 @@ ServerNetworkTableEntryStore::~ServerNetworkTableEntryStore()
bool ServerNetworkTableEntryStore::addEntry(NetworkTableEntry* newEntry)
{
NTSynchronized sync(LOCK);
NTSynchronized sync(block_namedEntries);
NetworkTableEntry* entry = namedEntries[newEntry->name];
if (entry == NULL)
{
newEntry->SetId(nextId++);
@@ -44,13 +44,19 @@ bool ServerNetworkTableEntryStore::updateEntry(NetworkTableEntry* entry, Sequenc
*/
void ServerNetworkTableEntryStore::sendServerHello(NetworkTableConnection& connection)
{
NTSynchronized sync(LOCK);
std::map<std::string, NetworkTableEntry*>::iterator itr;
for (itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
std::vector<NetworkTableEntry *> entry_list;
{
NetworkTableEntry* entry = itr->second;
connection.sendEntryAssignment(*entry);
NTSynchronized sync(block_namedEntries);
std::map<std::string, NetworkTableEntry*>::iterator itr;
for (itr = namedEntries.begin(); itr != namedEntries.end(); itr++)
{
NetworkTableEntry* entry = itr->second;
entry_list.push_back(entry);
}
}
for (size_t i=0;i<entry_list.size();i++)
connection.sendEntryAssignment(*(entry_list[i]));
connection.sendServerHelloComplete();
connection.flush();
}

View File

@@ -5,6 +5,10 @@
/*---------------------------------------------------------------------------*/
#pragma once
/** @file
* Contains global utility functions
*/
#include <stdint.h>
#include <string>

View File

@@ -7,6 +7,7 @@
#include "interfaces/Accelerometer.h"
#include "I2C.h"
#include "LiveWindow/LiveWindowSendable.h"
/**
* ADXL345 Accelerometer on I2C.
@@ -14,7 +15,7 @@
* This class allows access to a Analog Devices ADXL345 3-axis accelerometer on an I2C bus.
* This class assumes the default (not alternate) sensor address of 0x1D (7-bit address).
*/
class ADXL345_I2C : public Accelerometer, public I2C
class ADXL345_I2C : public Accelerometer, public I2C, public LiveWindowSendable
{
protected:
static const uint8_t kAddress = 0x1D;
@@ -48,6 +49,15 @@ public:
virtual double GetAcceleration(Axes axis);
virtual AllAxes GetAccelerations();
virtual std::string GetSmartDashboardType();
virtual void InitTable(ITable *subtable);
virtual void UpdateTable();
virtual ITable* GetTable();
virtual void StartLiveWindowMode() {}
virtual void StopLiveWindowMode() {}
protected:
//I2C* m_i2c;
private:
ITable *m_table;
};

View File

@@ -8,6 +8,7 @@
#include "interfaces/Accelerometer.h"
#include "SensorBase.h"
#include "SPI.h"
#include "LiveWindow/LiveWindowSendable.h"
class DigitalInput;
class DigitalOutput;
@@ -18,7 +19,7 @@ class DigitalOutput;
* This class allows access to an Analog Devices ADXL345 3-axis accelerometer via SPI.
* This class assumes the sensor is wired in 4-wire SPI mode.
*/
class ADXL345_SPI : public Accelerometer, public SensorBase
class ADXL345_SPI : public Accelerometer, public SensorBase, public LiveWindowSendable
{
protected:
static const uint8_t kPowerCtlRegister = 0x2D;
@@ -52,9 +53,18 @@ public:
virtual double GetAcceleration(Axes axis);
virtual AllAxes GetAccelerations();
virtual std::string GetSmartDashboardType();
virtual void InitTable(ITable *subtable);
virtual void UpdateTable();
virtual ITable* GetTable();
virtual void StartLiveWindowMode() {}
virtual void StopLiveWindowMode() {}
protected:
void Init(Range range);
SPI* m_spi;
SPI::Port m_port;
private:
ITable *m_table;
};

View File

@@ -6,54 +6,76 @@
/*----------------------------------------------------------------------------*/
#pragma once
#include "USBCamera.h"
#include "ErrorBase.h"
#include "nivision.h"
#include "NIIMAQdx.h"
#include <vector>
#include <thread>
#include <mutex>
#include <thread>
#include <memory>
#include <condition_variable>
#include <tuple>
#include <vector>
/**
* Class that runs a TCP server that serves an M-JPEG stream to the dashboard.
*/
class CameraServer : public ErrorBase {
static constexpr uint16_t kPort = 1180;
static constexpr uint8_t kMagicNumber[] = { 0x01, 0x00, 0x00, 0x00 };
static constexpr uint32_t kSize640x480 = 0;
static constexpr uint32_t kSize320x240 = 1;
static constexpr uint32_t kSize160x120 = 2;
static constexpr int32_t kHardwareCompression = -1;
static constexpr char const *kDefaultCameraName = "cam0";
private:
static constexpr uint16_t kPort = 1180;
static constexpr uint8_t kMagicNumber[] = { 0x01, 0x00, 0x00, 0x00 };
static constexpr uint32_t kSize640x480 = 0;
static constexpr uint32_t kSize320x240 = 1;
static constexpr uint32_t kSize160x120 = 2;
static constexpr int32_t kHardwareCompression = -1;
static constexpr uint32_t kMaxImageSize = 200000;
public:
static CameraServer *GetInstance();
protected:
CameraServer();
std::shared_ptr<USBCamera> m_camera;
std::thread m_serverThread;
std::thread m_captureThread;
std::recursive_mutex m_imageMutex;
std::condition_variable_any m_newImageVariable;
std::vector<uint8_t*> m_dataPool;
unsigned int m_quality;
bool m_autoCaptureStarted;
bool m_hwClient;
std::tuple<uint8_t*, unsigned int, unsigned int, bool> m_imageData;
void SetImage(Image const *image);
void Serve();
void AutoCapture();
void SetImageData(uint8_t* data, unsigned int size, unsigned int start = 0, bool imaqData = false);
void FreeImageData(std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData);
void StartAutomaticCapture(char const *cameraName = kDefaultCameraName);
struct Request {
uint32_t fps;
int32_t compression;
uint32_t size;
};
void SetQuality(unsigned int quality);
unsigned int GetQuality() const;
static CameraServer* s_instance;
public:
static CameraServer* GetInstance();
void SetImage(Image const *image);
protected:
CameraServer();
void serve();
void StartAutomaticCapture(char const *cameraName = USBCamera::kDefaultCameraName);
std::thread m_serverThread;
std::recursive_mutex m_imageMutex;
std::condition_variable_any m_newImageReady;
std::vector<uint8_t> m_imageData;
unsigned int m_quality;
bool m_autoCaptureStarted;
/**
* Start automatically capturing images to send to the dashboard.
*
* You should call this method to just see a camera feed on the
* dashboard without doing any vision processing on the roboRIO.
* {@link #SetImage} should not be called after this is called.
*
* @param camera The camera interface (eg. USBCamera)
*/
void StartAutomaticCapture(std::shared_ptr<USBCamera> camera);
struct Request {
uint32_t fps;
int32_t compression;
uint32_t size;
};
bool IsAutoCaptureStarted();
static CameraServer *s_instance;
void SetQuality(unsigned int quality);
unsigned int GetQuality();
void SetSize(unsigned int size);
};

View File

@@ -32,6 +32,14 @@ public:
void SetClosedLoopControl(bool on);
bool GetClosedLoopControl();
bool GetCompressorCurrentTooHighFault();
bool GetCompressorCurrentTooHighStickyFault();
bool GetCompressorShortedStickyFault();
bool GetCompressorShortedFault();
bool GetCompressorNotConnectedStickyFault();
bool GetCompressorNotConnectedFault();
void ClearAllPCMStickyFaults();
void UpdateTable();
void StartLiveWindowMode();
void StopLiveWindowMode();

View File

@@ -67,7 +67,7 @@ public:
bool GetDirection();
void SetSamplesToAverage(int samplesToAverage);
int GetSamplesToAverage();
uint32_t GetIndex()
uint32_t GetFPGAIndex()
{
return m_index;
}

View File

@@ -31,6 +31,8 @@ public:
virtual ~DoubleSolenoid();
virtual void Set(Value value);
virtual Value Get();
bool IsFwdSolenoidBlackListed();
bool IsRevSolenoidBlackListed();
void ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew);
void UpdateTable();

View File

@@ -29,6 +29,7 @@ class DigitalSource;
class Encoder : public SensorBase, public CounterBase, public PIDSource, public LiveWindowSendable
{
public:
enum IndexingType { kResetWhileHigh, kResetWhileLow, kResetOnFallingEdge, kResetOnRisingEdge };
Encoder(uint32_t aChannel, uint32_t bChannel, bool reverseDirection = false,
EncodingType encodingType = k4X);
@@ -41,6 +42,7 @@ public:
// CounterBase interface
int32_t Get();
int32_t GetRaw();
int32_t GetEncodingScale();
void Reset();
double GetPeriod();
void SetMaxPeriod(double maxPeriod);
@@ -56,6 +58,10 @@ public:
void SetPIDSourceParameter(PIDSourceParameter pidSource);
double PIDGet();
void SetIndexSource(uint32_t channel, IndexingType type = kResetOnRisingEdge);
void SetIndexSource(DigitalSource *source, IndexingType type = kResetOnRisingEdge);
void SetIndexSource(DigitalSource &source, IndexingType type = kResetOnRisingEdge);
void UpdateTable();
void StartLiveWindowMode();
void StopLiveWindowMode();
@@ -63,6 +69,11 @@ public:
void InitTable(ITable *subTable);
ITable * GetTable();
int32_t GetFPGAIndex()
{
return m_index;
}
private:
void InitEncoder(bool _reverseDirection, EncodingType encodingType);
double DecodingScaleFactor();
@@ -72,9 +83,11 @@ private:
bool m_allocatedASource; // was the A source allocated locally?
bool m_allocatedBSource; // was the B source allocated locally?
void* m_encoder;
int32_t m_index; // The encoder's FPGA index.
double m_distancePerPulse; // distance of travel for each encoder tick
Counter *m_counter; // Counter object for 1x and 2x encoding
EncodingType m_encodingType; // Encoding type
int32_t m_encodingScale; // 1x, 2x, or 4x, per the encodingType
PIDSourceParameter m_pidSource; // Encoder parameter that sources a PID controller
ITable *m_table;

View File

@@ -13,16 +13,16 @@
* Class implements the PWM generation in the FPGA.
*
* The values supplied as arguments for PWM outputs range from -1.0 to 1.0. They are mapped
* to the hardware dependent values, in this case 0-255 for the FPGA.
* to the hardware dependent values, in this case 0-2000 for the FPGA.
* Changes are immediately sent to the FPGA, and the update occurs at the next
* FPGA cycle. There is no delay.
*
* As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-255 values as follows:
* - 255 = full "forward"
* - 254 to 129 = linear scaling from "full forward" to "center"
* - 128 = center value
* - 127 to 2 = linear scaling from "center" to "full reverse"
* - 1 = full "reverse"
* As of revision 0.1.10 of the FPGA, the FPGA interprets the 0-2000 values as follows:
* - 2000 = maximum pulse width
* - 1999 to 1001 = linear scaling from "full forward" to "center"
* - 1000 = center value
* - 999 to 2 = linear scaling from "center" to "full reverse"
* - 1 = minimum pulse width (currently .5ms)
* - 0 = disabled (i.e. PWM output is held low)
*/
class PWM : public SensorBase, public ITableListener, public LiveWindowSendable

View File

@@ -9,12 +9,14 @@
#define __WPILIB_POWER_DISTRIBUTION_PANEL_H__
#include "SensorBase.h"
#include "LiveWindow/LiveWindowSendable.h"
/**
* Class for getting voltage, current, and temperature from the CAN PDP
* Class for getting voltage, current, temperature, power and energy from the CAN PDP.
* The PDP must be at CAN Address 0.
* @author Thomas Clark
*/
class PowerDistributionPanel : public SensorBase {
class PowerDistributionPanel : public SensorBase, public LiveWindowSendable {
public:
PowerDistributionPanel();
@@ -26,6 +28,16 @@ class PowerDistributionPanel : public SensorBase {
double GetTotalEnergy();
void ResetTotalEnergy();
void ClearStickyFaults();
void UpdateTable();
void StartLiveWindowMode();
void StopLiveWindowMode();
std::string GetSmartDashboardType();
void InitTable(ITable *subTable);
ITable * GetTable();
private:
ITable *m_table;
};
#endif /* __WPILIB_POWER_DISTRIBUTION_PANEL_H__ */

View File

@@ -67,6 +67,7 @@ public:
void SetInvertedMotor(MotorType motor, bool isInverted);
void SetSensitivity(float sensitivity);
void SetMaxOutput(double maxOutput);
void SetCANJaguarSyncGroup(uint8_t syncGroup);
void SetExpiration(float timeout);
float GetExpiration();
@@ -93,6 +94,7 @@ protected:
SpeedController *m_rearLeftMotor;
SpeedController *m_rearRightMotor;
MotorSafetyHelper *m_safetyHelper;
uint8_t m_syncGroup;
private:
int32_t GetNumMotors()

View File

@@ -23,6 +23,7 @@ public:
virtual ~Solenoid();
virtual void Set(bool on);
virtual bool Get();
bool IsBlackListed();
void ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew);
void UpdateTable();

View File

@@ -20,6 +20,10 @@ public:
virtual ~SolenoidBase();
uint8_t GetAll();
uint8_t GetPCMSolenoidBlackList();
bool GetPCMSolenoidVoltageStickyFault();
bool GetPCMSolenoidVoltageFault();
void ClearAllPCMStickyFaults();
protected:
explicit SolenoidBase(uint8_t pcmID);
void Set(uint8_t value, uint8_t mask);

View File

@@ -0,0 +1,111 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "ErrorBase.h"
#include "nivision.h"
#include "NIIMAQdx.h"
#include <mutex>
#include <string>
typedef enum whiteBalance_enum {
kFixedIndoor = 3000,
kFixedOutdoor1 = 4000,
kFixedOutdoor2 = 5000,
kFixedFluorescent1 = 5100,
kFixedFlourescent2 = 5200
} whiteBalance;
class USBCamera : public ErrorBase {
private:
static constexpr char const *ATTR_WB_MODE = "CameraAttributes::WhiteBalance::Mode";
static constexpr char const *ATTR_WB_VALUE = "CameraAttributes::WhiteBalance::Value";
static constexpr char const *ATTR_EX_MODE = "CameraAttributes::Exposure::Mode";
static constexpr char const *ATTR_EX_VALUE = "CameraAttributes::Exposure::Value";
static constexpr char const *ATTR_BR_MODE = "CameraAttributes::Brightness::Mode";
static constexpr char const *ATTR_BR_VALUE = "CameraAttributes::Brightness::Value";
protected:
IMAQdxSession m_id;
std::string m_name;
bool m_useJpeg;
bool m_active;
bool m_open;
std::recursive_mutex m_mutex;
unsigned int m_width;
unsigned int m_height;
double m_fps;
std::string m_whiteBalance;
unsigned int m_whiteBalanceValue;
bool m_whiteBalanceValuePresent;
std::string m_exposure;
unsigned int m_exposureValue;
bool m_exposureValuePresent;
unsigned int m_brightness;
bool m_needSettingsUpdate;
unsigned int GetJpegSize(void* buffer, unsigned int buffSize);
public:
static constexpr char const *kDefaultCameraName = "cam0";
USBCamera(std::string name, bool useJpeg);
void OpenCamera();
void CloseCamera();
void StartCapture();
void StopCapture();
void SetFPS(double fps);
void SetSize(unsigned int width, unsigned int height);
void UpdateSettings();
/**
* Set the brightness, as a percentage (0-100).
*/
void SetBrightness(unsigned int brightness);
/**
* Get the brightness, as a percentage (0-100).
*/
unsigned int GetBrightness();
/**
* Set the white balance to auto
*/
void SetWhiteBalanceAuto();
/**
* Set the white balance to hold current
*/
void SetWhiteBalanceHoldCurrent();
/**
* Set the white balance to manual, with specified color temperature
*/
void SetWhiteBalanceManual(unsigned int wbValue);
/**
* Set the exposure to auto exposure
*/
void SetExposureAuto();
/**
* Set the exposure to hold current
*/
void SetExposureHoldCurrent();
/**
* Set the exposure to manual, with a given percentage (0-100)
*/
void SetExposureManual(unsigned int expValue);
void GetImage(Image* image);
unsigned int GetImageData(void* buffer, unsigned int bufferSize);
};

View File

@@ -0,0 +1,127 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <thread>
#include <string>
#include <mutex>
#include "ErrorBase.h"
#include "Vision/ColorImage.h"
#include "Vision/HSLImage.h"
#include "nivision.h"
/**
* Axis M1011 network camera
*/
class AxisCamera: public ErrorBase
{
public:
enum WhiteBalance
{
kWhiteBalance_Automatic,
kWhiteBalance_Hold,
kWhiteBalance_FixedOutdoor1,
kWhiteBalance_FixedOutdoor2,
kWhiteBalance_FixedIndoor,
kWhiteBalance_FixedFluorescent1,
kWhiteBalance_FixedFluorescent2
};
enum ExposureControl
{
kExposureControl_Automatic,
kExposureControl_Hold,
kExposureControl_FlickerFree50Hz,
kExposureControl_FlickerFree60Hz
};
enum Resolution
{
kResolution_640x480,
kResolution_480x360,
kResolution_320x240,
kResolution_240x180,
kResolution_176x144,
kResolution_160x120,
};
enum Rotation
{
kRotation_0, kRotation_180
};
explicit AxisCamera(std::string const& cameraHost);
virtual ~AxisCamera();
bool IsFreshImage() const;
int GetImage(Image *image);
int GetImage(ColorImage *image);
HSLImage *GetImage();
int CopyJPEG(char **destImage, unsigned int &destImageSize, unsigned int &destImageBufferSize);
void WriteBrightness(int brightness);
int GetBrightness();
void WriteWhiteBalance(WhiteBalance whiteBalance);
WhiteBalance GetWhiteBalance();
void WriteColorLevel(int colorLevel);
int GetColorLevel();
void WriteExposureControl(ExposureControl exposureControl);
ExposureControl GetExposureControl();
void WriteExposurePriority(int exposurePriority);
int GetExposurePriority();
void WriteMaxFPS(int maxFPS);
int GetMaxFPS();
void WriteResolution(Resolution resolution);
Resolution GetResolution();
void WriteCompression(int compression);
int GetCompression();
void WriteRotation(Rotation rotation);
Rotation GetRotation();
private:
std::thread m_captureThread;
std::string m_cameraHost;
int m_cameraSocket;
std::mutex m_captureMutex;
std::mutex m_imageDataMutex;
std::vector<uint8_t> m_imageData;
bool m_freshImage;
int m_brightness;
WhiteBalance m_whiteBalance;
int m_colorLevel;
ExposureControl m_exposureControl;
int m_exposurePriority;
int m_maxFPS;
Resolution m_resolution;
int m_compression;
Rotation m_rotation;
bool m_parametersDirty;
bool m_streamDirty;
std::mutex m_parametersMutex;
bool m_done;
void Capture();
void ReadImagesFromCamera();
bool WriteParameters();
int CreateCameraSocket(std::string const& requestString, bool setError);
DISALLOW_COPY_AND_ASSIGN(AxisCamera);
};

View File

@@ -0,0 +1,57 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
/* Constants */
#define LOG_DEBUG __FILE__,__FUNCTION__,__LINE__,DEBUG_TYPE
#define LOG_INFO __FILE__,__FUNCTION__,__LINE__,INFO_TYPE
#define LOG_ERROR __FILE__,__FUNCTION__,__LINE__,ERROR_TYPE
#define LOG_CRITICAL __FILE__,__FUNCTION__,__LINE__,CRITICAL_TYPE
#define LOG_FATAL __FILE__,__FUNCTION__,__LINE__,FATAL_TYPE
#define LOG_DEBUG __FILE__,__FUNCTION__,__LINE__,DEBUG_TYPE
/* Enumerated Types */
/** debug levels */
enum dprint_type {DEBUG_TYPE, INFO_TYPE, ERROR_TYPE, CRITICAL_TYPE, FATAL_TYPE};
/** debug output setting */
typedef enum DebugOutputType_enum {
DEBUG_OFF, DEBUG_MOSTLY_OFF, DEBUG_SCREEN_ONLY, DEBUG_FILE_ONLY, DEBUG_SCREEN_AND_FILE
}DebugOutputType;
/* Enumerated Types */
/* Utility functions */
/* debug */
void SetDebugFlag ( DebugOutputType flag );
void dprintf ( const char * tempString, ... ); /* Variable argument list */
/* set FRC ranges for drive */
double RangeToNormalized(double pixel, int range);
/* change normalized value to any range - used for servo */
float NormalizeToRange(float normalizedValue, float minRange, float maxRange);
float NormalizeToRange(float normalizedValue);
/* system utilities */
void ShowActivity (char *fmt, ...);
double ElapsedTime (double startTime);
/* servo panning utilities */
class Servo;
double SinPosition (double *period, double sinStart);
void panInit();
void panInit(double period);
void panForTarget(Servo *panServo);
void panForTarget(Servo *panServo, double sinStart);
/* config file read utilities */
int processFile(char *inputFile, char *outputString, int lineNumber);
int emptyString(char *string);
void stripString(char *string);

View File

@@ -0,0 +1,39 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "MonoImage.h"
/**
* Included for ParticleAnalysisReport definition
* TODO: Eliminate this dependency!
*/
#include "Vision/VisionAPI.h"
#include <vector>
#include <algorithm>
class BinaryImage : public MonoImage
{
public:
BinaryImage();
virtual ~BinaryImage();
int GetNumberParticles();
ParticleAnalysisReport GetParticleAnalysisReport(int particleNumber);
void GetParticleAnalysisReport(int particleNumber, ParticleAnalysisReport *par);
std::vector<ParticleAnalysisReport>* GetOrderedParticleAnalysisReports();
BinaryImage *RemoveSmallObjects(bool connectivity8, int erosions);
BinaryImage *RemoveLargeObjects(bool connectivity8, int erosions);
BinaryImage *ConvexHull(bool connectivity8);
BinaryImage *ParticleFilter(ParticleFilterCriteria2 *criteria, int criteriaCount);
virtual void Write(const char *fileName);
private:
bool ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, int *result);
bool ParticleMeasurement(int particleNumber, MeasurementType whatToMeasure, double *result);
static double NormalizeFromRange(double position, int range);
static bool CompareParticleSizes(ParticleAnalysisReport particle1, ParticleAnalysisReport particle2);
};

View File

@@ -0,0 +1,65 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "ImageBase.h"
#include "BinaryImage.h"
#include "Threshold.h"
class ColorImage : public ImageBase
{
public:
ColorImage(ImageType type);
virtual ~ColorImage();
BinaryImage *ThresholdRGB(int redLow, int redHigh, int greenLow, int greenHigh, int blueLow, int blueHigh);
BinaryImage *ThresholdHSL(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int luminenceLow, int luminenceHigh);
BinaryImage *ThresholdHSV(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int valueHigh, int valueLow);
BinaryImage *ThresholdHSI(int hueLow, int hueHigh, int saturationLow, int saturationHigh, int intensityLow, int intensityHigh);
BinaryImage *ThresholdRGB(Threshold &threshold);
BinaryImage *ThresholdHSL(Threshold &threshold);
BinaryImage *ThresholdHSV(Threshold &threshold);
BinaryImage *ThresholdHSI(Threshold &threshold);
MonoImage *GetRedPlane();
MonoImage *GetGreenPlane();
MonoImage *GetBluePlane();
MonoImage *GetHSLHuePlane();
MonoImage *GetHSVHuePlane();
MonoImage *GetHSIHuePlane();
MonoImage *GetHSLSaturationPlane();
MonoImage *GetHSVSaturationPlane();
MonoImage *GetHSISaturationPlane();
MonoImage *GetLuminancePlane();
MonoImage *GetValuePlane();
MonoImage *GetIntensityPlane();
void ReplaceRedPlane(MonoImage *plane);
void ReplaceGreenPlane(MonoImage *plane);
void ReplaceBluePlane(MonoImage *plane);
void ReplaceHSLHuePlane(MonoImage *plane);
void ReplaceHSVHuePlane(MonoImage *plane);
void ReplaceHSIHuePlane(MonoImage *plane);
void ReplaceHSLSaturationPlane(MonoImage *plane);
void ReplaceHSVSaturationPlane(MonoImage *plane);
void ReplaceHSISaturationPlane(MonoImage *plane);
void ReplaceLuminancePlane(MonoImage *plane);
void ReplaceValuePlane(MonoImage *plane);
void ReplaceIntensityPlane(MonoImage *plane);
void ColorEqualize();
void LuminanceEqualize();
private:
BinaryImage *ComputeThreshold(ColorMode colorMode, int low1, int high1, int low2, int high2, int low3, int high3);
void Equalize(bool allPlanes);
MonoImage * ExtractColorPlane(ColorMode mode, int planeNumber);
MonoImage * ExtractFirstColorPlane(ColorMode mode);
MonoImage * ExtractSecondColorPlane(ColorMode mode);
MonoImage * ExtractThirdColorPlane(ColorMode mode);
void ReplacePlane(ColorMode mode, MonoImage *plane, int planeNumber);
void ReplaceFirstColorPlane(ColorMode mode, MonoImage *plane);
void ReplaceSecondColorPlane(ColorMode mode, MonoImage *plane);
void ReplaceThirdColorPlane(ColorMode mode, MonoImage *plane);
};

View File

@@ -0,0 +1,30 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
/* Error Codes */
#define ERR_VISION_GENERAL_ERROR 166000 //
#define ERR_COLOR_NOT_FOUND 166100 // TrackAPI.cpp
#define ERR_PARTICLE_TOO_SMALL 166101 // TrackAPI.cpp
#define ERR_CAMERA_FAILURE 166200 // AxisCamera.cpp
#define ERR_CAMERA_SOCKET_CREATE_FAILED 166201 // AxisCamera.cpp
#define ERR_CAMERA_CONNECT_FAILED 166202 // AxisCamera.cpp
#define ERR_CAMERA_STALE_IMAGE 166203 // AxisCamera.cpp
#define ERR_CAMERA_NOT_INITIALIZED 166204 // AxisCamera.cpp
#define ERR_CAMERA_NO_BUFFER_AVAILABLE 166205 // AxisCamera.cpp
#define ERR_CAMERA_HEADER_ERROR 166206 // AxisCamera.cpp
#define ERR_CAMERA_BLOCKING_TIMEOUT 166207 // AxisCamera.cpp
#define ERR_CAMERA_AUTHORIZATION_FAILED 166208 // AxisCamera.cpp
#define ERR_CAMERA_TASK_SPAWN_FAILED 166209 // AxisCamera.cpp
#define ERR_CAMERA_TASK_INPUT_OUT_OF_RANGE 166210 // AxisCamera.cpp
#define ERR_CAMERA_COMMAND_FAILURE 166211 // AxisCamera.cpp
/* error handling functions */
int GetLastVisionError();
const char* GetVisionErrorText(int errorCode);

View File

@@ -0,0 +1,22 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "ColorImage.h"
/**
* A color image represented in HSL color space at 3 bytes per pixel.
*/
class HSLImage : public ColorImage
{
public:
HSLImage();
HSLImage(const char *fileName);
virtual ~HSLImage();
};

View File

@@ -0,0 +1,27 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include <stdio.h>
#include "nivision.h"
#include "ErrorBase.h"
#define DEFAULT_BORDER_SIZE 3
class ImageBase : public ErrorBase
{
public:
ImageBase(ImageType type);
virtual ~ImageBase();
virtual void Write(const char *fileName);
int GetHeight();
int GetWidth();
Image *GetImaqImage();
protected:
Image *m_imaqImage;
};

View File

@@ -0,0 +1,25 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "ImageBase.h"
#include <vector>
class MonoImage : public ImageBase
{
public:
MonoImage();
virtual ~MonoImage();
std::vector<EllipseMatch> *DetectEllipses(EllipseDescriptor *ellipseDescriptor,
CurveOptions *curveOptions,
ShapeDetectionOptions *shapeDetectionOptions,
ROI *roi);
std::vector<EllipseMatch> * DetectEllipses(EllipseDescriptor *ellipseDescriptor);
};

View File

@@ -0,0 +1,21 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "ColorImage.h"
/**
* A color image represented in RGB color space at 3 bytes per pixel.
*/
class RGBImage : public ColorImage
{
public:
RGBImage();
RGBImage(const char *fileName);
virtual ~RGBImage();
};

View File

@@ -0,0 +1,28 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
/**
* Color threshold values.
* This object represnts the threshold values for any type of color object
* that is used in a threshhold operation. It simplifies passing values
* around in a program for color detection.
*/
class Threshold
{
public:
int plane1Low;
int plane1High;
int plane2Low;
int plane2High;
int plane3Low;
int plane3High;
Threshold(int plane1Low, int plane1High,
int plane2Low, int plane2High,
int plane3Low, int plane3High);
};

View File

@@ -0,0 +1,148 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#pragma once
#include "nivision.h"
/* Constants */
#define DEFAULT_BORDER_SIZE 3 //VisionAPI.frcCreateImage
#define DEFAULT_SATURATION_THRESHOLD 40 //TrackAPI.FindColor
/* Forward Declare Data Structures */
typedef struct FindEdgeOptions_struct FindEdgeOptions;
typedef struct CircularEdgeReport_struct CircularEdgeReport;
/* Data Structures */
/** frcParticleAnalysis returns this structure */
typedef struct ParticleAnalysisReport_struct {
int imageHeight;
int imageWidth;
double imageTimestamp;
int particleIndex; // the particle index analyzed
/* X-coordinate of the point representing the average position of the
* total particle mass, assuming every point in the particle has a constant density */
int center_mass_x; // MeasurementType: IMAQ_MT_CENTER_OF_MASS_X
/* Y-coordinate of the point representing the average position of the
* total particle mass, assuming every point in the particle has a constant density */
int center_mass_y; // MeasurementType: IMAQ_MT_CENTER_OF_MASS_Y
double center_mass_x_normalized; //Center of mass x value normalized to -1.0 to +1.0 range
double center_mass_y_normalized; //Center of mass y value normalized to -1.0 to +1.0 range
/* Area of the particle */
double particleArea; // MeasurementType: IMAQ_MT_AREA
/* Bounding Rectangle */
Rect boundingRect; // left/top/width/height
/* Percentage of the particle Area covering the Image Area. */
double particleToImagePercent; // MeasurementType: IMAQ_MT_AREA_BY_IMAGE_AREA
/* Percentage of the particle Area in relation to its Particle and Holes Area */
double particleQuality; // MeasurementType: IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA
} ParticleAnalysisReport;
/** Tracking functions return this structure */
typedef struct ColorReport_struct {
int numberParticlesFound; // Number of particles found for this color
int largestParticleNumber; // The particle index of the largest particle
/* Color information */
float particleHueMax; // HistogramReport: hue max
float particleHueMin; // HistogramReport: hue max
float particleHueMean; // HistogramReport: hue mean
float particleSatMax; // HistogramReport: saturation max
float particleSatMin; // HistogramReport: saturation max
float particleSatMean; // HistogramReport: saturation mean
float particleLumMax; // HistogramReport: luminance max
float particleLumMin; // HistogramReport: luminance max
float particleLumMean; // HistogramReport: luminance mean
} ColorReport;
/* Image Management functions */
/* Create: calls imaqCreateImage. Border size is set to some default value */
Image* frcCreateImage( ImageType type );
/* Dispose: calls imaqDispose */
int frcDispose( void* object );
int frcDispose( const char* filename, ... ) ;
/* Copy: calls imaqDuplicateImage */
int frcCopyImage( Image* dest, const Image* source );
/* Image Extraction: Crop: calls imaqScale */
int frcCrop( Image* dest, const Image* source, Rect rect );
/* Image Extraction: Scale: calls imaqScale. Scales entire image */
int frcScale(Image* dest, const Image* source, int xScale, int yScale, ScalingMode scaleMode );
/* Read Image : calls imaqReadFile */
int frcReadImage( Image* image, const char* fileName );
/* Write Image : calls imaqWriteFile */
int frcWriteImage( const Image* image, const char* fileName);
/* Measure Intensity functions */
/* Histogram: calls imaqHistogram */
HistogramReport* frcHistogram( const Image* image, int numClasses, float min, float max, Rect rect );
/* Color Histogram: calls imaqColorHistogram2 */
ColorHistogramReport* frcColorHistogram(const Image* image, int numClasses, ColorMode mode, Image* mask);
/* Get Pixel Value: calls imaqGetPixel */
int frcGetPixelValue( const Image* image, Point pixel, PixelValue* value );
/* Particle Analysis functions */
/* Particle Filter: calls imaqParticleFilter3 */
int frcParticleFilter(Image* dest, Image* source, const ParticleFilterCriteria2* criteria,
int criteriaCount, const ParticleFilterOptions* options, Rect rect, int* numParticles);
int frcParticleFilter(Image* dest, Image* source, const ParticleFilterCriteria2* criteria,
int criteriaCount, const ParticleFilterOptions* options, int* numParticles);
/* Morphology: calls imaqMorphology */
int frcMorphology(Image* dest, Image* source, MorphologyMethod method);
int frcMorphology(Image* dest, Image* source, MorphologyMethod method, const StructuringElement* structuringElement);
/* Reject Border: calls imaqRejectBorder */
int frcRejectBorder(Image* dest, Image* source);
int frcRejectBorder(Image* dest, Image* source, int connectivity8);
/* Count Particles: calls imaqCountParticles */
int frcCountParticles(Image* image, int* numParticles);
/* Particle Analysis Report: calls imaqMeasureParticle */
int frcParticleAnalysis(Image* image, int particleNumber, ParticleAnalysisReport* par);
/* Image Enhancement functions */
/* Equalize: calls imaqEqualize */
int frcEqualize(Image* dest, const Image* source, float min, float max);
int frcEqualize(Image* dest, const Image* source, float min, float max, const Image* mask);
/* Color Equalize: calls imaqColorEqualize */
int frcColorEqualize(Image* dest, const Image* source);
int frcColorEqualize(Image* dest, const Image* source, int colorEqualization);
/* Image Thresholding & Conversion functions */
/* Smart Threshold: calls imaqLocalThreshold */
int frcSmartThreshold(Image* dest, const Image* source, unsigned int windowWidth, unsigned int windowHeight,
LocalThresholdMethod method, double deviationWeight, ObjectType type);
int frcSmartThreshold(Image* dest, const Image* source, unsigned int windowWidth, unsigned int windowHeight,
LocalThresholdMethod method, double deviationWeight, ObjectType type, float replaceValue);
/* Simple Threshold: calls imaqThreshold */
int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin, float rangeMax, float newValue);
int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin, float rangeMax);
/* Color/Hue Threshold: calls imaqColorThreshold */
int frcColorThreshold(Image* dest, const Image* source, ColorMode mode,
const Range* plane1Range, const Range* plane2Range, const Range* plane3Range);
int frcColorThreshold(Image* dest, const Image* source, int replaceValue, ColorMode mode,
const Range* plane1Range, const Range* plane2Range, const Range* plane3Range);
int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange);
int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange, int minSaturation);
/* Extract ColorHue Plane: calls imaqExtractColorPlanes */
int frcExtractColorPlanes(const Image* image, ColorMode mode, Image* plane1, Image* plane2, Image* plane3);
int frcExtractHuePlane(const Image* image, Image* huePlane);
int frcExtractHuePlane(const Image* image, Image* huePlane, int minSaturation);

View File

@@ -85,5 +85,13 @@
#include "Utility.h"
#include "Victor.h"
#include "VictorSP.h"
#include "Vision/AxisCamera.h"
#include "Vision/BinaryImage.h"
#include "Vision/ColorImage.h"
#include "Vision/HSLImage.h"
#include "Vision/ImageBase.h"
#include "Vision/MonoImage.h"
#include "Vision/RGBImage.h"
#include "Vision/Threshold.h"
// XXX: #include "Vision/AxisCamera.h"
#include "WPIErrors.h"

View File

@@ -5339,5 +5339,7 @@ IMAQ_FUNC ReadTextReport* IMAQ_STDCALL imaqReadText(const Image* image, c
IMAQ_FUNC ThresholdData* IMAQ_STDCALL imaqAutoThreshold(Image* dest, Image* source, int numClasses, ThresholdMethod method);
IMAQ_FUNC ColorHistogramReport* IMAQ_STDCALL imaqColorHistogram(Image* image, int numClasses, ColorMode mode, const Image* mask);
IMAQ_FUNC RakeReport* IMAQ_STDCALL imaqRake(const Image* image, const ROI* roi, RakeDirection direction, EdgeProcess process, const RakeOptions* options);
IMAQ_FUNC int IMAQ_STDCALL Priv_ReadJPEGString_C(Image* image, const unsigned char* string, unsigned int stringLength);
#endif

View File

@@ -7,6 +7,7 @@
#include "ADXL345_I2C.h"
#include "I2C.h"
#include "HAL/HAL.hpp"
#include "LiveWindow/LiveWindow.h"
const uint8_t ADXL345_I2C::kAddress;
const uint8_t ADXL345_I2C::kPowerCtlRegister;
@@ -17,6 +18,7 @@ constexpr double ADXL345_I2C::kGsPerLSB;
/**
* Constructor.
*
* @param port The I2C port the accelerometer is attached to
* @param range The range (+ or -) that the accelerometer will measure.
*/
ADXL345_I2C::ADXL345_I2C(Port port, Range range):
@@ -30,6 +32,7 @@ ADXL345_I2C::ADXL345_I2C(Port port, Range range):
SetRange(range);
HALReport(HALUsageReporting::kResourceType_ADXL345, HALUsageReporting::kADXL345_I2C, 0);
LiveWindow::GetInstance()->AddSensor("ADXL345_I2C", port, this);
}
/**
@@ -84,7 +87,7 @@ double ADXL345_I2C::GetAcceleration(ADXL345_I2C::Axes axis)
/**
* Get the acceleration of all axes in Gs.
*
* @return Acceleration measured on all axes of the ADXL345 in Gs.
* @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
*/
ADXL345_I2C::AllAxes ADXL345_I2C::GetAccelerations()
{
@@ -100,3 +103,24 @@ ADXL345_I2C::AllAxes ADXL345_I2C::GetAccelerations()
//}
return data;
}
std::string ADXL345_I2C::GetSmartDashboardType() {
return "3AxisAccelerometer";
}
void ADXL345_I2C::InitTable(ITable *subtable) {
m_table = subtable;
UpdateTable();
}
void ADXL345_I2C::UpdateTable() {
if (m_table != NULL) {
m_table->PutNumber("X", GetX());
m_table->PutNumber("Y", GetY());
m_table->PutNumber("Z", GetZ());
}
}
ITable* ADXL345_I2C::GetTable() {
return m_table;
}

View File

@@ -8,16 +8,25 @@
#include "DigitalInput.h"
#include "DigitalOutput.h"
#include "SPI.h"
#include "LiveWindow/LiveWindow.h"
const uint8_t ADXL345_SPI::kPowerCtlRegister;
const uint8_t ADXL345_SPI::kDataFormatRegister;
const uint8_t ADXL345_SPI::kDataRegister;
constexpr double ADXL345_SPI::kGsPerLSB;
/**
* Constructor.
*
* @param port The SPI port the accelerometer is attached to
* @param range The range (+ or -) that the accelerometer will measure.
*/
ADXL345_SPI::ADXL345_SPI(SPI::Port port, ADXL345_SPI::Range range)
{
m_port = port;
Init(range);
LiveWindow::GetInstance()->AddSensor("ADXL345_SPI", port, this);
}
/**
@@ -106,7 +115,7 @@ double ADXL345_SPI::GetAcceleration(ADXL345_SPI::Axes axis)
/**
* Get the acceleration of all axes in Gs.
*
* @return Acceleration measured on all axes of the ADXL345 in Gs.
* @return An object containing the acceleration measured on each axis of the ADXL345 in Gs.
*/
ADXL345_SPI::AllAxes ADXL345_SPI::GetAccelerations()
{
@@ -131,3 +140,24 @@ ADXL345_SPI::AllAxes ADXL345_SPI::GetAccelerations()
}
return data;
}
std::string ADXL345_SPI::GetSmartDashboardType() {
return "3AxisAccelerometer";
}
void ADXL345_SPI::InitTable(ITable *subtable) {
m_table = subtable;
UpdateTable();
}
void ADXL345_SPI::UpdateTable() {
if (m_table != NULL) {
m_table->PutNumber("X", GetX());
m_table->PutNumber("Y", GetY());
m_table->PutNumber("Z", GetZ());
}
}
ITable* ADXL345_SPI::GetTable() {
return m_table;
}

View File

@@ -23,8 +23,8 @@ void AnalogAccelerometer::InitAccelerometer()
/**
* Create a new instance of an accelerometer.
*
* The constructor allocates desired analog input.
* @param channel The channel number for the analog input the accelerometer is connected to
*/
AnalogAccelerometer::AnalogAccelerometer(int32_t channel)
{
@@ -38,6 +38,7 @@ AnalogAccelerometer::AnalogAccelerometer(int32_t channel)
* Make a new instance of accelerometer given an AnalogInput. This is particularly
* useful if the port is going to be read as an analog channel as well as through
* the Accelerometer class.
* @param channel The existing AnalogInput object for the analog input the accelerometer is connected to
*/
AnalogAccelerometer::AnalogAccelerometer(AnalogInput *channel)
{
@@ -80,7 +81,7 @@ float AnalogAccelerometer::GetAcceleration()
* Set the accelerometer sensitivity.
*
* This sets the sensitivity of the accelerometer used for calculating the acceleration.
* The sensitivity varys by accelerometer model. There are constants defined for various models.
* The sensitivity varies by accelerometer model. There are constants defined for various models.
*
* @param sensitivity The sensitivity of accelerometer in Volts per G.
*/
@@ -92,7 +93,7 @@ void AnalogAccelerometer::SetSensitivity(float sensitivity)
/**
* Set the voltage that corresponds to 0 G.
*
* The zero G voltage varys by accelerometer model. There are constants defined for various models.
* The zero G voltage varies by accelerometer model. There are constants defined for various models.
*
* @param zero The zero G voltage.
*/

View File

@@ -54,7 +54,7 @@ void AnalogInput::InitAnalogInput(uint32_t channel)
/**
* Construct an analog input.
*
* @param channel The channel number to represent.
* @param channel The channel number on the roboRIO to represent. 0-3 are on-board 4-7 are on the MXP port.
*/
AnalogInput::AnalogInput(uint32_t channel)
{
@@ -86,9 +86,9 @@ int16_t AnalogInput::GetValue()
/**
* Get a sample from the output of the oversample and average engine for this channel.
* The sample is 12-bit + the value configured in SetOversampleBits().
* The sample is 12-bit + the bits configured in SetOversampleBits().
* The value configured in SetAverageBits() will cause this value to be averaged 2**bits number of samples.
* This is not a sliding window. The sample will not change until 2**(OversamplBits + AverageBits) samples
* This is not a sliding window. The sample will not change until 2**(OversampleBits + AverageBits) samples
* have been acquired from the module on this channel.
* Use GetAverageVoltage() to get the analog value in calibrated units.
* @return A sample from the oversample and average engine for this channel.
@@ -176,7 +176,7 @@ uint32_t AnalogInput::GetChannel()
/**
* Set the number of averaging bits.
* This sets the number of averaging bits. The actual number of averaged samples is 2**bits.
* This sets the number of averaging bits. The actual number of averaged samples is 2^bits.
* Use averaging to improve the stability of your measurement at the expense of sampling rate.
* The averaging is done automatically in the FPGA.
*
@@ -192,7 +192,7 @@ void AnalogInput::SetAverageBits(uint32_t bits)
/**
* Get the number of averaging bits previously configured.
* This gets the number of averaging bits from the FPGA. The actual number of averaged samples is 2**bits.
* This gets the number of averaging bits from the FPGA. The actual number of averaged samples is 2^bits.
* The averaging is done automatically in the FPGA.
*
* @return Number of bits of averaging previously configured.
@@ -207,7 +207,7 @@ uint32_t AnalogInput::GetAverageBits()
/**
* Set the number of oversample bits.
* This sets the number of oversample bits. The actual number of oversampled values is 2**bits.
* This sets the number of oversample bits. The actual number of oversampled values is 2^bits.
* Use oversampling to improve the resolution of your measurements at the expense of sampling rate.
* The oversampling is done automatically in the FPGA.
*
@@ -224,7 +224,7 @@ void AnalogInput::SetOversampleBits(uint32_t bits)
/**
* Get the number of oversample bits previously configured.
* This gets the number of oversample bits from the FPGA. The actual number of oversampled values is
* 2**bits. The oversampling is done automatically in the FPGA.
* 2^bits. The oversampling is done automatically in the FPGA.
*
* @return Number of bits of oversampling previously configured.
*/
@@ -265,7 +265,7 @@ void AnalogInput::InitAccumulator()
/**
* Set an inital value for the accumulator.
* Set an initial value for the accumulator.
*
* This will be added to all values returned to the user.
* @param initialValue The value that the accumulator should start from when reset.
@@ -301,11 +301,11 @@ void AnalogInput::ResetAccumulator()
* Set the center value of the accumulator.
*
* The center value is subtracted from each A/D value before it is added to the accumulator. This
* is used for the center value of devices like gyros and accelerometers to make integration work
* and to take the device offset into account when integrating.
* is used for the center value of devices like gyros and accelerometers to
* take the device offset into account when integrating.
*
* This center value is based on the output of the oversampled and averaged source from channel 1.
* Because of this, any non-zero oversample bits will affect the size of the value for this field.
* This center value is based on the output of the oversampled and averaged source from the accumulator
* channel. Because of this, any non-zero oversample bits will affect the size of the value for this field.
*/
void AnalogInput::SetAccumulatorCenter(int32_t center)
{
@@ -317,6 +317,7 @@ void AnalogInput::SetAccumulatorCenter(int32_t center)
/**
* Set the accumulator's deadband.
* @param
*/
void AnalogInput::SetAccumulatorDeadband(int32_t deadband)
{
@@ -329,7 +330,7 @@ void AnalogInput::SetAccumulatorDeadband(int32_t deadband)
/**
* Read the accumulated value.
*
* Read the value that has been accumulating on channel 1.
* Read the value that has been accumulating.
* The accumulator is attached after the oversample and average engine.
*
* @return The 64-bit value accumulated since the last Reset().
@@ -379,8 +380,9 @@ void AnalogInput::GetAccumulatorOutput(int64_t *value, uint32_t *count)
}
/**
* Set the sample rate for all analog channels.
*
* Set the sample rate per channel for all analog channels.
* The maximum rate is 500kS/s divided by the number of channels in use.
* This is 62500 samples/s per channel.
* @param samplesPerSecond The number of samples per second.
*/
void AnalogInput::SetSampleRate(float samplesPerSecond)

View File

@@ -44,12 +44,17 @@ void AnalogOutput::InitAnalogOutput(uint32_t channel) {
}
/**
* Construct an anlog output on the given channel
* Construct an analog output on the given channel.
* All analog outputs are located on the MXP port.
* @param The channel number on the roboRIO to represent.
*/
AnalogOutput::AnalogOutput(uint32_t channel) {
InitAnalogOutput(channel);
}
/**
* Destructor. Frees analog output resource
*/
AnalogOutput::~AnalogOutput() {
outputs->Free(m_channel);
}

View File

@@ -11,21 +11,42 @@ void AnalogPotentiometer::initPot(AnalogInput *input, double fullRange, double o
m_analog_input = input;
}
/**
* Construct an Analog Potentiometer object from a channel number.
* @param channel The channel number on the roboRIO to represent. 0-3 are on-board 4-7 are on the MXP port.
* @param fullRange The angular value (in desired units) representing the full 0-5V range of the input.
* @param offset The angular value (in desired units) representing the angular output at 0V.
*/
AnalogPotentiometer::AnalogPotentiometer(int channel, double fullRange, double offset) {
m_init_analog_input = true;
initPot(new AnalogInput(channel), fullRange, offset);
}
/**
* Construct an Analog Potentiometer object from an existing Analog Input pointer.
* @param channel The existing Analog Input pointer
* @param fullRange The angular value (in desired units) representing the full 0-5V range of the input.
* @param offset The angular value (in desired units) representing the angular output at 0V.
*/
AnalogPotentiometer::AnalogPotentiometer(AnalogInput *input, double fullRange, double offset) {
m_init_analog_input = false;
initPot(input, fullRange, offset);
}
/**
* Construct an Analog Potentiometer object from an existing Analog Input reference.
* @param channel The existing Analog Input reference
* @param fullRange The angular value (in desired units) representing the full 0-5V range of the input.
* @param offset The angular value (in desired units) representing the angular output at 0V.
*/
AnalogPotentiometer::AnalogPotentiometer(AnalogInput &input, double fullRange, double offset) {
m_init_analog_input = false;
initPot(&input, fullRange, offset);
}
/**
* Destructor. Releases the Analog Input resource if it was allocated by this object
*/
AnalogPotentiometer::~AnalogPotentiometer() {
if(m_init_analog_input){
delete m_analog_input;
@@ -36,7 +57,7 @@ AnalogPotentiometer::~AnalogPotentiometer() {
/**
* Get the current reading of the potentiometer.
*
* @return The current position of the potentiometer.
* @return The current position of the potentiometer (in the units used for fullRaneg and offset).
*/
double AnalogPotentiometer::Get() {
return (m_analog_input->GetVoltage() / ControllerPower::GetVoltage5V()) * m_fullRange + m_offset;

View File

@@ -29,7 +29,7 @@ void AnalogTrigger::InitTrigger(uint32_t channel)
/**
* Constructor for an analog trigger given a channel number.
*
* @param channel The analog channel (0..7).
* @param channel The channel number on the roboRIO to represent. 0-3 are on-board 4-7 are on the MXP port.
*/
AnalogTrigger::AnalogTrigger(int32_t channel)
{
@@ -40,6 +40,7 @@ AnalogTrigger::AnalogTrigger(int32_t channel)
* Construct an analog trigger given an analog input.
* This should be used in the case of sharing an analog channel between the
* trigger and an analog input object.
* @param channel The pointer to the existing AnalogInput object
*/
AnalogTrigger::AnalogTrigger(AnalogInput *input)
{
@@ -57,6 +58,8 @@ AnalogTrigger::~AnalogTrigger()
* Set the upper and lower limits of the analog trigger.
* The limits are given in ADC codes. If oversampling is used, the units must be scaled
* appropriately.
* @param lower The lower limit of the trigger in ADC codes (12-bit values).
* @param upper The upper limit of the trigger in ADC codes (12-bit values).
*/
void AnalogTrigger::SetLimitsRaw(int32_t lower, int32_t upper)
{
@@ -69,6 +72,8 @@ void AnalogTrigger::SetLimitsRaw(int32_t lower, int32_t upper)
/**
* Set the upper and lower limits of the analog trigger.
* The limits are given as floating point voltage values.
* @param lower The lower limit of the trigger in Volts.
* @param upper The upper limit of the trigger in Volts.
*/
void AnalogTrigger::SetLimitsVoltage(float lower, float upper)
{
@@ -82,6 +87,7 @@ void AnalogTrigger::SetLimitsVoltage(float lower, float upper)
* Configure the analog trigger to use the averaged vs. raw values.
* If the value is true, then the averaged value is selected for the analog trigger, otherwise
* the immediate value is used.
* @param useAveragedValue If true, use the Averaged value, otherwise use the instantaneous reading
*/
void AnalogTrigger::SetAveraged(bool useAveragedValue)
{
@@ -95,6 +101,7 @@ void AnalogTrigger::SetAveraged(bool useAveragedValue)
* Configure the analog trigger to use a filtered value.
* The analog trigger will operate with a 3 point average rejection filter. This is designed to
* help with 360 degree pot applications for the period where the pot crosses through zero.
* @param useFilteredValue If true, use the 3 point rejection filter, otherwise use the unfiltered value
*/
void AnalogTrigger::SetFiltered(bool useFilteredValue)
{
@@ -118,7 +125,7 @@ uint32_t AnalogTrigger::GetIndex()
/**
* Return the InWindow output of the analog trigger.
* True if the analog input is between the upper and lower limits.
* @return The InWindow output of the analog trigger.
* @return True if the analog input is between the upper and lower limits.
*/
bool AnalogTrigger::GetInWindow()
{
@@ -134,7 +141,7 @@ bool AnalogTrigger::GetInWindow()
* True if above upper limit.
* False if below lower limit.
* If in Hysteresis, maintain previous state.
* @return The TriggerState output of the analog trigger.
* @return True if above upper limit. False if below lower limit. If in Hysteresis, maintain previous state.
*/
bool AnalogTrigger::GetTriggerState()
{

View File

@@ -7,6 +7,7 @@
#include "BuiltInAccelerometer.h"
#include "HAL/HAL.hpp"
#include "WPIErrors.h"
#include "LiveWindow/LiveWindow.h"
/**
* Constructor.
@@ -18,6 +19,7 @@ BuiltInAccelerometer::BuiltInAccelerometer(Range range)
SetRange(range);
HALReport(HALUsageReporting::kResourceType_Accelerometer, 0, 0, "Built-in accelerometer");
LiveWindow::GetInstance()->AddSensor((std::string)"BuiltInAccel",0, this);
}
BuiltInAccelerometer::~BuiltInAccelerometer()
@@ -62,7 +64,7 @@ double BuiltInAccelerometer::GetZ()
}
std::string BuiltInAccelerometer::GetSmartDashboardType() {
return "Accelerometer";
return "3AxisAccelerometer";
}
void BuiltInAccelerometer::InitTable(ITable *subtable) {

View File

@@ -66,9 +66,16 @@ void CANTalon::PIDWrite(float output)
}
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
/**
* Gets the current status of the Talon (usually a sensor value).
*
* In Current mode: returns output current.
* In Speed mode: returns current speed.
* In Position mode: returns current sensor position.
* In PercentVbus and Follower modes: returns current applied throttle.
*
* @return The current sensor value of the Talon.
*/
float CANTalon::Get()
{
int value;
@@ -94,17 +101,20 @@ float CANTalon::Get()
* Sets the appropriate output on the talon, depending on the mode.
*
* In PercentVbus, the output is between -1.0 and 1.0, with 0.0 as stopped.
* In Voltage mode, outputValue is in volts.
* In Current mode, outputValue is in amperes.
* In Speed mode, outputValue is in position change / 10ms.
* In Position mode, outputValue is in encoder ticks or an analog value,
* In Voltage mode, output value is in volts.
* In Current mode, output value is in amperes.
* In Speed mode, output value is in position change / 10ms.
* In Position mode, output value is in encoder ticks or an analog value,
* depending on the sensor.
* In Follower mode, the output value is the integer device ID of the talon to duplicate.
*
* @param outputValue The setpoint value, as described above.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::Set(float value, uint8_t syncGroup)
{
/* feed safety helper since caller just updated our output */
m_safetyHelper->Feed();
if(m_controlEnabled) {
m_setPoint = value;
CTR_Code status;
@@ -186,10 +196,12 @@ void CANTalon::SetP(double p)
}
}
/**
* TODO documentation (see CANJaguar.cpp)
* @see SelectProfileSlot to choose between the two sets of gains.
*/
/**
* Set the integration constant of the currently selected profile.
*
* @param i Integration constant for the currently selected PID profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetI(double i)
{
CTR_Code status = m_impl->SetIgain(m_profile, i);
@@ -199,7 +211,9 @@ void CANTalon::SetI(double i)
}
/**
* TODO documentation (see CANJaguar.cpp)
* Set the derivative constant of the currently selected profile.
*
* @param d Derivative constant for the currently selected PID profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetD(double d)
@@ -210,7 +224,9 @@ void CANTalon::SetD(double d)
}
}
/**
* Set the feedforward value of the currently selected profile.
*
* @param f Feedforward constant for the currently selected PID profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
void CANTalon::SetF(double f)
@@ -246,7 +262,11 @@ void CANTalon::SelectProfileSlot(int slotIdx)
}
}
/**
* TODO documentation (see CANJaguar.cpp)
* Sets control values for closed loop control.
*
* @param p Proportional constant.
* @param i Integration constant.
* @param d Differential constant.
* This function does not modify F-gain. Considerable passing a zero for f using
* the four-parameter function.
*/
@@ -256,6 +276,14 @@ void CANTalon::SetPID(double p, double i, double d)
SetI(i);
SetD(d);
}
/**
* Sets control values for closed loop control.
*
* @param p Proportional constant.
* @param i Integration constant.
* @param d Differential constant.
* @param f Feedforward constant.
*/
void CANTalon::SetPID(double p, double i, double d, double f)
{
SetP(p);
@@ -285,7 +313,9 @@ void CANTalon::SetStatusFrameRateMs(StatusFrameRate stateFrame, int periodMs)
}
/**
* TODO documentation (see CANJaguar.cpp)
* Get the current proportional constant.
*
* @return double proportional constant for current profile.
* @see SelectProfileSlot to choose between the two sets of gains.
*/
double CANTalon::GetP()
@@ -400,7 +430,7 @@ double CANTalon::GetSetpoint() {
/**
* Returns the voltage coming in from the battery.
*
* @return The input voltage in vols.
* @return The input voltage in volts.
*/
float CANTalon::GetBusVoltage()
{
@@ -413,7 +443,7 @@ float CANTalon::GetBusVoltage()
}
/**
* TODO documentation (see CANJaguar.cpp)
* @return The voltage being output by the Talon, in Volts.
*/
float CANTalon::GetOutputVoltage()
{
@@ -428,7 +458,7 @@ float CANTalon::GetOutputVoltage()
/**
* TODO documentation (see CANJaguar.cpp)
* Returns the current going through the Talon, in Amperes.
*/
float CANTalon::GetOutputCurrent()
{
@@ -442,9 +472,9 @@ float CANTalon::GetOutputCurrent()
return current;
}
/**
* TODO documentation (see CANJaguar.cpp)
*/
/**
* Returns temperature of Talon, in degrees Celsius.
*/
float CANTalon::GetTemperature()
{
double temp;
@@ -963,7 +993,7 @@ int CANTalon::GetBrakeEnableDuringNeutral()
return brakeEn;
}
/**
* TODO documentation (see CANJaguar.cpp)
* @deprecated not implemented
*/
void CANTalon::ConfigEncoderCodesPerRev(uint16_t codesPerRev)
{
@@ -971,7 +1001,7 @@ void CANTalon::ConfigEncoderCodesPerRev(uint16_t codesPerRev)
}
/**
* TODO documentation (see CANJaguar.cpp)
* @deprecated not implemented
*/
void CANTalon::ConfigPotentiometerTurns(uint16_t turns)
{
@@ -979,7 +1009,7 @@ void CANTalon::ConfigPotentiometerTurns(uint16_t turns)
}
/**
* TODO documentation (see CANJaguar.cpp)
* @deprecated not implemented
*/
void CANTalon::ConfigSoftPositionLimits(double forwardLimitPosition, double reverseLimitPosition)
{

View File

@@ -1,21 +1,10 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "CameraServer.h"
#include "ErrorBase.h"
#include "WPIErrors.h"
#include "Utility.h"
#include "Timer.h"
#include <iostream>
#include <algorithm>
#include <algorithm>
#include <chrono>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
@@ -23,228 +12,234 @@
constexpr uint8_t CameraServer::kMagicNumber[];
CameraServer *CameraServer::s_instance = nullptr;
/**
* Singleton getter.
*/
CameraServer *CameraServer::GetInstance() {
if(s_instance == nullptr) {
s_instance = new CameraServer;
}
return s_instance;
CameraServer* CameraServer::GetInstance() {
if (s_instance == NULL) {
s_instance = new CameraServer;
}
return s_instance;
}
CameraServer::CameraServer() :
m_serverThread(&CameraServer::serve, this),
m_quality(50),
m_autoCaptureStarted(false) {
m_camera(),
m_serverThread(&CameraServer::Serve, this),
m_captureThread(),
m_imageMutex(),
m_newImageVariable(),
m_dataPool(3),
m_quality(50),
m_autoCaptureStarted(false),
m_hwClient(true),
m_imageData(nullptr, 0, 0, false) {
for (int i = 0; i < 3; i++)
m_dataPool.push_back(new uint8_t[kMaxImageSize]);
}
/**
* Manually change the image that is served by the MJPEG stream. This can
* be called to pass custom annotated images to the dashboard. Note that, for
* 640x480 video, this method could take between 40 and 50 milliseconds to
* complete.
*
* This shouldn't be called if {@link #StartAutomaticCapture} is called.
*
* @param image The IMAQ image to show on the dashboard
*/
void CameraServer::SetImage(Image const *image) {std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
/* Flatten the IMAQ image to a JPEG */
uint32_t dataSize;
uint8_t *data = (uint8_t *)imaqFlatten(image, IMAQ_FLATTEN_IMAGE, IMAQ_COMPRESSION_JPEG, 10 * m_quality, &dataSize);
/* Find the start of the JPEG data */
uint8_t *jpegData = data;
while(jpegData[0] != 0xff || jpegData[1] != 0xd8) {
jpegData++;
dataSize--;
wpi_assert(dataSize >= 2);
}
m_imageData.assign(dataSize, '\0');
std::copy(jpegData, jpegData + dataSize, m_imageData.begin());
/* Release the data from IMAQ */
imaqDispose(data);
m_newImageReady.notify_all();
void CameraServer::FreeImageData(std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData) {
if (std::get<3>(imageData)) imaqDispose(std::get<0>(imageData));
else if (std::get<0>(imageData) != nullptr) {
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
m_dataPool.push_back(std::get<0>(imageData));
}
}
void CameraServer::SetImageData(uint8_t* data, unsigned int size, unsigned int start, bool imaqData) {
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
FreeImageData(m_imageData);
m_imageData = std::make_tuple(data, size, start, imaqData);
m_newImageVariable.notify_all();
}
void CameraServer::SetImage(Image const *image) {
unsigned int dataSize = 0;
uint8_t* data = (uint8_t*) imaqFlatten(image, IMAQ_FLATTEN_IMAGE, IMAQ_COMPRESSION_JPEG, 10 * m_quality, &dataSize);
// If we're using a HW camera, then find the start of the data
bool hwClient;
{
// Make a local copy of the hwClient variable so that we can safely use it.
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
hwClient = m_hwClient;
}
unsigned int start = 0;
if (hwClient) {
while (start < dataSize - 1) {
if (data[start] == 0xFF && data[start + 1] == 0xD8) break;
else start++;
}
}
dataSize -= start;
wpi_assert(dataSize > 2);
SetImageData(data, dataSize, start, true);
}
void CameraServer::AutoCapture() {
Image* frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
while (true) {
bool hwClient;
uint8_t* data = nullptr;
{
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
hwClient = m_hwClient;
if (hwClient) {
data = m_dataPool.back();
m_dataPool.pop_back();
}
}
if (hwClient) {
unsigned int size = m_camera->GetImageData(data, kMaxImageSize);
SetImageData(data, size);
} else {
m_camera->GetImage(frame);
SetImage(frame);
}
}
}
/**
* Start automatically capturing images to send to the dashboard.
*
* You should call this method to just see a camera feed on the dashboard
* without doing any vision processing on the roboRIO. {@link #SetImage}
* shouldn't be called after this is called.
*
* @param cameraName The name of the camera interface (e.g. "cam1")
*/
void CameraServer::StartAutomaticCapture(char const *cameraName) {
if(m_autoCaptureStarted) {
wpi_setWPIErrorWithContext(IncompatibleState, "Automatic capture has already been started");
return;
}
/* Capturing images takes a lot of CPU time, since JPEG compression is
done in software, so this is done in a new thread. */
std::thread captureThread([cameraName, this] {
IMAQdxSession session;
IMAQdxError error;
Image *frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
error = IMAQdxOpenCamera(cameraName, IMAQdxCameraControlModeController, &session);
if(error != IMAQdxErrorSuccess) {
wpi_setImaqErrorWithContext(error, "IMAQdxOpenCamera");
}
error = IMAQdxConfigureGrab(session);
if(error != IMAQdxErrorSuccess) {
wpi_setImaqErrorWithContext(error, "IMAQdxConfigureGrab");
}
error = IMAQdxStartAcquisition(session);
if(error != IMAQdxErrorSuccess) {
wpi_setImaqErrorWithContext(error, "IMAQdxStartAcquisition");
}
/* In an infinite loop, wait for an image from the camera, then sent
it to the dashboard. */
for(;;) {
error = IMAQdxGrab(session, frame, true, NULL);
if(error != IMAQdxErrorSuccess) {
wpi_setImaqErrorWithContext(error, "IMAQdxGrab");
break;
}
SetImage(frame);
}
/* If something went wrong, close the session */
IMAQdxStopAcquisition(session);
IMAQdxCloseCamera(session);
});
captureThread.detach();
std::shared_ptr<USBCamera> camera = std::make_shared<USBCamera>(cameraName, true);
camera->OpenCamera();
StartAutomaticCapture(camera);
}
void CameraServer::StartAutomaticCapture(std::shared_ptr<USBCamera> camera) {
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
if (m_autoCaptureStarted) return;
m_camera = camera;
m_camera->StartCapture();
m_captureThread = std::thread(&CameraServer::AutoCapture, this);
m_captureThread.detach();
m_autoCaptureStarted = true;
}
bool CameraServer::IsAutoCaptureStarted() {
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
return m_autoCaptureStarted;
}
void CameraServer::SetSize(unsigned int size) {
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
if (!m_camera) return;
if (size == kSize160x120) m_camera->SetSize(160, 120);
else if (size == kSize320x240) m_camera->SetSize(320, 240);
else if (size == kSize640x480) m_camera->SetSize(640, 480);
}
/**
* Set the quality of the compressed image sent to the dashboard
*
* @param quality The quality of the JPEG image, from 0 to 100
*/
void CameraServer::SetQuality(unsigned int quality) {
if(quality > 100) {
wpi_setWPIErrorWithContext(ParameterOutOfRange, "JPEG quality should be from 0 to 100");
return;
}
m_quality = quality;
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
m_quality = quality > 100 ? 100 : quality;
}
/**
* Get the quality of the compressed image sent to the dashboard
*
* @return The quality, from 0 to 100
*/
unsigned int CameraServer::GetQuality() const {
return m_quality;
unsigned int CameraServer::GetQuality() {
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
return m_quality;
}
/**
* Run the M-JPEG server.
*
* This function listens for a connection from the dashboard in a background
* thread, then sends back the M-JPEG stream.
*/
void CameraServer::serve() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
void CameraServer::Serve() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1) {
wpi_setErrnoError();
}
if (sock == -1)
wpi_setErrnoError();
int reuseAddr = 1;
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) == -1) {
wpi_setErrnoError();
}
int reuseAddr = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) == -1)
wpi_setErrnoError();
sockaddr_in address, clientAddress;
sockaddr_in address, clientAddress;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(kPort);
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(kPort);
if(bind(sock, (struct sockaddr *)&address, sizeof(address)) == -1) {
wpi_setErrnoError();
}
if (bind(sock, (struct sockaddr *)&address, sizeof(address)) == -1)
wpi_setErrnoError();
if(listen(sock, 10) == -1) {
wpi_setErrnoError();
}
if (listen(sock, 10) == -1)
wpi_setErrnoError();
for(;;) {
socklen_t clientaddresslen = sizeof(clientAddress);
int conn = accept(sock, (struct sockaddr *)&clientAddress, &clientaddresslen);
while(true) {
socklen_t clientAddressLen = sizeof(clientAddress);
/* Read the request from the dashboard */
Request req;
if(read(conn, &req, sizeof req) == -1) {
wpi_setErrnoError();
close(conn);
continue;
} else {
req.fps = ntohl(req.fps);
req.compression = ntohl(req.compression);
req.size = ntohl(req.size);
}
int conn = accept(sock, (struct sockaddr*)&clientAddress, &clientAddressLen);
if (conn == -1) {
wpi_setErrnoError();
continue;
}
/* Only the "hardware compression" mode is supported from C++ */
if(req.compression != kHardwareCompression) {
wpi_setWPIErrorWithContext(IncompatibleState, "Choose \"USB Camera HW\" on the dashboard");
close(conn);
continue;
}
Request req;
if (read(conn, &req, sizeof(req)) == -1) {
wpi_setErrnoError();
close(conn);
continue;
} else {
req.fps = ntohl(req.fps);
req.compression = ntohl(req.compression);
req.size = ntohl(req.size);
}
/* The period that frames are sent is 1/FPS */
auto period = std::chrono::microseconds(1000000) / req.fps;
// TODO: Support the SW Compression. The rest of the code below will work as though this
// check isn't here
if (req.compression != kHardwareCompression) {
wpi_setWPIErrorWithContext(IncompatibleState, "Choose \"USB Camera HW\" on the dashboard");
close(conn);
continue;
}
for(;;) {
auto startTime = std::chrono::steady_clock::now();
{
// Wait for the camera to be setw
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
if (!m_camera) {
std::cout << "Camera not yet ready, awaiting first image" << std::endl;
m_newImageVariable.wait(lock);
}
m_hwClient = req.compression == kHardwareCompression;
if (!m_hwClient) SetQuality(100 - req.compression);
else if (m_camera) m_camera->SetFPS(req.fps);
SetSize(req.size);
}
{
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
auto period = std::chrono::microseconds(1000000) / req.fps;
while (true) {
auto startTime = std::chrono::steady_clock::now();
std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData;
{
std::unique_lock<std::recursive_mutex> lock(m_imageMutex);
m_newImageVariable.wait(lock);
imageData = m_imageData;
m_imageData = std::make_tuple(nullptr, 0, 0, false);
}
m_newImageReady.wait(lock);
unsigned int size = std::get<1>(imageData);
unsigned int netSize = htonl(size);
unsigned int start = std::get<2>(imageData);
uint8_t *data = std::get<0>(imageData);
/* Send the magic number that indicates the beginning of a new image */
if(write(conn, kMagicNumber, sizeof kMagicNumber) == -1) {
break;
}
if (data == nullptr) continue;
/* Send the size of this image */
uint32_t size = htonl(m_imageData.size());
if(write(conn, &size, sizeof size) == -1) {
m_imageMutex.unlock();
break;
}
/* Send the image data itself */
if(write(conn, m_imageData.data(), m_imageData.size()) == -1) {
break;
}
}
/* Sleep long enough to maintain a constant framerate */
std::this_thread::sleep_until(startTime + period);
}
close(conn);
}
close(sock);
if (write(conn, kMagicNumber, sizeof(kMagicNumber)) == -1) {
wpi_setErrnoErrorWithContext("[CameraServer] Error sending magic number");
FreeImageData(imageData);
break;
}
if (write(conn, &netSize, sizeof(netSize)) == -1) {
wpi_setErrnoErrorWithContext("[CameraServer] Error sending image size");
FreeImageData(imageData);
break;
}
if (write(conn, &data[start], sizeof(uint8_t) * size) == -1) {
wpi_setErrnoErrorWithContext("[CameraServer] Error sending image data");
FreeImageData(imageData);
break;
}
FreeImageData(imageData);
std::this_thread::sleep_until(startTime + period);
}
close(conn);
}
close(sock);
}

View File

@@ -15,7 +15,7 @@ void Compressor::InitCompressor(uint8_t pcmID) {
/**
* Constructor
*
* Uses the default solenoid module number
* Uses the default PCM ID (0)
*/
Compressor::Compressor() {
InitCompressor(GetDefaultSolenoidModule());
@@ -24,7 +24,7 @@ Compressor::Compressor() {
/**
* Constructor
*
* @param module The module number to use (1 or 2)
* @param module The PCM ID to use (0-62)
*/
Compressor::Compressor(uint8_t pcmID) {
InitCompressor(pcmID);
@@ -35,20 +35,21 @@ Compressor::~Compressor() {
}
/**
* Starts closed-loop control
* Starts closed-loop control. Note that closed loop control is enabled by default.
*/
void Compressor::Start() {
SetClosedLoopControl(true);
}
/**
* Stops closed-loop control
* Stops closed-loop control. Note that closed loop control is enabled by default.
*/
void Compressor::Stop() {
SetClosedLoopControl(false);
}
/**
* Check if compressor output is active
* @return true if the compressor is on
*/
bool Compressor::Enabled() {
@@ -65,6 +66,7 @@ bool Compressor::Enabled() {
}
/**
* Check if the pressure switch is triggered
* @return true if pressure is low
*/
bool Compressor::GetPressureSwitchValue() {
@@ -82,6 +84,7 @@ bool Compressor::GetPressureSwitchValue() {
/**
* Query how much current the compressor is drawing
* @return The current through the compressor, in amps
*/
float Compressor::GetCompressorCurrent() {
@@ -101,6 +104,7 @@ float Compressor::GetCompressorCurrent() {
/**
* Enables or disables automatically turning the compressor on when the
* pressure is low.
* @param on Set to true to enable closed loop control of the compressor. False to disable.
*/
void Compressor::SetClosedLoopControl(bool on) {
int32_t status = 0;
@@ -115,6 +119,7 @@ void Compressor::SetClosedLoopControl(bool on) {
/**
* Returns true if the compressor will automatically turn on when the
* pressure is low.
* @return True if closed loop control of the compressor is enabled. False if disabled.
*/
bool Compressor::GetClosedLoopControl() {
int32_t status = 0;
@@ -129,6 +134,132 @@ bool Compressor::GetClosedLoopControl() {
return value;
}
/**
* Query if the compressor output has been disabled due to high current draw.
* @return true if PCM is in fault state : Compressor Drive is
* disabled due to compressor current being too high.
*/
bool Compressor::GetCompressorCurrentTooHighFault() {
int32_t status = 0;
bool value;
value = getCompressorCurrentTooHighFault(m_pcm_pointer, &status);
if(status) {
wpi_setWPIError(Timeout);
}
return value;
}
/**
* Query if the compressor output has been disabled due to high current draw (sticky).
* A sticky fault will not clear on device reboot, it must be cleared through code or the webdash.
* @return true if PCM sticky fault is set : Compressor Drive is
* disabled due to compressor current being too high.
*/
bool Compressor::GetCompressorCurrentTooHighStickyFault() {
int32_t status = 0;
bool value;
value = getCompressorCurrentTooHighStickyFault(m_pcm_pointer, &status);
if(status) {
wpi_setWPIError(Timeout);
}
return value;
}
/**
* Query if the compressor output has been disabled due to a short circuit (sticky).
* A sticky fault will not clear on device reboot, it must be cleared through code or the webdash.
* @return true if PCM sticky fault is set : Compressor output
* appears to be shorted.
*/
bool Compressor::GetCompressorShortedStickyFault() {
int32_t status = 0;
bool value;
value = getCompressorShortedStickyFault(m_pcm_pointer, &status);
if(status) {
wpi_setWPIError(Timeout);
}
return value;
}
/**
* Query if the compressor output has been disabled due to a short circuit.
* @return true if PCM is in fault state : Compressor output
* appears to be shorted.
*/
bool Compressor::GetCompressorShortedFault() {
int32_t status = 0;
bool value;
value = getCompressorShortedFault(m_pcm_pointer, &status);
if(status) {
wpi_setWPIError(Timeout);
}
return value;
}
/**
* Query if the compressor output does not appear to be wired (sticky).
* A sticky fault will not clear on device reboot, it must be cleared through code or the webdash.
* @return true if PCM sticky fault is set : Compressor does not
* appear to be wired, i.e. compressor is
* not drawing enough current.
*/
bool Compressor::GetCompressorNotConnectedStickyFault() {
int32_t status = 0;
bool value;
value = getCompressorNotConnectedStickyFault(m_pcm_pointer, &status);
if(status) {
wpi_setWPIError(Timeout);
}
return value;
}
/**
* Query if the compressor output does not appear to be wired.
* @return true if PCM is in fault state : Compressor does not
* appear to be wired, i.e. compressor is
* not drawing enough current.
*/
bool Compressor::GetCompressorNotConnectedFault() {
int32_t status = 0;
bool value;
value = getCompressorNotConnectedFault(m_pcm_pointer, &status);
if(status) {
wpi_setWPIError(Timeout);
}
return value;
}
/**
* Clear ALL sticky faults inside PCM that Compressor is wired to.
*
* If a sticky fault is set, then it will be persistently cleared. Compressor drive
* maybe momentarily disable while flags are being cleared. Care should be
* taken to not call this too frequently, otherwise normal compressor
* functionality may be prevented.
*
* If no sticky faults are set then this call will have no effect.
*/
void Compressor::ClearAllPCMStickyFaults() {
int32_t status = 0;
clearAllPCMStickyFaults(m_pcm_pointer, &status);
if(status) {
wpi_setWPIError(Timeout);
}
}
void Compressor::UpdateTable() {
if(m_table) {
m_table->PutBoolean("Enabled", Enabled());

View File

@@ -13,7 +13,7 @@
/**
* Get the input voltage to the robot controller
* @return The controller input voltage value
* @return The controller input voltage value in Volts
*/
double ControllerPower::GetInputVoltage() {
int32_t status = 0;
@@ -24,7 +24,7 @@ double ControllerPower::GetInputVoltage() {
/**
* Get the input current to the robot controller
* @return The controller input current value
* @return The controller input current value in Amps
*/
double ControllerPower::GetInputCurrent() {
int32_t status = 0;
@@ -35,7 +35,7 @@ double ControllerPower::GetInputCurrent() {
/**
* Get the voltage of the 6V rail
* @return The controller 6V rail voltage value
* @return The controller 6V rail voltage value in Volts
*/
double ControllerPower::GetVoltage6V() {
int32_t status = 0;
@@ -46,7 +46,7 @@ double ControllerPower::GetVoltage6V() {
/**
* Get the current output of the 6V rail
* @return The controller 6V rail output current value
* @return The controller 6V rail output current value in Amps
*/
double ControllerPower::GetCurrent6V() {
int32_t status = 0;
@@ -58,7 +58,7 @@ double ControllerPower::GetCurrent6V() {
/**
* Get the enabled state of the 6V rail. The rail may be disabled due to a controller
* brownout, a short circuit on the rail, or controller over-voltage
* @return The controller 6V rail enabled value
* @return The controller 6V rail enabled value. True for enabled.
*/
bool ControllerPower::GetEnabled6V() {
int32_t status = 0;
@@ -69,7 +69,7 @@ bool ControllerPower::GetEnabled6V() {
/**
* Get the count of the total current faults on the 6V rail since the controller has booted
* @return The number of faults
* @return The number of faults.
*/
int ControllerPower::GetFaultCount6V() {
int32_t status = 0;
@@ -80,6 +80,7 @@ int ControllerPower::GetFaultCount6V() {
/**
* Get the voltage of the 5V rail
* @return The controller 5V rail voltage value in Volts
*/
double ControllerPower::GetVoltage5V() {
int32_t status = 0;
@@ -90,6 +91,7 @@ double ControllerPower::GetVoltage5V() {
/**
* Get the current output of the 5V rail
* @return The controller 5V rail output current value in Amps
*/
double ControllerPower::GetCurrent5V() {
int32_t status = 0;
@@ -101,7 +103,7 @@ double ControllerPower::GetCurrent5V() {
/**
* Get the enabled state of the 5V rail. The rail may be disabled due to a controller
* brownout, a short circuit on the rail, or controller over-voltage
* @return The controller 5V rail enabled value
* @return The controller 5V rail enabled value. True for enabled.
*/
bool ControllerPower::GetEnabled5V() {
int32_t status = 0;
@@ -123,6 +125,7 @@ int ControllerPower::GetFaultCount5V() {
/**
* Get the voltage of the 3.3V rail
* @return The controller 3.3V rail voltage value in Volts
*/
double ControllerPower::GetVoltage3V3() {
int32_t status = 0;
@@ -133,6 +136,7 @@ double ControllerPower::GetVoltage3V3() {
/**
* Get the current output of the 3.3V rail
* @return The controller 3.3V rail output current value in Amps
*/
double ControllerPower::GetCurrent3V3() {
int32_t status = 0;
@@ -145,7 +149,7 @@ double ControllerPower::GetCurrent3V3() {
/**
* Get the enabled state of the 3.3V rail. The rail may be disabled due to a controller
* brownout, a short circuit on the rail, or controller over-voltage
* @return The controller 3.3V rail enabled value
* @return The controller 3.3V rail enabled value. True for enabled.
*/
bool ControllerPower::GetEnabled3V3() {
int32_t status = 0;

View File

@@ -16,14 +16,15 @@
* This creates a ChipObject counter and initializes status variables appropriately
*
* The counter will start counting immediately.
* @param mode The counter mode
*/
void Counter::InitCounter(Mode mode)
{
m_table = NULL;
int32_t status = 0;
uint32_t index = 0;
m_counter = initializeCounter(mode, &index, &status);
m_index = 0;
m_counter = initializeCounter(mode, &m_index, &status);
wpi_setErrorWithContext(status, getHALErrorMessage(status));
m_upSource = NULL;
@@ -31,12 +32,14 @@ void Counter::InitCounter(Mode mode)
m_allocatedUpSource = false;
m_allocatedDownSource = false;
HALReport(HALUsageReporting::kResourceType_Counter, index, mode);
SetMaxPeriod(.5);
HALReport(HALUsageReporting::kResourceType_Counter, m_index, mode);
}
/**
* Create an instance of a counter where no sources are selected.
* Then they all must be selected by calling functions to specify the upsource and the downsource
* They all must be selected by calling functions to specify the upsource and the downsource
* independently.
*
* The counter will start counting immediately.
@@ -50,11 +53,12 @@ Counter::Counter() :
}
/**
* Create an instance of a counter from a Digital Input.
* Create an instance of a counter from a Digital Source (such as a Digital Input).
* This is used if an existing digital input is to be shared by multiple other objects such
* as encoders.
* as encoders or if the Digital Source is not a Digital Input channel (such as an Analog Trigger).
*
* The counter will start counting immediately.
* @param source A pointer to the existing DigitalSource object. It will be set as the Up Source.
*/
Counter::Counter(DigitalSource *source) :
m_upSource(NULL),
@@ -66,6 +70,14 @@ Counter::Counter(DigitalSource *source) :
ClearDownSource();
}
/**
* Create an instance of a counter from a Digital Source (such as a Digital Input).
* This is used if an existing digital input is to be shared by multiple other objects such
* as encoders or if the Digital Source is not a Digital Input channel (such as an Analog Trigger).
*
* The counter will start counting immediately.
* @param source A reference to the existing DigitalSource object. It will be set as the Up Source.
*/
Counter::Counter(DigitalSource &source) :
m_upSource(NULL),
m_downSource(NULL),
@@ -81,6 +93,7 @@ Counter::Counter(DigitalSource &source) :
* Create an up-Counter instance given a channel.
*
* The counter will start counting immediately.
* @param channel The DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
*/
Counter::Counter(int32_t channel) :
m_upSource(NULL),
@@ -98,6 +111,7 @@ Counter::Counter(int32_t channel) :
* Use the trigger state output from the analog trigger.
*
* The counter will start counting immediately.
* @param trigger The pointer to the existing AnalogTrigger object.
*/
Counter::Counter(AnalogTrigger *trigger) :
m_upSource(NULL),
@@ -110,6 +124,14 @@ Counter::Counter(AnalogTrigger *trigger) :
m_allocatedUpSource = true;
}
/**
* Create an instance of a Counter object.
* Create an instance of a simple up-Counter given an analog trigger.
* Use the trigger state output from the analog trigger.
*
* The counter will start counting immediately.
* @param trigger The reference to the existing AnalogTrigger object.
*/
Counter::Counter(AnalogTrigger &trigger) :
m_upSource(NULL),
m_downSource(NULL),
@@ -121,6 +143,15 @@ Counter::Counter(AnalogTrigger &trigger) :
m_allocatedUpSource = true;
}
/**
* Create an instance of a Counter object.
* Creates a full up-down counter given two Digital Sources
* @param encodingType The quadrature decoding mode (1x or 2x)
* @param upSource The pointer to the DigitalSource to set as the up source
* @param downSource The pointer to the DigitalSource to set as the down source
* @param inverted True to invert the output (reverse the direction)
*/
Counter::Counter(EncodingType encodingType, DigitalSource *upSource, DigitalSource *downSource, bool inverted) :
m_upSource(NULL),
m_downSource(NULL),
@@ -176,6 +207,7 @@ Counter::~Counter()
/**
* Set the upsource for the counter as a digital input channel.
* @param channel The DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
*/
void Counter::SetUpSource(int32_t channel)
{
@@ -209,6 +241,7 @@ void Counter::SetUpSource(AnalogTrigger &analogTrigger, AnalogTriggerType trigge
/**
* Set the source object that causes the counter to count up.
* Set the up counting DigitalSource.
* @param source Pointer to the DigitalSource object to set as the up source
*/
void Counter::SetUpSource(DigitalSource *source)
{
@@ -236,6 +269,7 @@ void Counter::SetUpSource(DigitalSource *source)
/**
* Set the source object that causes the counter to count up.
* Set the up counting DigitalSource.
* @param source Reference to the DigitalSource object to set as the up source
*/
void Counter::SetUpSource(DigitalSource &source)
{
@@ -244,7 +278,9 @@ void Counter::SetUpSource(DigitalSource &source)
/**
* Set the edge sensitivity on an up counting source.
* Set the up source to either detect rising edges or falling edges.
* Set the up source to either detect rising edges or falling edges or both.
* @param risingEdge True to trigger on rising edges
* @param fallingEdge True to trigger on falling edges
*/
void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge)
{
@@ -277,6 +313,7 @@ void Counter::ClearUpSource()
/**
* Set the down counting source to be a digital input channel.
* @param channel The DIO channel to use as the up source. 0-9 are on-board, 10-25 are on the MXP
*/
void Counter::SetDownSource(int32_t channel)
{
@@ -310,6 +347,7 @@ void Counter::SetDownSource(AnalogTrigger &analogTrigger, AnalogTriggerType trig
/**
* Set the source object that causes the counter to count down.
* Set the down counting DigitalSource.
* @param source Pointer to the DigitalSource object to set as the down source
*/
void Counter::SetDownSource(DigitalSource *source)
{
@@ -337,6 +375,7 @@ void Counter::SetDownSource(DigitalSource *source)
/**
* Set the source object that causes the counter to count down.
* Set the down counting DigitalSource.
* @param source Reference to the DigitalSource object to set as the down source
*/
void Counter::SetDownSource(DigitalSource &source)
{
@@ -346,6 +385,8 @@ void Counter::SetDownSource(DigitalSource &source)
/**
* Set the edge sensitivity on a down counting source.
* Set the down source to either detect rising edges or falling edges.
* @param risingEdge True to trigger on rising edges
* @param fallingEdge True to trigger on falling edges
*/
void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge)
{
@@ -484,11 +525,11 @@ void Counter::Reset()
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/*
/**
* Get the Period of the most recent count.
* Returns the time interval of the most recent count. This can be used for velocity calculations
* to determine shaft speed.
* @returns The period of the last two pulses in units of seconds.
* @returns The period between the last two pulses in units of seconds.
*/
double Counter::GetPeriod()
{
@@ -525,6 +566,7 @@ void Counter::SetMaxPeriod(double maxPeriod)
* output (except when there have been no events since an FPGA reset) and you will likely not
* see the stopped bit become true (since it is updated at the end of an average and there are
* no samples to average).
* @param enabled True to enable update when empty
*/
void Counter::SetUpdateWhenEmpty(bool enabled)
{

View File

@@ -40,7 +40,7 @@ void DigitalInput::InitDigitalInput(uint32_t channel)
* Create an instance of a Digital Input class.
* Creates a digital input given a channel.
*
* @param channel The digital channel (0..19).
* @param channel The DIO channel 0-9 are on-board, 10-25 are on the MXP port
*/
DigitalInput::DigitalInput(uint32_t channel)
{
@@ -67,7 +67,7 @@ DigitalInput::~DigitalInput()
wpi_setErrorWithContext(status, getHALErrorMessage(status));
}
/*
/**
* Get the value from a digital input channel.
* Retrieve the value of a single digital input channel from the FPGA.
*/

View File

@@ -39,7 +39,7 @@ void DigitalOutput::InitDigitalOutput(uint32_t channel)
* Create an instance of a digital output.
* Create a digital output given a channel.
*
* @param channel The digital channel (0..19)
* @param channel The digital channel 0-9 are on-board, 10-25 are on the MXP port
*/
DigitalOutput::DigitalOutput(uint32_t channel)
{
@@ -63,6 +63,7 @@ DigitalOutput::~DigitalOutput()
/**
* Set the value of a digital output.
* Set the value of a digital output to either one (true) or zero (false).
* @param value 1 (true) for high, 0 (false) for disabled
*/
void DigitalOutput::Set(uint32_t value)
{

View File

@@ -10,7 +10,7 @@
#include "LiveWindow/LiveWindow.h"
/**
* Common function to implement constructor behavior.
* Common function to implement constructor behaviour.
*/
void DoubleSolenoid::InitSolenoid()
{
@@ -59,9 +59,9 @@ void DoubleSolenoid::InitSolenoid()
/**
* Constructor.
*
* @param forwardChannel The forward channel on the module to control.
* @param reverseChannel The reverse channel on the module to control.
* Uses the default PCM ID of 0
* @param forwardChannel The forward channel number on the PCM (0..7).
* @param reverseChannel The reverse channel number on the PCM (0..7).
*/
DoubleSolenoid::DoubleSolenoid(uint32_t forwardChannel, uint32_t reverseChannel)
: SolenoidBase (GetDefaultSolenoidModule())
@@ -75,8 +75,8 @@ DoubleSolenoid::DoubleSolenoid(uint32_t forwardChannel, uint32_t reverseChannel)
* Constructor.
*
* @param moduleNumber The CAN ID of the PCM.
* @param forwardChannel The forward channel on the module to control.
* @param reverseChannel The reverse channel on the module to control.
* @param forwardChannel The forward channel on the PCM to control (0..7).
* @param reverseChannel The reverse channel on the PCM to control (0..7).
*/
DoubleSolenoid::DoubleSolenoid(uint8_t moduleNumber, uint32_t forwardChannel, uint32_t reverseChannel)
: SolenoidBase (moduleNumber)
@@ -101,7 +101,7 @@ DoubleSolenoid::~DoubleSolenoid()
/**
* Set the value of a solenoid.
*
* @param value Move the solenoid to forward, reverse, or don't move it.
* @param value The value to set (Off, Forward or Reverse)
*/
void DoubleSolenoid::Set(Value value)
{
@@ -138,6 +138,32 @@ DoubleSolenoid::Value DoubleSolenoid::Get()
if (value & m_reverseMask) return kReverse;
return kOff;
}
/**
* Check if the forward solenoid is blacklisted.
* If a solenoid is shorted, it is added to the blacklist and
* disabled until power cycle, or until faults are cleared.
* @see ClearAllPCMStickyFaults()
*
* @return If solenoid is disabled due to short.
*/
bool DoubleSolenoid::IsFwdSolenoidBlackListed()
{
int blackList = GetPCMSolenoidBlackList();
return (blackList & m_forwardMask) ? 1 : 0;
}
/**
* Check if the reverse solenoid is blacklisted.
* If a solenoid is shorted, it is added to the blacklist and
* disabled until power cycle, or until faults are cleared.
* @see ClearAllPCMStickyFaults()
*
* @return If solenoid is disabled due to short.
*/
bool DoubleSolenoid::IsRevSolenoidBlackListed()
{
int blackList = GetPCMSolenoidBlackList();
return (blackList & m_reverseMask) ? 1 : 0;
}
void DoubleSolenoid::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew) {
Value lvalue = kOff;

View File

@@ -116,6 +116,7 @@ void DriverStation::Run()
/**
* Return a pointer to the singleton DriverStation.
* @return Pointer to the DS instance
*/
DriverStation* DriverStation::GetInstance()
{
@@ -146,7 +147,7 @@ void DriverStation::GetData()
/**
* Read the battery voltage.
*
* @return The battery voltage.
* @return The battery voltage in Volts.
*/
float DriverStation::GetBatteryVoltage()
{
@@ -157,6 +158,10 @@ float DriverStation::GetBatteryVoltage()
return voltage;
}
/**
* Reports errors related to unplugged joysticks
* Throttles the errors so that they don't overwhelm the DS
*/
void DriverStation::ReportJoystickUnpluggedError(std::string message) {
double currentTime = Timer::GetFPGATimestamp();
if (currentTime > m_nextMessageTime) {
@@ -165,10 +170,11 @@ void DriverStation::ReportJoystickUnpluggedError(std::string message) {
}
}
/** Returns the number of axis on a given joystick port
/**
* Returns the number of axes on a given joystick port
*
* @param stick The joystick port number
* @return the number of axis on the indicated joystick
* @return The number of axes on the indicated joystick
*/
int DriverStation::GetStickAxisCount(uint32_t stick)
{
@@ -182,10 +188,11 @@ int DriverStation::GetStickAxisCount(uint32_t stick)
return joystickAxes.count;
}
/** Returns the number of POVs on a given joystick port
/**
* Returns the number of POVs on a given joystick port
*
* @param stick The joystick port number
* @return thenumber of POVs on the indicated joystick
* @return The number of POVs on the indicated joystick
*/
int DriverStation::GetStickPOVCount(uint32_t stick)
{
@@ -199,7 +206,8 @@ int DriverStation::GetStickPOVCount(uint32_t stick)
return joystickPOVs.count;
}
/** Returns the number of buttons on a given joystick port
/**
* Returns the number of buttons on a given joystick port
*
* @param stick The joystick port number
* @return The number of buttons on the indicated joystick
@@ -262,7 +270,7 @@ int DriverStation::GetStickPOV(uint32_t stick, uint32_t pov) {
if (stick >= kJoystickPorts)
{
wpi_setWPIError(BadJoystickIndex);
return 0;
return -1;
}
if (pov >= m_joystickPOVs[stick].count)
@@ -271,7 +279,7 @@ int DriverStation::GetStickPOV(uint32_t stick, uint32_t pov) {
wpi_setWPIError(BadJoystickAxis);
else
ReportJoystickUnpluggedError("WARNING: Joystick POV missing, check if all controllers are plugged in\n");
return 0;
return -1;
}
return m_joystickPOVs[stick].povs[pov];
@@ -322,6 +330,10 @@ bool DriverStation::GetStickButton(uint32_t stick, uint8_t button)
return ((0x1 << (button-1)) & m_joystickButtons[stick].buttons) !=0;
}
/**
* Check if the DS has enabled the robot
* @return True if the robot is enabled and the DS is connected
*/
bool DriverStation::IsEnabled()
{
HALControlWord controlWord;
@@ -330,6 +342,10 @@ bool DriverStation::IsEnabled()
return controlWord.enabled && controlWord.dsAttached;
}
/**
* Check if the robot is disabled
* @return True if the robot is explicitly disabled or the DS is not connected
*/
bool DriverStation::IsDisabled()
{
HALControlWord controlWord;
@@ -338,6 +354,10 @@ bool DriverStation::IsDisabled()
return !(controlWord.enabled && controlWord.dsAttached);
}
/**
* Check if the DS is commanding autonomous mode
* @return True if the robot is being commanded to be in autonomous mode
*/
bool DriverStation::IsAutonomous()
{
HALControlWord controlWord;
@@ -346,6 +366,10 @@ bool DriverStation::IsAutonomous()
return controlWord.autonomous;
}
/**
* Check if the DS is commanding teleop mode
* @return True if the robot is being commanded to be in teleop mode
*/
bool DriverStation::IsOperatorControl()
{
HALControlWord controlWord;
@@ -354,6 +378,10 @@ bool DriverStation::IsOperatorControl()
return !(controlWord.autonomous || controlWord.test);
}
/**
* Check if the DS is commanding test mode
* @return True if the robot is being commanded to be in test mode
*/
bool DriverStation::IsTest()
{
HALControlWord controlWord;
@@ -361,6 +389,10 @@ bool DriverStation::IsTest()
return controlWord.test;
}
/**
* Check if the DS is attached
* @return True if the DS is connected to the robot
*/
bool DriverStation::IsDSAttached()
{
HALControlWord controlWord;
@@ -369,6 +401,11 @@ bool DriverStation::IsDSAttached()
return controlWord.dsAttached;
}
/**
* Check if the FPGA outputs are enabled. The outputs may be disabled if the robot is disabled
* or e-stopped, the watchdog has expired, or if the roboRIO browns out.
* @return True if the FPGA outputs are enabled.
*/
bool DriverStation::IsSysActive()
{
int32_t status = 0;
@@ -377,6 +414,10 @@ bool DriverStation::IsSysActive()
return retVal;
}
/**
* Check if the system is browned out.
* @return True if the system is browned out
*/
bool DriverStation::IsSysBrownedOut()
{
int32_t status = 0;
@@ -388,7 +429,7 @@ bool DriverStation::IsSysBrownedOut()
/**
* Has a new control packet from the driver station arrived since the last time this function was called?
* Warning: If you call this function from more than one place at the same time,
* you will not get the get the intended behavior
* you will not get the get the intended behaviour.
* @return True if the control data has been updated since the last call.
*/
bool DriverStation::IsNewControlData()
@@ -398,7 +439,6 @@ bool DriverStation::IsNewControlData()
/**
* Is the driver station attached to a Field Management System?
* Note: This does not work with the Blue DS.
* @return True if the robot is competing on a field being controlled by a Field Management System
*/
bool DriverStation::IsFMSAttached()
@@ -411,7 +451,7 @@ bool DriverStation::IsFMSAttached()
/**
* Return the alliance that the driver station says it is on.
* This could return kRed or kBlue
* @return The Alliance enum
* @return The Alliance enum (kRed, kBlue or kInvalid)
*/
DriverStation::Alliance DriverStation::GetAlliance()
{
@@ -435,7 +475,7 @@ DriverStation::Alliance DriverStation::GetAlliance()
/**
* Return the driver station location on the field
* This could return 1, 2, or 3
* @return The location of the driver station
* @return The location of the driver station (1-3, 0 for invalid)
*/
uint32_t DriverStation::GetLocation()
{
@@ -469,13 +509,12 @@ void DriverStation::WaitForData()
/**
* Return the approximate match time
* The FMS does not currently send the official match time to the robots
* This returns the time since the enable signal sent from the Driver Station
* At the beginning of autonomous, the time is reset to 0.0 seconds
* At the beginning of teleop, the time is reset to +15.0 seconds
* If the robot is disabled, this returns 0.0 seconds
* Warning: This is not an official time (so it cannot be used to argue with referees)
* @return Match time in seconds since the beginning of autonomous
* The FMS does not send an official match time to the robots, but does send an approximate match time.
* The value will count down the time remaining in the current period (auto or teleop).
* Warning: This is not an official time (so it cannot be used to dispute ref calls or guarantee that a function
* will trigger before the match ends)
* The Practice Match function of the DS approximates the behaviour seen on the field.
* @return Time remaining in current match period (auto or teleop)
*/
double DriverStation::GetMatchTime()
{
@@ -491,5 +530,11 @@ double DriverStation::GetMatchTime()
void DriverStation::ReportError(std::string error)
{
std::cout << error << std::endl;
HALSetErrorData(error.c_str(), error.size(), 0);
HALControlWord controlWord;
HALGetControlWord(&controlWord);
if(controlWord.dsAttached)
{
HALSetErrorData(error.c_str(), error.size(), 0);
}
}

View File

@@ -28,11 +28,12 @@ void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType)
{
m_table = NULL;
m_encodingType = encodingType;
int32_t index = 0;
m_index = 0;
switch (encodingType)
{
case k4X:
{
m_encodingScale = 4;
if (m_aSource->StatusIsFatal())
{
CloneError(m_aSource);
@@ -44,28 +45,32 @@ void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType)
return;
}
int32_t status = 0;
int32_t index = 0;
m_encoder = initializeEncoder(m_aSource->GetModuleForRouting(), m_aSource->GetChannelForRouting(),
m_aSource->GetAnalogTriggerForRouting(),
m_bSource->GetModuleForRouting(), m_bSource->GetChannelForRouting(),
m_bSource->GetAnalogTriggerForRouting(),
reverseDirection, &index, &status);
reverseDirection, &m_index, &status);
wpi_setErrorWithContext(status, getHALErrorMessage(status));
m_counter = NULL;
SetMaxPeriod(.5);
break;
}
case k1X:
case k2X:
{
m_encodingScale = encodingType == k1X ? 1 : 2;
m_counter = new Counter(m_encodingType, m_aSource, m_bSource, reverseDirection);
index = m_counter->GetIndex();
m_index = m_counter->GetFPGAIndex();
break;
}
default:
wpi_setErrorWithContext(-1, "Invalid encodingType argument");
break;
}
m_distancePerPulse = 1.0;
m_pidSource = kDistance;
HALReport(HALUsageReporting::kResourceType_Encoder, index, encodingType);
HALReport(HALUsageReporting::kResourceType_Encoder, m_index, encodingType);
LiveWindow::GetInstance()->AddSensor("Encoder", m_aSource->GetChannelForRouting(), this);
}
@@ -75,8 +80,8 @@ void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType)
*
* The counter will start counting immediately.
*
* @param aChannel The a channel digital input channel.
* @param bChannel The b channel digital input channel.
* @param aChannel The a channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
* @param bChannel The b channel DIO channel. 0-9 are on-board, 10-25 are on the MXP port
* @param reverseDirection represents the orientation of the encoder and inverts the output values
* if necessary so forward represents positive values.
* @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
@@ -177,6 +182,12 @@ Encoder::~Encoder()
}
}
/**
* The encoding scale factor 1x, 2x, or 4x, per the requested encodingType.
* Used to divide raw edge counts down to spec'd counts.
*/
int32_t Encoder::GetEncodingScale() { return m_encodingScale; }
/**
* Gets the raw value from the encoder.
* The raw value is the actual count unscaled by the 1x, 2x, or 4x scale
@@ -231,7 +242,7 @@ void Encoder::Reset()
/**
* Returns the period of the most recent pulse.
* Returns the period of the most recent Encoder pulse in seconds.
* This method compenstates for the decoding type.
* This method compensates for the decoding type.
*
* @deprecated Use GetRate() in favor of this method. This returns unscaled periods and GetRate() scales using value from SetDistancePerPulse().
*
@@ -496,6 +507,47 @@ double Encoder::PIDGet()
}
}
/**
* Set the index source for the encoder. When this source is activated, the encoder count automatically resets.
*
* @param channel A DIO channel to set as the encoder index
* @param type The state that will cause the encoder to reset
*/
void Encoder::SetIndexSource(uint32_t channel, Encoder::IndexingType type) {
int32_t status = 0;
bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge);
bool edgeSensitive = (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge);
setEncoderIndexSource(m_encoder, channel, false, activeHigh, edgeSensitive, &status);
wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
}
/**
* Set the index source for the encoder. When this source is activated, the encoder count automatically resets.
*
* @param channel A digital source to set as the encoder index
* @param type The state that will cause the encoder to reset
*/
void Encoder::SetIndexSource(DigitalSource *source, Encoder::IndexingType type) {
int32_t status = 0;
bool activeHigh = (type == kResetWhileHigh) || (type == kResetOnRisingEdge);
bool edgeSensitive = (type == kResetOnFallingEdge) || (type == kResetOnRisingEdge);
setEncoderIndexSource(m_encoder, source->GetChannelForRouting(), source->GetAnalogTriggerForRouting(), activeHigh,
edgeSensitive, &status);
wpi_setGlobalErrorWithContext(status, getHALErrorMessage(status));
}
/**
* Set the index source for the encoder. When this source is activated, the encoder count automatically resets.
*
* @param channel A digital source to set as the encoder index
* @param type The state that will cause the encoder to reset
*/
void Encoder::SetIndexSource(DigitalSource &source, Encoder::IndexingType type) {
SetIndexSource(&source, type);
}
void Encoder::UpdateTable() {
if (m_table != NULL) {
m_table->PutNumber("Speed", GetRate());

View File

@@ -23,8 +23,8 @@ void GearTooth::EnableDirectionSensing(bool directionSensitive)
/**
* Construct a GearTooth sensor given a channel.
*
* @param channel The GPIO channel that the sensor is connected to.
* @param directionSensitive Enable the pulse length decoding in hardware to specify count direction.
* @param channel The DIO channel that the sensor is connected to. 0-9 are on-board, 10-25 are on the MXP.
* @param directionSensitive True to enable the pulse length decoding in hardware to specify count direction.
*/
GearTooth::GearTooth(uint32_t channel, bool directionSensitive)
: Counter(channel)
@@ -35,10 +35,10 @@ GearTooth::GearTooth(uint32_t channel, bool directionSensitive)
/**
* Construct a GearTooth sensor given a digital input.
* This should be used when sharing digial inputs.
* This should be used when sharing digital inputs.
*
* @param source An object that fully descibes the input that the sensor is connected to.
* @param directionSensitive Enable the pulse length decoding in hardware to specify count direction.
* @param source A pointer to the existing DigitalSource object (such as a DigitalInput)
* @param directionSensitive True to enable the pulse length decoding in hardware to specify count direction.
*/
GearTooth::GearTooth(DigitalSource *source, bool directionSensitive)
: Counter(source)
@@ -46,6 +46,13 @@ GearTooth::GearTooth(DigitalSource *source, bool directionSensitive)
EnableDirectionSensing(directionSensitive);
}
/**
* Construct a GearTooth sensor given a digital input.
* This should be used when sharing digital inputs.
*
* @param source A reference to the existing DigitalSource object (such as a DigitalInput)
* @param directionSensitive True to enable the pulse length decoding in hardware to specify count direction.
*/
GearTooth::GearTooth(DigitalSource &source, bool directionSensitive): Counter(source)
{
EnableDirectionSensing(directionSensitive);

View File

@@ -19,8 +19,8 @@ constexpr float Gyro::kDefaultVoltsPerDegreePerSecond;
/**
* Initialize the gyro.
* Calibrate the gyro by running for a number of samples and computing the center value for this
* part. Then use the center value as the Accumulator center value for subsequent measurements.
* Calibrate the gyro by running for a number of samples and computing the center value.
* Then use the center value as the Accumulator center value for subsequent measurements.
* It's important to make sure that the robot is not moving while the centering calculations are
* in progress, this is typically done when the robot is first turned on while it's sitting at
* rest before the competition starts.
@@ -71,9 +71,10 @@ void Gyro::InitGyro()
}
/**
* Gyro constructor with only a channel..
* Gyro constructor using the Analog Input channel number.
*
* @param channel The analog channel the gyro is connected to.
* @param channel The analog channel the gyro is connected to. Gyros
can only be used on on-board Analog Inputs 0-1.
*/
Gyro::Gyro(int32_t channel)
{
@@ -83,10 +84,11 @@ Gyro::Gyro(int32_t channel)
}
/**
* Gyro constructor with a precreated analog channel object.
* Use this constructor when the analog channel needs to be shared. There
* is no reference counting when an AnalogInput is passed to the gyro.
* @param channel The AnalogInput object that the gyro is connected to.
* Gyro constructor with a precreated AnalogInput object.
* Use this constructor when the analog channel needs to be shared.
* This object will not clean up the AnalogInput object when using this constructor.
* Gyros can only be used on on-board channels 0-1.
* @param channel A pointer to the AnalogInput object that the gyro is connected to.
*/
Gyro::Gyro(AnalogInput *channel)
{
@@ -102,6 +104,12 @@ Gyro::Gyro(AnalogInput *channel)
}
}
/**
* Gyro constructor with a precreated AnalogInput object.
* Use this constructor when the analog channel needs to be shared.
* This object will not clean up the AnalogInput object when using this constructor
* @param channel A reference to the AnalogInput object that the gyro is connected to.
*/
Gyro::Gyro(AnalogInput &channel)
{
m_analog = &channel;
@@ -133,8 +141,8 @@ Gyro::~Gyro()
*
* The angle is based on the current accumulator value corrected by the oversampling rate, the
* gyro type and the A/D calibration values.
* The angle is continuous, that is can go beyond 360 degrees. This make algorithms that wouldn't
* want to see a discontinuity in the gyro output as it sweeps past 0 on the second time around.
* The angle is continuous, that is it will continue from 360->361 degrees. This allows algorithms that wouldn't
* want to see a discontinuity in the gyro output as it sweeps from 360 to 0 on the second time around.
*
* @return the current heading of the robot in degrees. This heading is based on integration
* of the returned rate from the gyro.
@@ -169,11 +177,12 @@ double Gyro::GetRate( void )
/**
* Set the gyro type based on the sensitivity.
* Set the gyro sensitivity.
* This takes the number of volts/degree/second sensitivity of the gyro and uses it in subsequent
* calculations to allow the code to work with multiple gyros.
* calculations to allow the code to work with multiple gyros. This value is typically found in
* the gyro datasheet.
*
* @param voltsPerDegreePerSecond The type of gyro specified as the voltage that represents one degree/second.
* @param voltsPerDegreePerSecond The sensitivity in Volts/degree/second
*/
void Gyro::SetSensitivity( float voltsPerDegreePerSecond )
{
@@ -193,6 +202,11 @@ void Gyro::SetDeadband( float volts ) {
m_analog->SetAccumulatorDeadband(deadband);
}
/**
* Sets the type of output to use with the WPILib PID class
* The gyro supports using either rate or angle for PID calculations
*/
void Gyro::SetPIDSourceParameter(PIDSourceParameter pidSource)
{
if(pidSource == 0 || pidSource > 2)
@@ -201,9 +215,10 @@ void Gyro::SetPIDSourceParameter(PIDSourceParameter pidSource)
}
/**
* Get the angle in degrees for the PIDSource base object.
* Get the PIDOutput for the PIDSource base object. Can be set to return
* angle or rate using SetPIDSourceParameter(). Defaults to angle.
*
* @return The angle in degrees.
* @return The PIDOutput (angle or rate, defaults to angle)
*/
double Gyro::PIDGet()
{

View File

@@ -12,7 +12,7 @@
/**
* Constructor.
*
* @param Port The I2C port to which the device is connected.
* @param port The I2C port to which the device is connected.
* @param deviceAddress The address of the device on the I2C bus.
*/
I2C::I2C(Port port, uint8_t deviceAddress) :

View File

@@ -101,7 +101,9 @@ void InterruptableSensorBase::CancelInterrupts()
}
/**
* In synchronous mode, wait for the defined interrupt to occur.
* In synchronous mode, wait for the defined interrupt to occur. You should <b>NOT</b> attempt to read the
* sensor from another thread while waiting for an interrupt. This is not threadsafe, and can cause
* memory corruption
* @param timeout Timeout in seconds
* @param ignorePrevious If true, ignore interrupts that happened before
* WaitForInterrupt was called.

View File

@@ -44,10 +44,7 @@ void IterativeRobot::Prestart() {
/**
* Provide an alternate "main loop" via StartCompetition().
*
* This specific StartCompetition() implements "main loop" behavior like that of the FRC
* control system in 2008 and earlier, with a primary (slow) loop that is
* called periodically, and a "fast loop" (a.k.a. "spin loop") that is
* called as fast as possible with no delay between calls.
* This specific StartCompetition() implements "main loop" behaviour synced with the DS packets
*/
void IterativeRobot::StartCompetition()
{

View File

@@ -14,7 +14,7 @@
*/
void Jaguar::InitJaguar()
{
/*
/**
* Input profile defined by Luminary Micro.
*
* Full reverse ranges from 0.671325ms to 0.6972211ms
@@ -33,7 +33,8 @@ void Jaguar::InitJaguar()
}
/**
* @param channel The PWM channel that the Jaguar is attached to.
* Constructor for a Jaguar connected via PWM
* @param channel The PWM channel that the Jaguar is attached to. 0-9 are on-board, 10-19 are on the MXP port
*/
Jaguar::Jaguar(uint32_t channel) : SafePWM(channel)
{

View File

@@ -24,7 +24,7 @@ static bool joySticksInitialized = false;
* Construct an instance of a joystick.
* The joystick index is the usb port on the drivers station.
*
* @param port The port on the driver station that the joystick is plugged into.
* @param port The port on the driver station that the joystick is plugged into (0-5).
*/
Joystick::Joystick(uint32_t port)
: m_ds (NULL)
@@ -221,12 +221,12 @@ bool Joystick::GetBumper(JoystickHand hand)
}
/**
* Get the button value for buttons 1 through 12.
* Get the button value (starting at button 1)
*
* The buttons are returned in a single 16 bit value with one bit representing the state
* of each button. The appropriate button is returned as a boolean value.
*
* @param button The button number to be read.
* @param button The button number to be read (starting at 1)
* @return The state of the button.
**/
bool Joystick::GetRawButton(uint32_t button)
@@ -237,6 +237,7 @@ bool Joystick::GetRawButton(uint32_t button)
/**
* Get the state of a POV on the joystick.
*
* @param pov The index of the POV to read (starting at 0)
* @return the angle of the POV in degrees, or -1 if the POV is not pressed.
*/
int Joystick::GetPOV(uint32_t pov) {

View File

@@ -56,7 +56,7 @@ MotorSafetyHelper::~MotorSafetyHelper()
}
}
/*
/**
* Feed the motor safety object.
* Resets the timer on this object that is used to do the timeouts.
*/
@@ -66,7 +66,7 @@ void MotorSafetyHelper::Feed()
m_stopTime = Timer::GetFPGATimestamp() + m_expiration;
}
/*
/**
* Set the expiration time for the corresponding motor safety object.
* @param expirationTime The timeout value in seconds.
*/
@@ -78,7 +78,7 @@ void MotorSafetyHelper::SetExpiration(float expirationTime)
/**
* Retrieve the timeout value for the corresponding motor safety object.
* @returns the timeout value in seconds.
* @return the timeout value in seconds.
*/
float MotorSafetyHelper::GetExpiration()
{
@@ -88,7 +88,7 @@ float MotorSafetyHelper::GetExpiration()
/**
* Determine if the motor is still operating or has timed out.
* @returns a true value if the motor is still operating normally and hasn't timed out.
* @return a true value if the motor is still operating normally and hasn't timed out.
*/
bool MotorSafetyHelper::IsAlive()
{
@@ -133,7 +133,7 @@ void MotorSafetyHelper::SetSafetyEnabled(bool enabled)
/**
* Return the state of the motor safety enabled flag
* Return if the motor safety is currently enabled for this devicce.
* @returns True if motor safety is enforced for this device
* @return True if motor safety is enforced for this device
*/
bool MotorSafetyHelper::IsSafetyEnabled()
{

Some files were not shown because too many files have changed in this diff Show More