mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-05 03:21:42 +00:00
Compare commits
108 Commits
jenkins-re
...
jenkins-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e46592ad1 | ||
|
|
d2edb80da2 | ||
|
|
6159fde98e | ||
|
|
7bdd91a058 | ||
|
|
605bb45e0c | ||
|
|
215002e487 | ||
|
|
964475c724 | ||
|
|
bfa4bbaf78 | ||
|
|
39158754d7 | ||
|
|
048b02e6cd | ||
|
|
130319b771 | ||
|
|
0b414b1bde | ||
|
|
8a244cc9dc | ||
|
|
397162390b | ||
|
|
bbc216f998 | ||
|
|
72a56e6a8a | ||
|
|
102c992e50 | ||
|
|
9f057fddfd | ||
|
|
e614217d41 | ||
|
|
9b7042a51a | ||
|
|
081d7f7361 | ||
|
|
352c021b17 | ||
|
|
5f90bf167e | ||
|
|
445eafefb3 | ||
|
|
a917f63e7d | ||
|
|
2228361f39 | ||
|
|
aaa599fa28 | ||
|
|
5d95bbb33f | ||
|
|
d0258923e8 | ||
|
|
91ccc5d10b | ||
|
|
9fda920055 | ||
|
|
e9dd72ac77 | ||
|
|
cce1bbc1f2 | ||
|
|
9fc79c41d1 | ||
|
|
1dba916887 | ||
|
|
ce7a56284f | ||
|
|
34d515debb | ||
|
|
cff103d15e | ||
|
|
1b5cabc0d1 | ||
|
|
9fcfc5b3b7 | ||
|
|
f181c70134 | ||
|
|
46c40da539 | ||
|
|
1c05a982e1 | ||
|
|
ba7f56833b | ||
|
|
7600473da6 | ||
|
|
9f04b79580 | ||
|
|
2282a11a79 | ||
|
|
1a2344bddc | ||
|
|
ae9a7d19f1 | ||
|
|
e1141b9f27 | ||
|
|
ff71aca572 | ||
|
|
837671e95f | ||
|
|
38c0308fc7 | ||
|
|
f21e4a9da5 | ||
|
|
e13720bb94 | ||
|
|
0cf7d6f457 | ||
|
|
a2dfffeddc | ||
|
|
87e1df068c | ||
|
|
f19c290fde | ||
|
|
5c342a8e1a | ||
|
|
f7117aac4b | ||
|
|
05cd0627b2 | ||
|
|
2bc83bd80a | ||
|
|
79fbbbc0dd | ||
|
|
359a155915 | ||
|
|
fb7f5e9de8 | ||
|
|
7db63055f6 | ||
|
|
b94341a23e | ||
|
|
afbd70d393 | ||
|
|
ceccd95084 | ||
|
|
91b9812482 | ||
|
|
0dbc40e515 | ||
|
|
5ce89b3382 | ||
|
|
8ccf3d9c14 | ||
|
|
230fa2d168 | ||
|
|
d165f57e6e | ||
|
|
2d15b6b03e | ||
|
|
5d9baa44ee | ||
|
|
822416d2f7 | ||
|
|
3fe726d851 | ||
|
|
4c0b2c18ab | ||
|
|
b4c5a3af77 | ||
|
|
eddb0b20b0 | ||
|
|
6d8e782f53 | ||
|
|
0368322385 | ||
|
|
19e05ff52b | ||
|
|
a6aef54ef4 | ||
|
|
32f3bea465 | ||
|
|
9aacf5eb29 | ||
|
|
f3484686c9 | ||
|
|
b78b14bf5f | ||
|
|
c08dc98650 | ||
|
|
9fbffd88cb | ||
|
|
6c15a3600a | ||
|
|
548941dd99 | ||
|
|
9d8418c14e | ||
|
|
326bf4fc9c | ||
|
|
3c4a1d9a1a | ||
|
|
7687028269 | ||
|
|
a55f34646d | ||
|
|
53473e21be | ||
|
|
b4097fbd58 | ||
|
|
3b72114555 | ||
|
|
cc36454b90 | ||
|
|
aae20ddbff | ||
|
|
ffd9061766 | ||
|
|
9ffdea188b | ||
|
|
198e2eed14 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}}/src""/>
|
||||
<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"/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Binary file not shown.
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}"
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(){
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
|
||||
/** @file
|
||||
* Contains global utility functions
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
bool GetDirection();
|
||||
void SetSamplesToAverage(int samplesToAverage);
|
||||
int GetSamplesToAverage();
|
||||
uint32_t GetIndex()
|
||||
uint32_t GetFPGAIndex()
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
111
wpilibc/wpilibC++Devices/include/USBCamera.h
Normal file
111
wpilibc/wpilibC++Devices/include/USBCamera.h
Normal 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);
|
||||
};
|
||||
127
wpilibc/wpilibC++Devices/include/Vision/AxisCamera.h
Normal file
127
wpilibc/wpilibC++Devices/include/Vision/AxisCamera.h
Normal 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);
|
||||
};
|
||||
57
wpilibc/wpilibC++Devices/include/Vision/BaeUtilities.h
Normal file
57
wpilibc/wpilibC++Devices/include/Vision/BaeUtilities.h
Normal 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);
|
||||
|
||||
39
wpilibc/wpilibC++Devices/include/Vision/BinaryImage.h
Normal file
39
wpilibc/wpilibC++Devices/include/Vision/BinaryImage.h
Normal 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);
|
||||
};
|
||||
|
||||
65
wpilibc/wpilibC++Devices/include/Vision/ColorImage.h
Normal file
65
wpilibc/wpilibC++Devices/include/Vision/ColorImage.h
Normal 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);
|
||||
};
|
||||
|
||||
30
wpilibc/wpilibC++Devices/include/Vision/FrcError.h
Normal file
30
wpilibc/wpilibC++Devices/include/Vision/FrcError.h
Normal 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);
|
||||
|
||||
22
wpilibc/wpilibC++Devices/include/Vision/HSLImage.h
Normal file
22
wpilibc/wpilibC++Devices/include/Vision/HSLImage.h
Normal 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();
|
||||
};
|
||||
|
||||
|
||||
27
wpilibc/wpilibC++Devices/include/Vision/ImageBase.h
Normal file
27
wpilibc/wpilibC++Devices/include/Vision/ImageBase.h
Normal 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;
|
||||
};
|
||||
|
||||
25
wpilibc/wpilibC++Devices/include/Vision/MonoImage.h
Normal file
25
wpilibc/wpilibC++Devices/include/Vision/MonoImage.h
Normal 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);
|
||||
};
|
||||
|
||||
21
wpilibc/wpilibC++Devices/include/Vision/RGBImage.h
Normal file
21
wpilibc/wpilibC++Devices/include/Vision/RGBImage.h
Normal 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();
|
||||
};
|
||||
|
||||
28
wpilibc/wpilibC++Devices/include/Vision/Threshold.h
Normal file
28
wpilibc/wpilibC++Devices/include/Vision/Threshold.h
Normal 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);
|
||||
};
|
||||
|
||||
148
wpilibc/wpilibC++Devices/include/Vision/VisionAPI.h
Normal file
148
wpilibc/wpilibC++Devices/include/Vision/VisionAPI.h
Normal 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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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) :
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user