mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-06 03:31:43 +00:00
Compare commits
259 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 | ||
|
|
46dc99a115 | ||
|
|
91d714d2e9 | ||
|
|
9a28aaaa7c | ||
|
|
96a76ba89e | ||
|
|
d26059a4fb | ||
|
|
ee0d835304 | ||
|
|
6080a3b186 | ||
|
|
3d06a763a2 | ||
|
|
e1480ec798 | ||
|
|
a5d9ba412c | ||
|
|
2434515d41 | ||
|
|
741618250b | ||
|
|
8b8d7e77cd | ||
|
|
c093a553ee | ||
|
|
a1375e58cd | ||
|
|
15ff7f5038 | ||
|
|
c17ba98f72 | ||
|
|
dee755ab19 | ||
|
|
92c54f5f5d | ||
|
|
1fde00643f | ||
|
|
47443b4e1e | ||
|
|
f01e5b5570 | ||
|
|
198e2eed14 | ||
|
|
22c4207553 | ||
|
|
bea9eb0efa | ||
|
|
b72eb4b812 | ||
|
|
0d8c454727 | ||
|
|
d61d491a02 | ||
|
|
786e844a9f | ||
|
|
170b5860ee | ||
|
|
26c50ebe02 | ||
|
|
46c659d6b6 | ||
|
|
6fdd491081 | ||
|
|
fe4535dfa0 | ||
|
|
636e2e13ad | ||
|
|
d3f5b74668 | ||
|
|
8116bbd15b | ||
|
|
37052246a5 | ||
|
|
a649d3b553 | ||
|
|
6a7e7cf611 | ||
|
|
77997e52fb | ||
|
|
e655072efc | ||
|
|
0427fc34c4 | ||
|
|
e33d80be14 | ||
|
|
8381eee185 | ||
|
|
1c24096cc9 | ||
|
|
3a684d28b2 | ||
|
|
8786b242b2 | ||
|
|
b29a4bebf2 | ||
|
|
db0b421019 | ||
|
|
8efe998270 | ||
|
|
ac60198842 | ||
|
|
8a5ee71fd8 | ||
|
|
af4ce1074a | ||
|
|
7636041393 | ||
|
|
745489fec7 | ||
|
|
04f9ca4feb | ||
|
|
ca5dfbe492 | ||
|
|
07619a37a0 | ||
|
|
34d3d756ea | ||
|
|
61a5fcce18 | ||
|
|
82c4563d34 | ||
|
|
fa337bc747 | ||
|
|
8ae7e973f2 | ||
|
|
574f2e692a | ||
|
|
827341caa2 | ||
|
|
dd272e6bcb | ||
|
|
3bdaa63a28 | ||
|
|
41b393c210 | ||
|
|
11cf860ecd | ||
|
|
2168d2cb77 | ||
|
|
430722c4a3 | ||
|
|
497f38fe0e | ||
|
|
9f2dcdeab6 | ||
|
|
ac07142e4c | ||
|
|
19a7243bfc | ||
|
|
e3ac0b628c | ||
|
|
709a88ad68 | ||
|
|
6b844b52ec | ||
|
|
9056edf932 | ||
|
|
ff2ea1287d | ||
|
|
b41690b387 | ||
|
|
ce8e65d41e | ||
|
|
66622b43e7 | ||
|
|
568b842c73 | ||
|
|
4d142cdafa | ||
|
|
2ae8f40a58 | ||
|
|
4833316571 | ||
|
|
16f9db30a9 | ||
|
|
e092742f40 | ||
|
|
4f6fa2482b | ||
|
|
52408e2658 | ||
|
|
d986ffac81 | ||
|
|
bc3c5447e7 | ||
|
|
b125e6b40a | ||
|
|
88a043bda4 | ||
|
|
c57d01af94 | ||
|
|
6abde412c1 | ||
|
|
5e6cd0bf9e | ||
|
|
ec03c3068d | ||
|
|
45f3b76103 | ||
|
|
dac04cb4a2 | ||
|
|
7d026be264 | ||
|
|
5893d28f39 | ||
|
|
cd01945908 | ||
|
|
36c53667cd | ||
|
|
ea610eb302 | ||
|
|
d04476bb2f | ||
|
|
2bb0a32c15 | ||
|
|
cdbe80315f | ||
|
|
517e708fd8 | ||
|
|
70825be690 | ||
|
|
43532198c7 | ||
|
|
4da9ebe1fd | ||
|
|
9479793e1d | ||
|
|
20e9f499b0 | ||
|
|
3d897cef58 | ||
|
|
fa229f2b13 | ||
|
|
b1056cf6d7 | ||
|
|
8e707169a1 | ||
|
|
47961c8b13 | ||
|
|
b59f4141c4 | ||
|
|
d62d82b28b | ||
|
|
a9d30c0389 | ||
|
|
6e0c84d942 | ||
|
|
bb8ea17acf | ||
|
|
c683e24aa9 | ||
|
|
7b371f6d7c | ||
|
|
f6de7bc961 | ||
|
|
c00d9f1523 | ||
|
|
6ec2d262c8 | ||
|
|
3c8b31608c | ||
|
|
29f36b0eac | ||
|
|
486885e8bf | ||
|
|
020d97580a | ||
|
|
28a41e4ac2 | ||
|
|
f590cda8f9 | ||
|
|
c98f54dbbc | ||
|
|
81840b2c49 | ||
|
|
78ccb48fd3 | ||
|
|
b9913d3e12 | ||
|
|
56430ccd7c | ||
|
|
d412584f16 | ||
|
|
364a3afad4 | ||
|
|
97456f40f7 | ||
|
|
7e5ed03d28 | ||
|
|
14a1e6ae8e | ||
|
|
529f5b773f | ||
|
|
91c70daf5b | ||
|
|
e86312cd6f | ||
|
|
d7a9794080 | ||
|
|
6234fe06f5 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -10,6 +10,13 @@ bin/
|
||||
.project
|
||||
.classpath
|
||||
**/dependency-reduced-pom.xml
|
||||
wpilibj/wpilibJavaJNI/nivision/*.c
|
||||
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
|
||||
@@ -155,6 +162,10 @@ local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
### Python ###
|
||||
*.pyc
|
||||
__pycache__
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
|
||||
@@ -85,6 +85,14 @@
|
||||
<outputDirectory>${tools-zip}</outputDirectory>
|
||||
<destFileName>SmartDashboard.jar</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>edu.wpi.first.wpilib</groupId>
|
||||
<artifactId>sfx</artifactId>
|
||||
<type>zip</type>
|
||||
<classifier>zip</classifier>
|
||||
<outputDirectory>${tools-zip}/../</outputDirectory>
|
||||
<destFileName>sfx.zip</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>edu.wpi.first.wpilib.networktables</groupId>
|
||||
<artifactId>OutlineViewer</artifactId>
|
||||
@@ -92,6 +100,13 @@
|
||||
<outputDirectory>${tools-zip}</outputDirectory>
|
||||
<destFileName>OutlineViewer.jar</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>edu.wpi.first.wpilib</groupId>
|
||||
<artifactId>java-installer</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<outputDirectory>${tools-zip}</outputDirectory>
|
||||
<destFileName>java-installer.jar</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
@@ -136,6 +151,7 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<unzip src="${tools-zip}/../sfx.zip" dest="${tools-zip}"/>
|
||||
<zip destfile="${project.build.directory}/classes/resources/tools.zip"
|
||||
basedir="${tools-zip}"
|
||||
update="true" />
|
||||
@@ -201,6 +217,12 @@
|
||||
<artifactId>SmartDashboard</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.wpilib</groupId>
|
||||
<artifactId>sfx</artifactId>
|
||||
<type>zip</type>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.wpi.first.wpilib.networktables</groupId>
|
||||
<artifactId>OutlineViewer</artifactId>
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package edu.wpi.first.wpilib.plugins.core.actions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.debug.core.DebugPlugin;
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.ui.IWorkbenchWindow;
|
||||
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
|
||||
|
||||
import edu.wpi.first.wpilib.plugins.core.WPILibCore;
|
||||
|
||||
/**
|
||||
* Our sample action implements workbench action delegate.
|
||||
* The action proxy will be created by the workbench and
|
||||
* shown in the UI. When the user tries to use the action,
|
||||
* this delegate will be created and execution will be
|
||||
* delegated to it.
|
||||
* @see IWorkbenchWindowActionDelegate
|
||||
*/
|
||||
public class RunSimulationSmartDashboardAction implements IWorkbenchWindowActionDelegate {
|
||||
/**
|
||||
* The constructor.
|
||||
*/
|
||||
public RunSimulationSmartDashboardAction() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The action has been activated. The argument of the
|
||||
* method represents the 'real' action sitting
|
||||
* in the workbench UI.
|
||||
* @see IWorkbenchWindowActionDelegate#run
|
||||
*/
|
||||
public void run(IAction action) {
|
||||
File dir = new File(WPILibCore.getDefault().getWPILibBaseDir()+File.separator+"tools");
|
||||
File[] files = dir.listFiles(new FilenameFilter() {
|
||||
@Override public boolean accept(File dir, String name) {
|
||||
return name.startsWith("SmartDashboard") && name.endsWith(".jar");
|
||||
}
|
||||
});
|
||||
if (files == null || files.length < 1) return;
|
||||
String[] cmd = {"java", "-jar", files[0].getAbsolutePath(), "-ip", "localhost"};
|
||||
try {
|
||||
DebugPlugin.exec(cmd, new File(System.getProperty("user.home")));
|
||||
} catch (CoreException e) {
|
||||
WPILibCore.logError("Error running SmartDashboard.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selection in the workbench has been changed. We
|
||||
* can change the state of the 'real' action here
|
||||
* if we want, but this can only happen after
|
||||
* the delegate has been created.
|
||||
* @see IWorkbenchWindowActionDelegate#selectionChanged
|
||||
*/
|
||||
public void selectionChanged(IAction action, ISelection selection) {
|
||||
}
|
||||
|
||||
/**
|
||||
* We can use this method to dispose of any system
|
||||
* resources we previously allocated.
|
||||
* @see IWorkbenchWindowActionDelegate#dispose
|
||||
*/
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
/**
|
||||
* We will cache window object in order to
|
||||
* be able to provide parent shell for the message dialog.
|
||||
* @see IWorkbenchWindowActionDelegate#init
|
||||
*/
|
||||
public void init(IWorkbenchWindow window) {
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,6 @@ import java.io.OutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
@@ -17,6 +15,7 @@ import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.debug.core.DebugPlugin;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
|
||||
import edu.wpi.first.wpilib.plugins.core.WPILibCore;
|
||||
|
||||
@@ -117,7 +116,7 @@ public abstract class AbstractInstaller {
|
||||
protected void install() throws InstallException {
|
||||
if(installLocation.exists()) {
|
||||
if(!removeFileHandler(installLocation, true)) {
|
||||
JOptionPane.showMessageDialog(null,
|
||||
MessageDialog.openError(null, "Error",
|
||||
String.format("Could not update the old wpilib folder.%n"
|
||||
+ "Please close any WPILib tools and restart Eclipse."));
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.debug.core.DebugPlugin;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
|
||||
import edu.wpi.first.wpilib.plugins.core.WPILibCore;
|
||||
|
||||
@@ -21,8 +20,8 @@ public class SimulationNotification {
|
||||
|
||||
public static void showUnsupported() {
|
||||
String title = "Simulation Unsupported";
|
||||
String message = SIMULATION_UNSUPPORTED_MSG;
|
||||
JOptionPane.showMessageDialog(null, message, title, JOptionPane.OK_OPTION);
|
||||
String message = SIMULATION_UNSUPPORTED_MSG;
|
||||
MessageDialog.openError(null, title, message);
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(URL));
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.Arrays;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionListener;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.widgets.Combo;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
@@ -48,4 +49,8 @@ public class ProjectComboField {
|
||||
combo.addModifyListener(modifyListener);
|
||||
}
|
||||
|
||||
public void addSelectionListener(SelectionListener selectionListener) {
|
||||
combo.addSelectionListener(selectionListener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@
|
||||
<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/sdformat-2.0"/>
|
||||
<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"/>
|
||||
<option id="gnu.cpp.compiler.option.debugging.level.937474733" name="Debug Level" superClass="gnu.cpp.compiler.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
|
||||
|
||||
@@ -23,7 +23,8 @@ private:
|
||||
|
||||
void AutonomousInit()
|
||||
{
|
||||
autonomousCommand->Start();
|
||||
if (autonomousCommand != NULL)
|
||||
autonomousCommand->Start();
|
||||
}
|
||||
|
||||
void AutonomousPeriodic()
|
||||
@@ -37,7 +38,8 @@ private:
|
||||
// teleop starts running. If you want the autonomous to
|
||||
// continue until interrupted by another command, remove
|
||||
// this line or comment it out.
|
||||
autonomousCommand->Cancel();
|
||||
if (autonomousCommand != NULL)
|
||||
autonomousCommand->Cancel();
|
||||
}
|
||||
|
||||
void TeleopPeriodic()
|
||||
|
||||
@@ -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);
|
||||
@@ -1,40 +1,37 @@
|
||||
#include "WPILib.h"
|
||||
|
||||
/**
|
||||
* Uses IMAQdx to manually acquire a new image each frame, and annotate the image by drawing
|
||||
* 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 IntermediateVisionRobot : public SampleRobot
|
||||
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
|
||||
IMAQdxOpenCamera("cam1", IMAQdxCameraControlModeController, &session);
|
||||
IMAQdxConfigureGrab(session);
|
||||
|
||||
// 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 {
|
||||
// acquire images
|
||||
IMAQdxStartAcquisition(session);
|
||||
|
||||
// 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()) {
|
||||
IMAQdxGrab(session, frame, true, NULL);
|
||||
|
||||
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);
|
||||
}
|
||||
// stop image acquisition
|
||||
IMAQdxStopAcquisition(session);
|
||||
}
|
||||
};
|
||||
|
||||
START_ROBOT_CLASS(IntermediateVisionRobot);
|
||||
START_ROBOT_CLASS(AxisCameraSample);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "WPILib.h"
|
||||
|
||||
/**
|
||||
* This sample shows how to use the new CANTalon to just run a motor in a basic
|
||||
* throttle mode, in the same manner as you might control a traditional PWM
|
||||
* controlled motor.
|
||||
*
|
||||
*/
|
||||
class Robot : public SampleRobot {
|
||||
CANTalon m_motor;
|
||||
|
||||
Joystick m_stick;
|
||||
|
||||
// update every 0.01 seconds/10 milliseconds.
|
||||
// The talon only receives control packets every 10ms.
|
||||
double kUpdatePeriod = 0.010;
|
||||
|
||||
public:
|
||||
Robot()
|
||||
: m_motor(1), // Initialize the Talon as device 1. Use the roboRIO web
|
||||
// interface to change the device number on the talons.
|
||||
m_stick(0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* Runs the motor from the output of a Joystick.
|
||||
*/
|
||||
void OperatorControl() {
|
||||
while (IsOperatorControl() && IsEnabled()) {
|
||||
// Takes a number from -1.0 (full reverse) to +1.0 (full forwards).
|
||||
m_motor.Set(m_stick.GetY());
|
||||
|
||||
Wait(kUpdatePeriod); // Wait a bit so that the loop doesn't lock everything up.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
START_ROBOT_CLASS(Robot);
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "WPILib.h"
|
||||
|
||||
/**
|
||||
* This is a quick sample program to show how to use the new Talon SRX over CAN.
|
||||
* This particular sample demonstrates running a basic PID control loop with an
|
||||
* analog potentiometer.
|
||||
*
|
||||
*/
|
||||
class Robot : public SampleRobot {
|
||||
CANTalon m_motor;
|
||||
|
||||
public:
|
||||
Robot()
|
||||
: m_motor(1) // Initialize the Talon as device 1. Use the roboRIO web
|
||||
// interface to change the device number on the m_motors.
|
||||
{
|
||||
// This sets the mode of the m_motor. The options are:
|
||||
// kPercentVbus: basic throttle; no closed-loop.
|
||||
// kCurrent: Runs the motor with the specified current if possible.
|
||||
// kSpeed: Runs a PID control loop to keep the motor going at a constant
|
||||
// speed using the specified sensor.
|
||||
// kPosition: Runs a PID control loop to move the motor to a specified move
|
||||
// the motor to a specified sensor position.
|
||||
// kVoltage: Runs the m_motor at a constant voltage, if possible.
|
||||
// kFollower: The m_motor will run at the same throttle as the specified other talon.
|
||||
m_motor.SetControlMode(CANSpeedController::kPosition);
|
||||
// This command allows you to specify which feedback device to use when doing
|
||||
// closed-loop control. The options are:
|
||||
// AnalogPot: Basic analog potentiometer
|
||||
// QuadEncoder: Quadrature Encoder
|
||||
// AnalogEncoder: Analog Encoder
|
||||
// EncRising: Counts the rising edges of the QuadA pin (allows use of a
|
||||
// non-quadrature encoder)
|
||||
// EncFalling: Same as EncRising, but counts on falling edges.
|
||||
m_motor.SetFeedbackDevice(CANTalon::AnalogPot);
|
||||
// This sets the basic P, I , and D values (F, Izone, and rampRate can also
|
||||
// be set, but are ignored here).
|
||||
// These must all be positive floating point numbers (SetSensorDirection will
|
||||
// multiply the sensor values by negative one in case your sensor is flipped
|
||||
// relative to your motor).
|
||||
// These values are in units of throttle / sensor_units where throttle ranges
|
||||
// from -1023 to +1023 and sensor units are from 0 - 1023 for analog
|
||||
// potentiometers, encoder ticks for encoders, and position / 10ms for
|
||||
// speeds.
|
||||
m_motor.SetPID(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the motor from the output of a Joystick.
|
||||
*/
|
||||
void OperatorControl() {
|
||||
while (IsOperatorControl() && IsEnabled()) {
|
||||
// In closed loop mode, this sets the goal in the units mentioned above.
|
||||
// Since we are using an analog potentiometer, this will try to go to
|
||||
// the middle of the potentiometer range.
|
||||
m_motor.Set(512);
|
||||
|
||||
Wait(5.0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
START_ROBOT_CLASS(Robot);
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "WPILib.h"
|
||||
|
||||
/**
|
||||
* Uses IMAQdx 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 IntermediateVisionRobot : public SampleRobot
|
||||
{
|
||||
IMAQdxSession session;
|
||||
Image *frame;
|
||||
IMAQdxError imaqError;
|
||||
|
||||
public:
|
||||
void RobotInit() override {
|
||||
// create an image
|
||||
frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
|
||||
//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");
|
||||
}
|
||||
imaqError = IMAQdxConfigureGrab(session);
|
||||
if(imaqError != IMAQdxErrorSuccess) {
|
||||
DriverStation::ReportError("IMAQdxConfigureGrab error: " + std::to_string((long)imaqError) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void OperatorControl() override {
|
||||
// acquire images
|
||||
IMAQdxStartAcquisition(session);
|
||||
|
||||
// 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()) {
|
||||
IMAQdxGrab(session, frame, true, NULL);
|
||||
if(imaqError != IMAQdxErrorSuccess) {
|
||||
DriverStation::ReportError("IMAQdxGrab error: " + std::to_string((long)imaqError) + "\n");
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
START_ROBOT_CLASS(IntermediateVisionRobot);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -128,6 +128,40 @@
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>CAN Talon SRX</name>
|
||||
<description>Quick demo of running the SRX at a given throttle value.</description>
|
||||
<tags>
|
||||
<tag>Robot and Motor</tag>
|
||||
<tag>Digital</tag>
|
||||
<tag>Actuators</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/CANTalon/src/Robot.cpp" destination="src/Robot.cpp"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>CAN Talon SRX PID</name>
|
||||
<description>Quick demo of running the SRX with a PID loop.</description>
|
||||
<tags>
|
||||
<tag>Robot and Motor</tag>
|
||||
<tag>Digital</tag>
|
||||
<tag>Actuators</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/CANTalonPID/src/Robot.cpp" destination="src/Robot.cpp"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Relay</name>
|
||||
<description>Demonstrate controlling a Relay from Joystick buttons.</description>
|
||||
@@ -247,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>
|
||||
@@ -264,7 +298,7 @@
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>Getting Started</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.
|
||||
@@ -282,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>
|
||||
|
||||
@@ -43,8 +43,8 @@ public class RSEUtils {
|
||||
RSECorePlugin.getTheCoreRegistry().getSystemTypeById("org.eclipse.rse.systemtype.ssh");
|
||||
host = registry.createHost(profile.getName(), systemType, connectionName, hostName,
|
||||
"The remote target for debugging the robot for team "+teamNumber+".");
|
||||
host.setDefaultUserId("admin");
|
||||
SystemSignonInformation info = new SystemSignonInformation(hostName, "admin",
|
||||
host.setDefaultUserId("lvuser");
|
||||
SystemSignonInformation info = new SystemSignonInformation(hostName, "lvuser",
|
||||
"", systemType);
|
||||
PasswordPersistenceManager.getInstance().add(info, true, false);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Deployment information
|
||||
username=admin
|
||||
username=lvuser
|
||||
password=
|
||||
deploy.dir=/home/lvuser
|
||||
deploy.kill.command=/usr/local/frc/bin/frcKillRobot.sh -t -r
|
||||
@@ -8,7 +8,7 @@ command.dir=/home/lvuser/
|
||||
# Libraries to use
|
||||
wpilib=${user.home}/wpilib/cpp/${cpp-version}
|
||||
wpilib.lib=${wpilib}/lib
|
||||
roboRIOAllowedImages=18
|
||||
roboRIOAllowedImages=23
|
||||
|
||||
# Ant support
|
||||
wpilib.ant.dir=${wpilib}/ant
|
||||
|
||||
@@ -19,21 +19,37 @@
|
||||
<!-- Targets -->
|
||||
|
||||
<target name="get-target-ip">
|
||||
<property name="ant.enable.asserts" value="true"/>
|
||||
<property name="target" value="roboRIO-${team-number}.local" />
|
||||
<echo>Trying Target: ${target}</echo>
|
||||
<if>
|
||||
<isreachable host="${target}"/>
|
||||
<isreachable host="${target}" timeout="5"/>
|
||||
<then>
|
||||
<echo>roboRIO found via mDNS</echo>
|
||||
</then>
|
||||
<else>
|
||||
<math result="ip.upper" operand1="${team-number}" operation="/" operand2="100" datatype="int"/>
|
||||
<math result="ip.lower" operand1="${team-number}" operation="%" operand2="100" datatype="int"/>
|
||||
<property name="target" value="10.${ip-upper}.${ip.lower}.2"/>
|
||||
<echo>roboRIO not found via mDNS, falling back to static address of ${target}</echo>
|
||||
<assert name="roboRIOFound" message="roboRIO not found, please check that the roboRIO is connected, imaged and that the team number is set properly in Eclipse">
|
||||
<isreachable host="${target}"/>
|
||||
</assert>
|
||||
<var name="target" unset="true"/>
|
||||
<echo> roboRIO not found via mDNS, falling back to static USB</echo>
|
||||
<property name="target" value="172.22.11.2"/>
|
||||
<if>
|
||||
<isreachable host="${target}" timeout="5"/>
|
||||
<then>
|
||||
<echo>roboRIO found via static USB</echo>
|
||||
</then>
|
||||
<else>
|
||||
<var name="target" unset="true"/>
|
||||
<math result="ip.upper" operand1="${team-number}" operation="/" operand2="100" datatype="int"/>
|
||||
<math result="ip.lower" operand1="${team-number}" operation="%" operand2="100" datatype="int"/>
|
||||
<property name="target" value="10.${ip.upper}.${ip.lower}.2"/>
|
||||
<echo>roboRIO not found via USB, falling back to static address of ${target}</echo>
|
||||
<assert name="roboRIOFound" message="roboRIO not found, please check that the roboRIO is connected, imaged and that the team number is set properly in Eclipse">
|
||||
<bool>
|
||||
<isreachable host="${target}" timeout="5"/>
|
||||
</bool>
|
||||
</assert>
|
||||
<echo>roboRIO found via Ethernet static</echo>
|
||||
</else>
|
||||
</if>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
@@ -48,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>
|
||||
@@ -93,7 +118,7 @@
|
||||
|
||||
<target name="dependencies" depends="get-target-ip">
|
||||
<property name="ant.enable.asserts" value="true"/>
|
||||
<post to="http://${target}/nisysapi/server" logfile="sysProps.xml" verbose="false" encoding="UTF-16LE">
|
||||
<post to="http://${target}/nisysapi/server" logfile="sysProps.xml" verbose="false" encoding="UTF-16LE" append="false">
|
||||
<prop name="Function" value="GetPropertiesOfItem"/>
|
||||
<prop name="Plugins" value="nisyscfg"/>
|
||||
<prop name="Items" value="system"/>
|
||||
@@ -105,5 +130,6 @@
|
||||
<contains string="${roboRIOAllowedImages}" substring="${roboRIOImage}"/>
|
||||
</bool>
|
||||
</assert>
|
||||
<echo>roboRIO image version validated</echo>
|
||||
</target>
|
||||
</project>
|
||||
|
||||
Binary file not shown.
@@ -38,7 +38,7 @@ public class Robot extends IterativeRobot {
|
||||
|
||||
public void autonomousInit() {
|
||||
// schedule the autonomous command (example)
|
||||
autonomousCommand.start();
|
||||
if (autonomousCommand != null) autonomousCommand.start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,7 +53,15 @@ public class Robot extends IterativeRobot {
|
||||
// teleop starts running. If you want the autonomous to
|
||||
// continue until interrupted by another command, remove
|
||||
// this line or comment it out.
|
||||
autonomousCommand.cancel();
|
||||
if (autonomousCommand != null) autonomousCommand.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called when the disabled button is hit.
|
||||
* You can use it to reset subsystems before shutting down.
|
||||
*/
|
||||
public void disabledInit(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package $package;
|
||||
|
||||
|
||||
import edu.wpi.first.wpilibj.SpeedController;
|
||||
import edu.wpi.first.wpilibj.CANTalon;
|
||||
import edu.wpi.first.wpilibj.SampleRobot;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
|
||||
/**
|
||||
* This is a short sample program demonstrating how to use the basic throttle
|
||||
* mode of the new CAN Talon.
|
||||
*/
|
||||
public class Robot extends SampleRobot {
|
||||
|
||||
CANTalon motor;
|
||||
|
||||
public Robot() {
|
||||
motor = new CANTalon(1); // Initialize the CanTalonSRX on device 1.
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the motor.
|
||||
*/
|
||||
public void operatorControl() {
|
||||
while (isOperatorControl() && isEnabled()) {
|
||||
// Set the motor's output to half power.
|
||||
// This takes a number from -1 (100% speed in reverse) to +1 (100% speed
|
||||
// going forward)
|
||||
motor.set(0.5);
|
||||
|
||||
Timer.delay(0.01); // Note that the CANTalon only receives updates every
|
||||
// 10ms, so updating more quickly would not gain you
|
||||
// anything.
|
||||
}
|
||||
motor.disable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package $package;
|
||||
|
||||
|
||||
import edu.wpi.first.wpilibj.SpeedController;
|
||||
import edu.wpi.first.wpilibj.CANTalon;
|
||||
import edu.wpi.first.wpilibj.SampleRobot;
|
||||
import edu.wpi.first.wpilibj.Timer;
|
||||
|
||||
/**
|
||||
* This is a short sample program demonstrating how to use the Talon SRX over
|
||||
* CAN to run a closed-loop PID controller with an analog potentiometer.
|
||||
*/
|
||||
public class Robot extends SampleRobot {
|
||||
|
||||
CANTalon motor;
|
||||
|
||||
public Robot() {
|
||||
motor = new CANTalon(1); // Initialize the CanTalonSRX on device 1.
|
||||
|
||||
// This sets the mode of the m_motor. The options are:
|
||||
// PercentVbus: basic throttle; no closed-loop.
|
||||
// Current: Runs the motor with the specified current if possible.
|
||||
// Speed: Runs a PID control loop to keep the motor going at a constant
|
||||
// speed using the specified sensor.
|
||||
// Position: Runs a PID control loop to move the motor to a specified move
|
||||
// the motor to a specified sensor position.
|
||||
// Voltage: Runs the m_motor at a constant voltage, if possible.
|
||||
// Follower: The m_motor will run at the same throttle as the specified
|
||||
// other talon.
|
||||
motor.changeControlMode(CANTalon.ControlMode.Position);
|
||||
// This command allows you to specify which feedback device to use when doing
|
||||
// closed-loop control. The options are:
|
||||
// AnalogPot: Basic analog potentiometer
|
||||
// QuadEncoder: Quadrature Encoder
|
||||
// AnalogEncoder: Analog Encoder
|
||||
// EncRising: Counts the rising edges of the QuadA pin (allows use of a
|
||||
// non-quadrature encoder)
|
||||
// EncFalling: Same as EncRising, but counts on falling edges.
|
||||
motor.setFeedbackDevice(CANTalon.FeedbackDevice.AnalogPot);
|
||||
// This sets the basic P, I , and D values (F, Izone, and rampRate can also
|
||||
// be set, but are ignored here).
|
||||
// These must all be positive floating point numbers (reverseSensor will
|
||||
// multiply the sensor values by negative one in case your sensor is flipped
|
||||
// relative to your motor).
|
||||
// These values are in units of throttle / sensor_units where throttle ranges
|
||||
// from -1023 to +1023 and sensor units are from 0 - 1023 for analog
|
||||
// potentiometers, encoder ticks for encoders, and position / 10ms for
|
||||
// speeds.
|
||||
motor.setPID(1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
public void operatorControl() {
|
||||
while (isOperatorControl() && isEnabled()) {
|
||||
// In closed loop mode, this sets the goal in the units mentioned above.
|
||||
// Since we are using an analog potentiometer, this will try to go to
|
||||
// the middle of the potentiometer range.
|
||||
motor.set(512);
|
||||
|
||||
Timer.delay(5.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* This is a demo program showing the use of the NIVision class to do vision processing.
|
||||
* The image is acquired from the USB Webcam, then a circle is overlayed on it.
|
||||
* The NIVision class supplies dozens of methods for different types of processing.
|
||||
* The resulting image can then be sent to the FRC PC Dashboard with setImage()
|
||||
*/
|
||||
public class Robot extends SampleRobot {
|
||||
int session;
|
||||
Image frame;
|
||||
|
||||
public void robotInit() {
|
||||
|
||||
frame = NIVision.imaqCreateImage(NIVision.ImageType.IMAGE_RGB, 0);
|
||||
|
||||
// the camera name (ex "cam0") can be found through the roborio web interface
|
||||
session = NIVision.IMAQdxOpenCamera("cam0",
|
||||
NIVision.IMAQdxCameraControlMode.CameraControlModeController);
|
||||
NIVision.IMAQdxConfigureGrab(session);
|
||||
}
|
||||
|
||||
public void operatorControl() {
|
||||
NIVision.IMAQdxStartAcquisition(session);
|
||||
|
||||
/**
|
||||
* grab an image, 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()) {
|
||||
|
||||
NIVision.IMAQdxGrab(session, frame, 1);
|
||||
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
|
||||
}
|
||||
NIVision.IMAQdxStopAcquisition(session);
|
||||
}
|
||||
|
||||
public void test() {
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package $package;
|
||||
|
||||
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 CameraServer class.
|
||||
* 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 {
|
||||
|
||||
CameraServer server;
|
||||
|
||||
public Robot() {
|
||||
server = CameraServer.getInstance();
|
||||
server.setQuality(50);
|
||||
//the camera name (ex "cam0") can be found through the roborio web interface
|
||||
server.startAutomaticCapture("cam0");
|
||||
}
|
||||
|
||||
/**
|
||||
* start up automatic capture you should see the video stream from the
|
||||
* webcam in your FRC PC Dashboard.
|
||||
*/
|
||||
public void operatorControl() {
|
||||
|
||||
while (isOperatorControl() && isEnabled()) {
|
||||
/** robot code here! **/
|
||||
Timer.delay(0.005); // wait for a motor update time
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -81,12 +81,18 @@
|
||||
<description>Example programs that demonstrate the use of the various commonly used sensors on FRC robots</description>
|
||||
</tagDescription>
|
||||
|
||||
<tagDescription>
|
||||
<name>Vision</name>
|
||||
<description>Example programs that demonstrate the use of USB Cameras and image processing</description>
|
||||
</tagDescription>
|
||||
|
||||
<example>
|
||||
<name>Getting Started</name>
|
||||
<description>An example program which demonstrates the simplest autonomous and
|
||||
teleoperated routines.</description>
|
||||
<tags>
|
||||
<tag>Getting Started with Java</tag>
|
||||
<tag>Complete List</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
@@ -154,6 +160,40 @@
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>CAN Talon SRX</name>
|
||||
<description>Demonstrate running a Talon SRX with the basic throttle mode.</description>
|
||||
<tags>
|
||||
<tag>Actuators</tag>
|
||||
<tag>Complete List</tag>
|
||||
<tag>Robot and Motor</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/CANTalon/src/org/usfirst/frc/team190/robot/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<name>CAN Talon SRX PID</name>
|
||||
<description>Demonstrate running a Talon SRX with PID Closed Loop control.</description>
|
||||
<tags>
|
||||
<tag>Actuators</tag>
|
||||
<tag>Complete List</tag>
|
||||
<tag>Robot and Motor</tag>
|
||||
</tags>
|
||||
<packages>
|
||||
<package>src/$package-dir</package>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/CANTalonPID/src/org/usfirst/frc/team190/robot/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
|
||||
<tagDescription>
|
||||
<name>CommandBased Robot</name>
|
||||
@@ -253,4 +293,97 @@
|
||||
destination="src/$package-dir/triggers/DoubleButton.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<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>
|
||||
</packages>
|
||||
<files>
|
||||
<file source="examples/QuickVision/src/org/usfirst/frc/team190/robot/Robot.java"
|
||||
destination="src/$package-dir/Robot.java"></file>
|
||||
</files>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<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/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>
|
||||
|
||||
@@ -2,6 +2,7 @@ package edu.wpi.first.wpilib.plugins.java.launching;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
@@ -15,8 +16,6 @@ import org.eclipse.debug.core.ILaunch;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationType;
|
||||
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
|
||||
import org.eclipse.debug.core.ILaunchManager;
|
||||
import org.eclipse.debug.core.IStreamListener;
|
||||
import org.eclipse.debug.core.model.IStreamMonitor;
|
||||
import org.eclipse.debug.ui.IDebugUIConstants;
|
||||
import org.eclipse.debug.ui.ILaunchShortcut;
|
||||
import org.eclipse.jdt.core.IJavaElement;
|
||||
@@ -37,13 +36,13 @@ import edu.wpi.first.wpilib.plugins.java.WPILibJavaPlugin;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public abstract class JavaLaunchShortcut implements ILaunchShortcut {
|
||||
private static final int DEBUG_ATTACH_ATTEMPTS = 20;
|
||||
private static final int DEBUG_ATTACH_RETRY_DELAY_SEC = 2;
|
||||
|
||||
//Class constants - used to delineate types for launch shortcuts
|
||||
public static final String DEPLOY_TYPE = "edu.wpi.first.wpilib.plugins.core.deploy";
|
||||
private static final String ANT_SERVER_THREAD_NAME = "Ant Build Server Connection";
|
||||
|
||||
// NOTE: This string must be changed if the port is changed.
|
||||
private static final String DEBUG_START_TEXT = "Listening for transport dt_socket at address: 8348";
|
||||
|
||||
private static ILaunch lastDeploy = null;
|
||||
|
||||
/**
|
||||
@@ -155,11 +154,9 @@ public abstract class JavaLaunchShortcut implements ILaunchShortcut {
|
||||
lastDeploy = AntLauncher.runAntFile(new File (activeProj.getLocation().toOSString() + File.separator + "build.xml"), targets, null, mode);
|
||||
|
||||
if((mode.equals(ILaunchManager.DEBUG_MODE))&&(getLaunchType().equals(DEPLOY_TYPE))) {
|
||||
ILaunchConfigurationWorkingCopy config;
|
||||
try {
|
||||
config = getRemoteDebugConfig(activeProj);
|
||||
startDebugConfig(config, lastDeploy);
|
||||
} catch (CoreException e) {
|
||||
startDebugConfig(getRemoteDebugConfig(activeProj));
|
||||
} catch (CoreException | InterruptedException e) {
|
||||
WPILibJavaPlugin.logError("Debug attach failed", e);
|
||||
}
|
||||
}
|
||||
@@ -190,20 +187,28 @@ public abstract class JavaLaunchShortcut implements ILaunchShortcut {
|
||||
return WPILibCore.getDefault().getTargetIP(proj);
|
||||
}
|
||||
|
||||
private void startDebugConfig(final ILaunchConfigurationWorkingCopy config, ILaunch deploy) throws CoreException {
|
||||
IStreamListener listener = new IStreamListener() {
|
||||
@Override
|
||||
public void streamAppended(String text, IStreamMonitor monitor) {
|
||||
if (text.contains(DEBUG_START_TEXT)) {
|
||||
try {
|
||||
config.launch(ILaunchManager.DEBUG_MODE, null);
|
||||
} catch (CoreException e) {
|
||||
WPILibJavaPlugin.logError("Error starting debug config..", e);
|
||||
}
|
||||
monitor.removeListener(this);
|
||||
private void startDebugConfig(final ILaunchConfigurationWorkingCopy config)
|
||||
throws CoreException, InterruptedException {
|
||||
int remainingAttempts = DEBUG_ATTACH_ATTEMPTS;
|
||||
|
||||
// Retry until success or rethrow of exception on failure
|
||||
while (true) {
|
||||
try {
|
||||
WPILibJavaPlugin.logInfo("Attemping to attach debugger...");
|
||||
config.launch(ILaunchManager.DEBUG_MODE, null);
|
||||
WPILibJavaPlugin.logInfo("Debugger attached.");
|
||||
break;
|
||||
} catch (CoreException e) {
|
||||
if (--remainingAttempts > 0) {
|
||||
String errorMsg = MessageFormat.format("Unable to attach debugger. "
|
||||
+ "{0} attempts remain - waiting {1} second(s) before retrying...",
|
||||
remainingAttempts, DEBUG_ATTACH_RETRY_DELAY_SEC);
|
||||
WPILibJavaPlugin.logError(errorMsg, null);
|
||||
Thread.sleep(DEBUG_ATTACH_RETRY_DELAY_SEC * 1000);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
deploy.getProcesses()[0].getStreamsProxy().getOutputStreamMonitor().addListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.wizard.WizardPage;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionListener;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
@@ -76,6 +78,15 @@ public class FileTemplateWizardMainPage extends WizardPage {
|
||||
});
|
||||
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
projectsCombo.setLayoutData(gd);
|
||||
projectsCombo.addSelectionListener(new SelectionListener() {
|
||||
public void widgetSelected(SelectionEvent e){
|
||||
if (project == null){
|
||||
project = projectsCombo.getProject();
|
||||
}
|
||||
packageText.setText(getDefaultPackage());
|
||||
}
|
||||
public void widgetDefaultSelected(SelectionEvent e){}
|
||||
});
|
||||
projectsCombo.addModifyListener(new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
dialogChanged();
|
||||
@@ -116,6 +127,7 @@ public class FileTemplateWizardMainPage extends WizardPage {
|
||||
*/
|
||||
|
||||
private void initialize() {
|
||||
WPILibJavaPlugin.logInfo("initialize");
|
||||
projectsCombo.setProject(project);
|
||||
packageText.setText(getDefaultPackage());
|
||||
}
|
||||
@@ -130,7 +142,7 @@ public class FileTemplateWizardMainPage extends WizardPage {
|
||||
|
||||
// Update the default package if necessary
|
||||
if (project == null || !project.equals(projectsCombo.getProject())) {
|
||||
String oldDefault = getDefaultPackage();
|
||||
String oldDefault = getDefaultPackage();
|
||||
project = projectsCombo.getProject();
|
||||
if (packageString.equals(oldDefault)) {
|
||||
packageText.setText(getDefaultPackage());
|
||||
@@ -200,4 +212,4 @@ public class FileTemplateWizardMainPage extends WizardPage {
|
||||
if (defaultPackage != null) return defaultPackage;
|
||||
else return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
# Deployment information
|
||||
username=admin
|
||||
username=lvuser
|
||||
password=
|
||||
deploy.dir=/home/lvuser
|
||||
deploy.kill.command=. /etc/profile.d/natinst-path.sh; /usr/local/frc/bin/frcKillRobot.sh -t -r
|
||||
@@ -20,7 +20,7 @@ networktables.sources=${wpilib.lib}/NetworkTables-sources.jar
|
||||
#jnaerator.jar=${wpilib.lib}/jnaerator-runtime.jar
|
||||
#classpath=${wpilib.jar}:${networktables.jar}:${jna.jar}:${jnaerator.jar}
|
||||
classpath=${wpilib.jar}:${networktables.jar}
|
||||
roboRIOAllowedImages=18
|
||||
roboRIOAllowedImages=23
|
||||
|
||||
# Ant support
|
||||
wpilib.ant.dir=${wpilib}/ant
|
||||
|
||||
@@ -19,21 +19,37 @@
|
||||
<!-- Targets -->
|
||||
|
||||
<target name="get-target-ip">
|
||||
<property name="ant.enable.asserts" value="true"/>
|
||||
<property name="target" value="roboRIO-${team-number}.local" />
|
||||
<echo>Trying Target: ${target}</echo>
|
||||
<if>
|
||||
<isreachable host="${target}"/>
|
||||
<isreachable host="${target}" timeout="5"/>
|
||||
<then>
|
||||
<echo>roboRIO found via mDNS</echo>
|
||||
</then>
|
||||
<else>
|
||||
<math result="ip.upper" operand1="${team-number}" operation="/" operand2="100" datatype="int"/>
|
||||
<math result="ip.lower" operand1="${team-number}" operation="%" operand2="100" datatype="int"/>
|
||||
<property name="target" value="10.${ip-upper}.${ip.lower}.2"/>
|
||||
<echo>roboRIO not found via mDNS, falling back to static address of ${target}</echo>
|
||||
<assert name="roboRIOFound" message="roboRIO not found, please check that the roboRIO is connected, imaged and that the team number is set properly in Eclipse">
|
||||
<isreachable host="${target}"/>
|
||||
</assert>
|
||||
<var name="target" unset="true"/>
|
||||
<echo> roboRIO not found via mDNS, falling back to static USB</echo>
|
||||
<property name="target" value="172.22.11.2"/>
|
||||
<if>
|
||||
<isreachable host="${target}" timeout="5"/>
|
||||
<then>
|
||||
<echo>roboRIO found via static USB</echo>
|
||||
</then>
|
||||
<else>
|
||||
<var name="target" unset="true"/>
|
||||
<math result="ip.upper" operand1="${team-number}" operation="/" operand2="100" datatype="int"/>
|
||||
<math result="ip.lower" operand1="${team-number}" operation="%" operand2="100" datatype="int"/>
|
||||
<property name="target" value="10.${ip.upper}.${ip.lower}.2"/>
|
||||
<echo>roboRIO not found via USB, falling back to static address of ${target}</echo>
|
||||
<assert name="roboRIOFound" message="roboRIO not found, please check that the roboRIO is connected, imaged and that the team number is set properly in Eclipse">
|
||||
<bool>
|
||||
<isreachable host="${target}" timeout="5"/>
|
||||
</bool>
|
||||
</assert>
|
||||
<echo>roboRIO found via Ethernet static</echo>
|
||||
</else>
|
||||
</if>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
@@ -88,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}"
|
||||
@@ -192,7 +217,7 @@
|
||||
|
||||
<target name="dependencies" depends="get-target-ip">
|
||||
<property name="ant.enable.asserts" value="true"/>
|
||||
<post to="http://${target}/nisysapi/server" logfile="sysProps.xml" verbose="false" encoding="UTF-16LE">
|
||||
<post to="http://${target}/nisysapi/server" logfile="sysProps.xml" verbose="false" encoding="UTF-16LE" append="false">
|
||||
<prop name="Function" value="GetPropertiesOfItem"/>
|
||||
<prop name="Plugins" value="nisyscfg"/>
|
||||
<prop name="Items" value="system"/>
|
||||
@@ -204,7 +229,8 @@
|
||||
<contains string="${roboRIOAllowedImages}" substring="${roboRIOImage}"/>
|
||||
</bool>
|
||||
</assert>
|
||||
<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>roboRIO image version validated</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.
@@ -1,2 +1,2 @@
|
||||
/usr/local/frc/JRE/bin/java -jar /home/lvuser/FRCUserProgram.jar
|
||||
env LD_PRELOAD=/lib/libstdc++.so.6.0.20 /usr/local/frc/bin/netconsole-host /usr/local/frc/JRE/bin/java -jar /home/lvuser/FRCUserProgram.jar
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/usr/local/frc/JRE/bin/java -XX:+UsePerfData -agentlib:jdwp=transport=dt_socket,address=8348,server=y,suspend=y -jar /home/lvuser/FRCUserProgram.jar
|
||||
env LD_PRELOAD=/lib/libstdc++.so.6.0.20 /usr/local/frc/bin/netconsole-host /usr/local/frc/JRE/bin/java -XX:+UsePerfData -agentlib:jdwp=transport=dt_socket,address=8348,server=y,suspend=y -jar /home/lvuser/FRCUserProgram.jar
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#define CTR_TxTimeout_MESSAGE "CTRE CAN Transmit Timeout"
|
||||
#define CTR_InvalidParamValue_MESSAGE "CTRE CAN Invalid Parameter"
|
||||
#define CTR_UnexpectedArbId_MESSAGE "CTRE Unexpected Arbitration ID (CAN Node ID)"
|
||||
#define CTR_TxFailed_MESSAGE "CTRE CAN Transmit Error"
|
||||
#define CTR_SigNotUpdated_MESSAGE "CTRE CAN Signal Not Updated"
|
||||
|
||||
#define NiFpga_Status_FifoTimeout_MESSAGE "NIFPGA: FIFO timeout error"
|
||||
#define NiFpga_Status_TransferAborted_MESSAGE "NIFPGA: Transfer aborted error"
|
||||
@@ -45,6 +47,8 @@
|
||||
#define ANALOG_TRIGGER_PULSE_OUTPUT_ERROR_MESSAGE "HAL: Attempted to read AnalogTrigger pulse output."
|
||||
#define PARAMETER_OUT_OF_RANGE -1028
|
||||
#define PARAMETER_OUT_OF_RANGE_MESSAGE "HAL: A parameter is out of range."
|
||||
#define RESOURCE_IS_ALLOCATED -1029
|
||||
#define RESOURCE_IS_ALLOCATED_MESSAGE "HAL: Resource already allocated"
|
||||
|
||||
#define VI_ERROR_SYSTEM_ERROR_MESSAGE "HAL - VISA: System Error";
|
||||
#define VI_ERROR_INV_OBJECT_MESSAGE "HAL - VISA: Invalid Object"
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include "Accelerometer.hpp"
|
||||
#include "Analog.hpp"
|
||||
#include "CAN.hpp"
|
||||
#include "Compressor.hpp"
|
||||
#include "Digital.hpp"
|
||||
#include "Solenoid.hpp"
|
||||
@@ -178,7 +177,20 @@ struct HALJoystickPOVs {
|
||||
int16_t povs[kMaxJoystickPOVs];
|
||||
};
|
||||
|
||||
typedef uint32_t HALJoystickButtons;
|
||||
struct HALJoystickButtons {
|
||||
uint32_t buttons;
|
||||
uint8_t count;
|
||||
};
|
||||
|
||||
struct HALJoystickDescriptor {
|
||||
uint8_t isXbox;
|
||||
uint8_t type;
|
||||
char name[256];
|
||||
uint8_t axisCount;
|
||||
uint8_t axisTypes;
|
||||
uint8_t buttonCount;
|
||||
uint8_t povCount;
|
||||
};
|
||||
|
||||
inline float intToFloat(int value)
|
||||
{
|
||||
@@ -213,9 +225,12 @@ extern "C"
|
||||
int HALGetAllianceStation(enum HALAllianceStationID *allianceStation);
|
||||
int HALGetJoystickAxes(uint8_t joystickNum, HALJoystickAxes *axes);
|
||||
int HALGetJoystickPOVs(uint8_t joystickNum, HALJoystickPOVs *povs);
|
||||
int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons, uint8_t *count);
|
||||
int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons);
|
||||
int HALGetJoystickDescriptor(uint8_t joystickNum, HALJoystickDescriptor *desc);
|
||||
int HALSetJoystickOutputs(uint8_t joystickNum, uint32_t outputs, uint16_t leftRumble, uint16_t rightRumble);
|
||||
int HALGetMatchTime(float *matchTime);
|
||||
|
||||
void HALSetNewDataSem(pthread_cond_t *);
|
||||
void HALSetNewDataSem(MULTIWAIT_ID sem);
|
||||
|
||||
bool HALGetSystemActive(int32_t *status);
|
||||
bool HALGetBrownedOut(int32_t *status);
|
||||
|
||||
@@ -11,4 +11,9 @@ extern "C"
|
||||
double getPDPTemperature(int32_t *status);
|
||||
double getPDPVoltage(int32_t *status);
|
||||
double getPDPChannelCurrent(uint8_t channel, int32_t *status);
|
||||
double getPDPTotalCurrent(int32_t *status);
|
||||
double getPDPTotalPower(int32_t *status);
|
||||
double getPDPTotalEnergy(int32_t *status);
|
||||
void resetPDPTotalEnergy(int32_t *status);
|
||||
void clearPDPStickyFaults(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);
|
||||
}
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
#include "HAL/CAN.hpp"
|
||||
#include <map>
|
||||
|
||||
struct CANMessage
|
||||
{
|
||||
uint8_t data[8];
|
||||
};
|
||||
|
||||
static std::map<uint32_t, CANMessage> outgoingMessages;
|
||||
static std::map<uint32_t, CANMessage> incomingMessages;
|
||||
|
||||
static const uint32_t kFullMessageIDMask = 0x1fffffff;
|
||||
|
||||
/**
|
||||
* Gets the data from the outgoing hashmap and calls
|
||||
* CANSessionMux...sendMessage.
|
||||
*/
|
||||
void canTxSend(uint32_t arbID, uint8_t length, int32_t period)
|
||||
{
|
||||
CANMessage &message = outgoingMessages[arbID];
|
||||
|
||||
int32_t status;
|
||||
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage(
|
||||
arbID, message.data, length, period, &status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a field in the outgoing hashmap.
|
||||
*
|
||||
* This is called every time an single byte field changes in a message.data,
|
||||
* such as when a setter on a CAN device is called.
|
||||
*/
|
||||
void canTxPackInt8(uint32_t arbID, uint8_t offset, uint8_t value)
|
||||
{
|
||||
CANMessage &message = outgoingMessages[arbID];
|
||||
|
||||
message.data[offset] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a field in the outgoing hashmap.
|
||||
*
|
||||
* This is called every time a short integer field changes in a message.data,
|
||||
* such as when a setter on a CAN device is called.
|
||||
*/
|
||||
void canTxPackInt16(uint32_t arbID, uint8_t offset, uint16_t value)
|
||||
{
|
||||
CANMessage &message = outgoingMessages[arbID];
|
||||
|
||||
*(uint16_t *)(message.data + offset) = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a field in the outgoing hashmap.
|
||||
*
|
||||
* This is called every time a long integer field changes in a message.data,
|
||||
* such as when a setter on a CAN device is called.
|
||||
*/
|
||||
void canTxPackInt32(uint32_t arbID, uint8_t offset, uint32_t value)
|
||||
{
|
||||
CANMessage &message = outgoingMessages[arbID];
|
||||
|
||||
*(uint32_t *)(message.data + offset) = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a field in the outgoing hashmap.
|
||||
*
|
||||
* This is called every time an 8.8 fixed point field changes in a message,
|
||||
* such as when a setter on a CAN device is called.
|
||||
*/
|
||||
void canTxPackFXP16(uint32_t arbID, uint8_t offset, double value)
|
||||
{
|
||||
int16_t raw = value * 255.0;
|
||||
|
||||
canTxPackInt16(arbID, offset, raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a field in the outgoing hashmap.
|
||||
*
|
||||
* This is called every time a 16.16 fixed point field changes in a message,
|
||||
* such as when a setter on a CAN device is called.
|
||||
*/
|
||||
void canTxPackFXP32(uint32_t arbID, uint8_t offset, double value)
|
||||
{
|
||||
int32_t raw = value * 65535.0;
|
||||
|
||||
canTxPackInt32(arbID, offset, raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the outgoing hashmap.
|
||||
*
|
||||
* This is called in getters for configuration data.
|
||||
*/
|
||||
uint8_t canTxUnpackInt8(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
CANMessage &message = outgoingMessages[arbID];
|
||||
|
||||
return message.data[offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the outgoing hashmap.
|
||||
*
|
||||
* This is called in getters for configuration data.
|
||||
*/
|
||||
uint16_t canTxUnpackInt16(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
CANMessage &message = outgoingMessages[arbID];
|
||||
|
||||
return *reinterpret_cast<uint16_t *>(message.data + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the outgoing hashmap.
|
||||
*
|
||||
* This is called in getters for configuration data.
|
||||
*/
|
||||
uint32_t canTxUnpackInt32(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
CANMessage &message = outgoingMessages[arbID];
|
||||
|
||||
return *reinterpret_cast<uint32_t *>(message.data + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the outgoing hashmap.
|
||||
*
|
||||
* This is called in getters for configuration data.
|
||||
*/
|
||||
double canTxUnpackFXP16(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
int16_t raw = canTxUnpackInt16(arbID, offset);
|
||||
|
||||
return raw / 255.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the outgoing hashmap.
|
||||
*
|
||||
* This is called in getters for configuration data.
|
||||
*/
|
||||
double canTxUnpackFXP32(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
int32_t raw = canTxUnpackInt32(arbID, offset);
|
||||
|
||||
return raw / 65535.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data from CANSessionMux (if it's available) and put it in the incoming
|
||||
* hashmap.
|
||||
*
|
||||
* @return true if there's new data. Otherwise, the last received value should
|
||||
* still be in the hashmap.
|
||||
*/
|
||||
bool canRxReceive(uint32_t arbID)
|
||||
{
|
||||
CANMessage &message = incomingMessages[arbID];
|
||||
|
||||
uint8_t length;
|
||||
uint32_t timestamp;
|
||||
int32_t status;
|
||||
|
||||
FRC_NetworkCommunication_CANSessionMux_receiveMessage(
|
||||
&arbID, kFullMessageIDMask, message.data, &length, ×tamp, &status);
|
||||
|
||||
return status != ERR_CANSessionMux_MessageNotFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the incoming hashmap.
|
||||
*
|
||||
* This is called in getters for status data.
|
||||
*/
|
||||
uint8_t canRxUnpackInt8(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
CANMessage &message = incomingMessages[arbID];
|
||||
|
||||
return message.data[offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the incoming hashmap.
|
||||
*
|
||||
* This is called in getters for status data.
|
||||
*/
|
||||
uint16_t canRxUnpackInt16(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
CANMessage &message = incomingMessages[arbID];
|
||||
|
||||
return *reinterpret_cast<uint16_t *>(message.data + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the incoming hashmap.
|
||||
*
|
||||
* This is called in getters for status data.
|
||||
*/
|
||||
uint32_t canRxUnpackInt32(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
CANMessage &message = incomingMessages[arbID];
|
||||
|
||||
return *reinterpret_cast<uint32_t *>(message.data + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the incoming hashmap.
|
||||
*
|
||||
* This is called in getters for status data.
|
||||
*/
|
||||
double canRxUnpackFXP16(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
int16_t raw = canRxUnpackInt16(arbID, offset);
|
||||
|
||||
return raw / 255.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a field from the incoming hashmap.
|
||||
*
|
||||
* This is called in getters for status data.
|
||||
*/
|
||||
double canRxUnpackFXP32(uint32_t arbID, uint8_t offset)
|
||||
{
|
||||
int32_t raw = canRxUnpackInt32(arbID, offset);
|
||||
|
||||
return raw / 65535.0;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -384,7 +384,11 @@ bool allocateDIO(void* digital_port_pointer, bool input, int32_t *status) {
|
||||
DigitalPort* port = (DigitalPort*) digital_port_pointer;
|
||||
char buf[64];
|
||||
snprintf(buf, 64, "DIO %d", port->port.pin);
|
||||
if (DIOChannels->Allocate(port->port.pin, buf) == ~0ul) return false;
|
||||
if (DIOChannels->Allocate(port->port.pin, buf) == ~0ul) {
|
||||
*status = RESOURCE_IS_ALLOCATED;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
Synchronized sync(digitalDIOSemaphore);
|
||||
|
||||
@@ -420,7 +424,11 @@ bool allocatePWMChannel(void* digital_port_pointer, int32_t *status) {
|
||||
DigitalPort* port = (DigitalPort*) digital_port_pointer;
|
||||
char buf[64];
|
||||
snprintf(buf, 64, "PWM %d", port->port.pin);
|
||||
if (PWMChannels->Allocate(port->port.pin, buf) == ~0ul) return false;
|
||||
if (PWMChannels->Allocate(port->port.pin, buf) == ~0ul) {
|
||||
*status = RESOURCE_IS_ALLOCATED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (port->port.pin > tPWM::kNumHdrRegisters-1) {
|
||||
snprintf(buf, 64, "PWM %d and DIO %d", port->port.pin, remapMXPPWMChannel(port->port.pin) + 10);
|
||||
if (DIOChannels->Allocate(remapMXPPWMChannel(port->port.pin) + 10, buf) == ~0ul) return false;
|
||||
@@ -624,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.
|
||||
@@ -633,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);
|
||||
@@ -688,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);
|
||||
@@ -830,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)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -842,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -917,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");
|
||||
@@ -992,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;
|
||||
}
|
||||
|
||||
@@ -1010,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1070,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
|
||||
*
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "NetworkCommunication/FRCComm.h"
|
||||
#include "NetworkCommunication/UsageReporting.h"
|
||||
#include "NetworkCommunication/LoadOut.h"
|
||||
#include "NetworkCommunication/CANSessionMux.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
@@ -53,6 +54,10 @@ const char* getHALErrorMessage(int32_t code)
|
||||
return CTR_InvalidParamValue_MESSAGE;
|
||||
case CTR_UnexpectedArbId:
|
||||
return CTR_UnexpectedArbId_MESSAGE;
|
||||
case CTR_TxFailed:
|
||||
return CTR_TxFailed_MESSAGE;
|
||||
case CTR_SigNotUpdated:
|
||||
return CTR_SigNotUpdated_MESSAGE;
|
||||
case NiFpga_Status_FifoTimeout:
|
||||
return NiFpga_Status_FifoTimeout_MESSAGE;
|
||||
case NiFpga_Status_TransferAborted:
|
||||
@@ -201,14 +206,30 @@ int HALGetJoystickPOVs(uint8_t joystickNum, HALJoystickPOVs *povs)
|
||||
return FRC_NetworkCommunication_getJoystickPOVs(joystickNum, (JoystickPOV_t*) povs, kMaxJoystickPOVs);
|
||||
}
|
||||
|
||||
int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons, uint8_t *count)
|
||||
int HALGetJoystickButtons(uint8_t joystickNum, HALJoystickButtons *buttons)
|
||||
{
|
||||
return FRC_NetworkCommunication_getJoystickButtons(joystickNum, buttons, count);
|
||||
return FRC_NetworkCommunication_getJoystickButtons(joystickNum, &buttons->buttons, &buttons->count);
|
||||
}
|
||||
|
||||
void HALSetNewDataSem(pthread_cond_t * param)
|
||||
int HALGetJoystickDescriptor(uint8_t joystickNum, HALJoystickDescriptor *desc)
|
||||
{
|
||||
setNewDataSem(param);
|
||||
return FRC_NetworkCommunication_getJoystickDesc(joystickNum, &desc->isXbox, &desc->type, (char *)(&desc->name),
|
||||
&desc->axisCount, &desc->axisTypes, &desc->buttonCount, &desc->povCount);
|
||||
}
|
||||
|
||||
int HALSetJoystickOutputs(uint8_t joystickNum, uint32_t outputs, uint16_t leftRumble, uint16_t rightRumble)
|
||||
{
|
||||
return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs, leftRumble, rightRumble);
|
||||
}
|
||||
|
||||
int HALGetMatchTime(float *matchTime)
|
||||
{
|
||||
return FRC_NetworkCommunication_getMatchTime(matchTime);
|
||||
}
|
||||
|
||||
void HALSetNewDataSem(MULTIWAIT_ID sem)
|
||||
{
|
||||
setNewDataSem(sem);
|
||||
}
|
||||
|
||||
bool HALGetSystemActive(int32_t *status)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#define WARN_CANSessionMux_NoToken 44087
|
||||
#define ERR_CANSessionMux_NotAllowed -44088
|
||||
#define ERR_CANSessionMux_NotInitialized -44089
|
||||
#define ERR_CANSessionMux_SessionOverrun 44050
|
||||
|
||||
struct tCANStreamMessage{
|
||||
uint32_t messageID;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#endif
|
||||
|
||||
#define ERR_FRCSystem_NetCommNotResponding -44049
|
||||
#define ERR_FRCSystem_NoDSConnection -44018
|
||||
|
||||
enum AllianceStationID_t {
|
||||
kAllianceStationID_red1,
|
||||
@@ -43,6 +44,13 @@ enum AllianceStationID_t {
|
||||
kAllianceStationID_blue3,
|
||||
};
|
||||
|
||||
enum MatchType_t {
|
||||
kMatchType_none,
|
||||
kMatchType_practice,
|
||||
kMatchType_qualification,
|
||||
kMatchType_elimination,
|
||||
};
|
||||
|
||||
struct ControlWord_t {
|
||||
#ifndef __vxworks
|
||||
uint32_t enabled : 1;
|
||||
|
||||
@@ -26,3 +26,36 @@ double getPDPChannelCurrent(uint8_t channel, int32_t *status) {
|
||||
return current;
|
||||
}
|
||||
|
||||
double getPDPTotalCurrent(int32_t *status) {
|
||||
double current;
|
||||
|
||||
*status = pdp.GetTotalCurrent(current);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
double getPDPTotalPower(int32_t *status) {
|
||||
double power;
|
||||
|
||||
*status = pdp.GetTotalPower(power);
|
||||
|
||||
return power;
|
||||
}
|
||||
|
||||
double getPDPTotalEnergy(int32_t *status) {
|
||||
double energy;
|
||||
|
||||
*status = pdp.GetTotalEnergy(energy);
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
void resetPDPTotalEnergy(int32_t *status) {
|
||||
*status = pdp.ResetEnergy();
|
||||
}
|
||||
|
||||
void clearPDPStickyFaults(int32_t *status) {
|
||||
*status = pdp.ClearStickyFaults();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,8 +13,10 @@ void serialInitializePort(uint8_t port, int32_t *status) {
|
||||
|
||||
if(port == 0)
|
||||
portName = "ASRL1::INSTR";
|
||||
else
|
||||
else if (port == 1)
|
||||
portName = "ASRL2::INSTR";
|
||||
else
|
||||
portName = "ASRL3::INSTR";
|
||||
|
||||
*status = viOpen(m_resourceManagerHandle, const_cast<char*>(portName), VI_NULL, VI_NULL, (ViSession*)&m_portHandle[port]);
|
||||
if(*status > 0)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
1241
hal/lib/Athena/ctre/CanTalonSRX.cpp
Normal file
1241
hal/lib/Athena/ctre/CanTalonSRX.cpp
Normal file
File diff suppressed because it is too large
Load Diff
375
hal/lib/Athena/ctre/CanTalonSRX.h
Normal file
375
hal/lib/Athena/ctre/CanTalonSRX.h
Normal file
@@ -0,0 +1,375 @@
|
||||
/**
|
||||
* @brief CAN TALON SRX driver.
|
||||
*
|
||||
* The TALON SRX is designed to instrument all runtime signals periodically. The default periods are chosen to support 16 TALONs
|
||||
* with 10ms update rate for control (throttle or setpoint). However these can be overridden with SetStatusFrameRate. @see SetStatusFrameRate
|
||||
* The getters for these unsolicited signals are auto generated at the bottom of this module.
|
||||
*
|
||||
* Likewise most control signals are sent periodically using the fire-and-forget CAN API.
|
||||
* The setters for these unsolicited signals are auto generated at the bottom of this module.
|
||||
*
|
||||
* Signals that are not available in an unsolicited fashion are the Close Loop gains.
|
||||
* For teams that have a single profile for their TALON close loop they can use either the webpage to configure their TALONs once
|
||||
* or set the PIDF,Izone,CloseLoopRampRate,etc... once in the robot application. These parameters are saved to flash so once they are
|
||||
* loaded in the TALON, they will persist through power cycles and mode changes.
|
||||
*
|
||||
* For teams that have one or two profiles to switch between, they can use the same strategy since there are two slots to choose from
|
||||
* and the ProfileSlotSelect is periodically sent in the 10 ms control frame.
|
||||
*
|
||||
* For teams that require changing gains frequently, they can use the soliciting API to get and set those parameters. Most likely
|
||||
* they will only need to set them in a periodic fashion as a function of what motion the application is attempting.
|
||||
* If this API is used, be mindful of the CAN utilization reported in the driver station.
|
||||
*
|
||||
* Encoder position is measured in encoder edges. Every edge is counted (similar to roboRIO 4X mode).
|
||||
* Analog position is 10 bits, meaning 1024 ticks per rotation (0V => 3.3V).
|
||||
* Use SetFeedbackDeviceSelect to select which sensor type you need. Once you do that you can use GetSensorPosition()
|
||||
* and GetSensorVelocity(). These signals are updated on CANBus every 20ms (by default).
|
||||
* If a relative sensor is selected, you can zero (or change the current value) using SetSensorPosition.
|
||||
*
|
||||
* Analog Input and quadrature position (and velocity) are also explicitly reported in GetEncPosition, GetEncVel, GetAnalogInWithOv, GetAnalogInVel.
|
||||
* These signals are available all the time, regardless of what sensor is selected at a rate of 100ms. This allows easy instrumentation
|
||||
* for "in the pits" checking of all sensors regardless of modeselect. The 100ms rate is overridable for teams who want to acquire sensor
|
||||
* data for processing, not just instrumentation. Or just select the sensor using SetFeedbackDeviceSelect to get it at 20ms.
|
||||
*
|
||||
* Velocity is in position ticks / 100ms.
|
||||
*
|
||||
* All output units are in respect to duty cycle (throttle) which is -1023(full reverse) to +1023 (full forward).
|
||||
* This includes demand (which specifies duty cycle when in duty cycle mode) and rampRamp, which is in throttle units per 10ms (if nonzero).
|
||||
*
|
||||
* Pos and velocity close loops are calc'd as
|
||||
* err = target - posOrVel.
|
||||
* iErr += err;
|
||||
* if( (IZone!=0) and abs(err) > IZone)
|
||||
* ClearIaccum()
|
||||
* output = P X err + I X iErr + D X dErr + F X target
|
||||
* dErr = err - lastErr
|
||||
* P, I,and D gains are always positive. F can be negative.
|
||||
* Motor direction can be reversed using SetRevMotDuringCloseLoopEn if sensor and motor are out of phase.
|
||||
* Similarly feedback sensor can also be reversed (multiplied by -1) if you prefer the sensor to be inverted.
|
||||
*
|
||||
* P gain is specified in throttle per error tick. For example, a value of 102 is ~9.9% (which is 102/1023) throttle per 1
|
||||
* ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
|
||||
*
|
||||
* I gain is specified in throttle per integrated error. For example, a value of 10 equates to ~0.99% (which is 10/1023)
|
||||
* for each accumulated ADC unit(10bit) or 1 quadrature encoder edge depending on selected sensor.
|
||||
* Close loop and integral accumulator runs every 1ms.
|
||||
*
|
||||
* D gain is specified in throttle per derivative error. For example a value of 102 equates to ~9.9% (which is 102/1023)
|
||||
* per change of 1 unit (ADC or encoder) per ms.
|
||||
*
|
||||
* I Zone is specified in the same units as sensor position (ADC units or quadrature edges). If pos/vel error is outside of
|
||||
* this value, the integrated error will auto-clear...
|
||||
* if( (IZone!=0) and abs(err) > IZone)
|
||||
* ClearIaccum()
|
||||
* ...this is very useful in preventing integral windup and is highly recommended if using full PID to keep stability low.
|
||||
*
|
||||
* CloseLoopRampRate is in throttle units per 1ms. Set to zero to disable ramping.
|
||||
* Works the same as RampThrottle but only is in effect when a close loop mode and profile slot is selected.
|
||||
*
|
||||
* auto generated using spreadsheet and WpiClassGen.csproj
|
||||
* @link https://docs.google.com/spreadsheets/d/1OU_ZV7fZLGYUQ-Uhc8sVAmUmWTlT8XBFYK8lfjg_tac/edit#gid=1766046967
|
||||
*/
|
||||
#ifndef CanTalonSRX_H_
|
||||
#define CanTalonSRX_H_
|
||||
#include "ctre.h" //BIT Defines + Typedefs
|
||||
#include "CtreCanNode.h"
|
||||
#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
|
||||
#include <map>
|
||||
class CanTalonSRX : public CtreCanNode
|
||||
{
|
||||
private:
|
||||
|
||||
/** just in case user wants to modify periods of certain status frames.
|
||||
* Default the vars to match the firmware default. */
|
||||
uint32_t _statusRateMs[4];
|
||||
//---------------------- Vars for opening a CAN stream if caller needs signals that require soliciting */
|
||||
uint32_t _can_h; //!< Session handle for catching response params.
|
||||
int32_t _can_stat; //!< Session handle status.
|
||||
struct tCANStreamMessage _msgBuff[20];
|
||||
static int const kMsgCapacity = 20;
|
||||
typedef std::map<uint32_t, uint32_t> sigs_t;
|
||||
sigs_t _sigs; //!< Catches signal updates that are solicited. Expect this to be very few.
|
||||
void OpenSessionIfNeedBe();
|
||||
void ProcessStreamMessages();
|
||||
/**
|
||||
* Send a one shot frame to set an arbitrary signal.
|
||||
* Most signals are in the control frame so avoid using this API unless you have to.
|
||||
* Use this api for...
|
||||
* -A motor controller profile signal eProfileParam_XXXs. These are backed up in flash. If you are gain-scheduling then call this periodically.
|
||||
* -Default brake and limit switch signals... eOnBoot_XXXs. Avoid doing this, use the override signals in the control frame.
|
||||
* Talon will automatically send a PARAM_RESPONSE after the set, so GetParamResponse will catch the latest value after a couple ms.
|
||||
*/
|
||||
CTR_Code SetParamRaw(uint32_t paramEnum, int32_t rawBits);
|
||||
/**
|
||||
* Checks cached CAN frames and updating solicited signals.
|
||||
*/
|
||||
CTR_Code GetParamResponseRaw(uint32_t paramEnum, int32_t & rawBits);
|
||||
public:
|
||||
static const int kDefaultControlPeriodMs = 10; //!< default control update rate is 10ms.
|
||||
CanTalonSRX(int deviceNumber = 0,int controlPeriodMs = kDefaultControlPeriodMs);
|
||||
~CanTalonSRX();
|
||||
void Set(double value);
|
||||
/* mode select enumerations */
|
||||
static const int kMode_DutyCycle = 0; //!< Demand is 11bit signed duty cycle [-1023,1023].
|
||||
static const int kMode_PositionCloseLoop = 1; //!< Position PIDF.
|
||||
static const int kMode_VelocityCloseLoop = 2; //!< Velocity PIDF.
|
||||
static const int kMode_CurrentCloseLoop = 3; //!< Current close loop - not done.
|
||||
static const int kMode_VoltCompen = 4; //!< Voltage Compensation Mode - not done. Demand is fixed pt target 8.8 volts.
|
||||
static const int kMode_SlaveFollower = 5; //!< Demand is the 6 bit Device ID of the 'master' TALON SRX.
|
||||
static const int kMode_NoDrive = 15; //!< Zero the output (honors brake/coast) regardless of demand. Might be useful if we need to change modes but can't atomically change all the signals we want in between.
|
||||
/* limit switch enumerations */
|
||||
static const int kLimitSwitchOverride_UseDefaultsFromFlash = 1;
|
||||
static const int kLimitSwitchOverride_DisableFwd_DisableRev = 4;
|
||||
static const int kLimitSwitchOverride_DisableFwd_EnableRev = 5;
|
||||
static const int kLimitSwitchOverride_EnableFwd_DisableRev = 6;
|
||||
static const int kLimitSwitchOverride_EnableFwd_EnableRev = 7;
|
||||
/* brake override enumerations */
|
||||
static const int kBrakeOverride_UseDefaultsFromFlash = 0;
|
||||
static const int kBrakeOverride_OverrideCoast = 1;
|
||||
static const int kBrakeOverride_OverrideBrake = 2;
|
||||
/* feedback device enumerations */
|
||||
static const int kFeedbackDev_DigitalQuadEnc=0;
|
||||
static const int kFeedbackDev_AnalogPot=2;
|
||||
static const int kFeedbackDev_AnalogEncoder=3;
|
||||
static const int kFeedbackDev_CountEveryRisingEdge=4;
|
||||
static const int kFeedbackDev_CountEveryFallingEdge=5;
|
||||
static const int kFeedbackDev_PosIsPulseWidth=8;
|
||||
/* ProfileSlotSelect enumerations*/
|
||||
static const int kProfileSlotSelect_Slot0 = 0;
|
||||
static const int kProfileSlotSelect_Slot1 = 1;
|
||||
/* status frame rate types */
|
||||
static const int kStatusFrame_General = 0;
|
||||
static const int kStatusFrame_Feedback = 1;
|
||||
static const int kStatusFrame_Encoder = 2;
|
||||
static const int kStatusFrame_AnalogTempVbat = 3;
|
||||
/**
|
||||
* Signal enumeration for generic signal access.
|
||||
* Although every signal is enumerated, only use this for traffic that must be solicited.
|
||||
* Use the auto generated getters/setters at bottom of this header as much as possible.
|
||||
*/
|
||||
typedef enum _param_t{
|
||||
eProfileParamSlot0_P=1,
|
||||
eProfileParamSlot0_I=2,
|
||||
eProfileParamSlot0_D=3,
|
||||
eProfileParamSlot0_F=4,
|
||||
eProfileParamSlot0_IZone=5,
|
||||
eProfileParamSlot0_CloseLoopRampRate=6,
|
||||
eProfileParamSlot1_P=11,
|
||||
eProfileParamSlot1_I=12,
|
||||
eProfileParamSlot1_D=13,
|
||||
eProfileParamSlot1_F=14,
|
||||
eProfileParamSlot1_IZone=15,
|
||||
eProfileParamSlot1_CloseLoopRampRate=16,
|
||||
eProfileParamSoftLimitForThreshold=21,
|
||||
eProfileParamSoftLimitRevThreshold=22,
|
||||
eProfileParamSoftLimitForEnable=23,
|
||||
eProfileParamSoftLimitRevEnable=24,
|
||||
eOnBoot_BrakeMode=31,
|
||||
eOnBoot_LimitSwitch_Forward_NormallyClosed=32,
|
||||
eOnBoot_LimitSwitch_Reverse_NormallyClosed=33,
|
||||
eOnBoot_LimitSwitch_Forward_Disable=34,
|
||||
eOnBoot_LimitSwitch_Reverse_Disable=35,
|
||||
eFault_OverTemp=41,
|
||||
eFault_UnderVoltage=42,
|
||||
eFault_ForLim=43,
|
||||
eFault_RevLim=44,
|
||||
eFault_HardwareFailure=45,
|
||||
eFault_ForSoftLim=46,
|
||||
eFault_RevSoftLim=47,
|
||||
eStckyFault_OverTemp=48,
|
||||
eStckyFault_UnderVoltage=49,
|
||||
eStckyFault_ForLim=50,
|
||||
eStckyFault_RevLim=51,
|
||||
eStckyFault_ForSoftLim=52,
|
||||
eStckyFault_RevSoftLim=53,
|
||||
eAppliedThrottle=61,
|
||||
eCloseLoopErr=62,
|
||||
eFeedbackDeviceSelect=63,
|
||||
eRevMotDuringCloseLoopEn=64,
|
||||
eModeSelect=65,
|
||||
eProfileSlotSelect=66,
|
||||
eRampThrottle=67,
|
||||
eRevFeedbackSensor=68,
|
||||
eLimitSwitchEn=69,
|
||||
eLimitSwitchClosedFor=70,
|
||||
eLimitSwitchClosedRev=71,
|
||||
eSensorPosition=73,
|
||||
eSensorVelocity=74,
|
||||
eCurrent=75,
|
||||
eBrakeIsEnabled=76,
|
||||
eEncPosition=77,
|
||||
eEncVel=78,
|
||||
eEncIndexRiseEvents=79,
|
||||
eQuadApin=80,
|
||||
eQuadBpin=81,
|
||||
eQuadIdxpin=82,
|
||||
eAnalogInWithOv=83,
|
||||
eAnalogInVel=84,
|
||||
eTemp=85,
|
||||
eBatteryV=86,
|
||||
eResetCount=87,
|
||||
eResetFlags=88,
|
||||
eFirmVers=89,
|
||||
eSettingsChanged=90,
|
||||
eQuadFilterEn=91,
|
||||
ePidIaccum=93,
|
||||
}param_t;
|
||||
/*---------------------setters and getters that use the solicated param request/response-------------*//**
|
||||
* Send a one shot frame to set an arbitrary signal.
|
||||
* Most signals are in the control frame so avoid using this API unless you have to.
|
||||
* Use this api for...
|
||||
* -A motor controller profile signal eProfileParam_XXXs. These are backed up in flash. If you are gain-scheduling then call this periodically.
|
||||
* -Default brake and limit switch signals... eOnBoot_XXXs. Avoid doing this, use the override signals in the control frame.
|
||||
* Talon will automatically send a PARAM_RESPONSE after the set, so GetParamResponse will catch the latest value after a couple ms.
|
||||
*/
|
||||
CTR_Code SetParam(param_t paramEnum, double value);
|
||||
/**
|
||||
* Asks TALON to immedietely respond with signal value. This API is only used for signals that are not sent periodically.
|
||||
* This can be useful for reading params that rarely change like Limit Switch settings and PIDF values.
|
||||
* @param param to request.
|
||||
*/
|
||||
CTR_Code RequestParam(param_t paramEnum);
|
||||
CTR_Code GetParamResponse(param_t paramEnum, double & value);
|
||||
CTR_Code GetParamResponseInt32(param_t paramEnum, int & value);
|
||||
/*----- getters and setters that use param request/response. These signals are backed up in flash and will survive a power cycle. ---------*/
|
||||
/*----- If your application requires changing these values consider using both slots and switch between slot0 <=> slot1. ------------------*/
|
||||
/*----- If your application requires changing these signals frequently then it makes sense to leverage this API. --------------------------*/
|
||||
/*----- Getters don't block, so it may require several calls to get the latest value. --------------------------*/
|
||||
CTR_Code SetPgain(unsigned slotIdx,double gain);
|
||||
CTR_Code SetIgain(unsigned slotIdx,double gain);
|
||||
CTR_Code SetDgain(unsigned slotIdx,double gain);
|
||||
CTR_Code SetFgain(unsigned slotIdx,double gain);
|
||||
CTR_Code SetIzone(unsigned slotIdx,int zone);
|
||||
CTR_Code SetCloseLoopRampRate(unsigned slotIdx,int closeLoopRampRate);
|
||||
CTR_Code SetSensorPosition(int pos);
|
||||
CTR_Code SetForwardSoftLimit(int forwardLimit);
|
||||
CTR_Code SetReverseSoftLimit(int reverseLimit);
|
||||
CTR_Code SetForwardSoftEnable(int enable);
|
||||
CTR_Code SetReverseSoftEnable(int enable);
|
||||
CTR_Code GetPgain(unsigned slotIdx,double & gain);
|
||||
CTR_Code GetIgain(unsigned slotIdx,double & gain);
|
||||
CTR_Code GetDgain(unsigned slotIdx,double & gain);
|
||||
CTR_Code GetFgain(unsigned slotIdx,double & gain);
|
||||
CTR_Code GetIzone(unsigned slotIdx,int & zone);
|
||||
CTR_Code GetCloseLoopRampRate(unsigned slotIdx,int & closeLoopRampRate);
|
||||
CTR_Code GetForwardSoftLimit(int & forwardLimit);
|
||||
CTR_Code GetReverseSoftLimit(int & reverseLimit);
|
||||
CTR_Code GetForwardSoftEnable(int & enable);
|
||||
CTR_Code GetReverseSoftEnable(int & enable);
|
||||
/**
|
||||
* Change the periodMs of a TALON's status frame. See kStatusFrame_* enums for what's available.
|
||||
*/
|
||||
CTR_Code SetStatusFrameRate(unsigned frameEnum, unsigned periodMs);
|
||||
/**
|
||||
* Clear all sticky faults in TALON.
|
||||
*/
|
||||
CTR_Code ClearStickyFaults();
|
||||
/*------------------------ auto generated. This API is optimal since it uses the fire-and-forget CAN interface ----------------------*/
|
||||
/*------------------------ These signals should cover the majority of all use cases. ----------------------------------*/
|
||||
CTR_Code GetFault_OverTemp(int ¶m);
|
||||
CTR_Code GetFault_UnderVoltage(int ¶m);
|
||||
CTR_Code GetFault_ForLim(int ¶m);
|
||||
CTR_Code GetFault_RevLim(int ¶m);
|
||||
CTR_Code GetFault_HardwareFailure(int ¶m);
|
||||
CTR_Code GetFault_ForSoftLim(int ¶m);
|
||||
CTR_Code GetFault_RevSoftLim(int ¶m);
|
||||
CTR_Code GetStckyFault_OverTemp(int ¶m);
|
||||
CTR_Code GetStckyFault_UnderVoltage(int ¶m);
|
||||
CTR_Code GetStckyFault_ForLim(int ¶m);
|
||||
CTR_Code GetStckyFault_RevLim(int ¶m);
|
||||
CTR_Code GetStckyFault_ForSoftLim(int ¶m);
|
||||
CTR_Code GetStckyFault_RevSoftLim(int ¶m);
|
||||
CTR_Code GetAppliedThrottle(int ¶m);
|
||||
CTR_Code GetCloseLoopErr(int ¶m);
|
||||
CTR_Code GetFeedbackDeviceSelect(int ¶m);
|
||||
CTR_Code GetModeSelect(int ¶m);
|
||||
CTR_Code GetLimitSwitchEn(int ¶m);
|
||||
CTR_Code GetLimitSwitchClosedFor(int ¶m);
|
||||
CTR_Code GetLimitSwitchClosedRev(int ¶m);
|
||||
CTR_Code GetSensorPosition(int ¶m);
|
||||
CTR_Code GetSensorVelocity(int ¶m);
|
||||
CTR_Code GetCurrent(double ¶m);
|
||||
CTR_Code GetBrakeIsEnabled(int ¶m);
|
||||
CTR_Code GetEncPosition(int ¶m);
|
||||
CTR_Code GetEncVel(int ¶m);
|
||||
CTR_Code GetEncIndexRiseEvents(int ¶m);
|
||||
CTR_Code GetQuadApin(int ¶m);
|
||||
CTR_Code GetQuadBpin(int ¶m);
|
||||
CTR_Code GetQuadIdxpin(int ¶m);
|
||||
CTR_Code GetAnalogInWithOv(int ¶m);
|
||||
CTR_Code GetAnalogInVel(int ¶m);
|
||||
CTR_Code GetTemp(double ¶m);
|
||||
CTR_Code GetBatteryV(double ¶m);
|
||||
CTR_Code GetResetCount(int ¶m);
|
||||
CTR_Code GetResetFlags(int ¶m);
|
||||
CTR_Code GetFirmVers(int ¶m);
|
||||
CTR_Code SetDemand(int param);
|
||||
CTR_Code SetOverrideLimitSwitchEn(int param);
|
||||
CTR_Code SetFeedbackDeviceSelect(int param);
|
||||
CTR_Code SetRevMotDuringCloseLoopEn(int param);
|
||||
CTR_Code SetOverrideBrakeType(int param);
|
||||
CTR_Code SetModeSelect(int param);
|
||||
CTR_Code SetModeSelect(int modeSelect,int demand);
|
||||
CTR_Code SetProfileSlotSelect(int param);
|
||||
CTR_Code SetRampThrottle(int param);
|
||||
CTR_Code SetRevFeedbackSensor(int param);
|
||||
};
|
||||
extern "C" {
|
||||
void *c_TalonSRX_Create(int deviceNumber, int controlPeriodMs);
|
||||
void c_TalonSRX_Destroy(void *handle);
|
||||
CTR_Code c_TalonSRX_SetParam(void *handle, int paramEnum, double value);
|
||||
CTR_Code c_TalonSRX_RequestParam(void *handle, int paramEnum);
|
||||
CTR_Code c_TalonSRX_GetParamResponse(void *handle, int paramEnum, double *value);
|
||||
CTR_Code c_TalonSRX_GetParamResponseInt32(void *handle, int paramEnum, int *value);
|
||||
CTR_Code c_TalonSRX_SetStatusFrameRate(void *handle, unsigned frameEnum, unsigned periodMs);
|
||||
CTR_Code c_TalonSRX_ClearStickyFaults(void *handle);
|
||||
CTR_Code c_TalonSRX_GetFault_OverTemp(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_UnderVoltage(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_ForLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_RevLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_HardwareFailure(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_ForSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFault_RevSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_OverTemp(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_UnderVoltage(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_ForLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_RevLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_ForSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetStckyFault_RevSoftLim(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetAppliedThrottle(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetCloseLoopErr(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFeedbackDeviceSelect(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetModeSelect(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchEn(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchClosedFor(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetLimitSwitchClosedRev(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetSensorPosition(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetSensorVelocity(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetCurrent(void *handle, double *param);
|
||||
CTR_Code c_TalonSRX_GetBrakeIsEnabled(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetEncPosition(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetEncVel(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetEncIndexRiseEvents(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetQuadApin(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetQuadBpin(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetQuadIdxpin(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetAnalogInWithOv(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetAnalogInVel(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetTemp(void *handle, double *param);
|
||||
CTR_Code c_TalonSRX_GetBatteryV(void *handle, double *param);
|
||||
CTR_Code c_TalonSRX_GetResetCount(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetResetFlags(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_GetFirmVers(void *handle, int *param);
|
||||
CTR_Code c_TalonSRX_SetDemand(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetOverrideLimitSwitchEn(void *handle, int param);
|
||||
CTR_Code c_TalonSRX_SetFeedbackDeviceSelect(void *handle, int param);
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,98 +1,101 @@
|
||||
#include "CtreCanNode.h"
|
||||
#include "NetworkCommunication/CANSessionMux.h"
|
||||
#include <string.h> // memset
|
||||
#include <unistd.h> // usleep
|
||||
|
||||
static const UINT32 kFullMessageIDMask = 0x1fffffff;
|
||||
|
||||
CtreCanNode::CtreCanNode(UINT8 deviceNumber)
|
||||
{
|
||||
_deviceNumber = deviceNumber;
|
||||
}
|
||||
CtreCanNode::~CtreCanNode()
|
||||
{
|
||||
}
|
||||
void CtreCanNode::RegisterRx(uint32_t arbId)
|
||||
{
|
||||
/* no need to do anything, we just use new API to poll last received message */
|
||||
}
|
||||
void CtreCanNode::RegisterTx(uint32_t arbId, uint32_t periodMs)
|
||||
{
|
||||
int32_t status = 0;
|
||||
|
||||
txJob_t job;
|
||||
job.arbId = arbId;
|
||||
job.periodMs = periodMs;
|
||||
_txJobs[arbId] = job;
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage( job.arbId,
|
||||
job.toSend,
|
||||
8,
|
||||
job.periodMs,
|
||||
&status);
|
||||
}
|
||||
timespec diff(const timespec & start, const timespec & end)
|
||||
{
|
||||
timespec temp;
|
||||
if ((end.tv_nsec-start.tv_nsec)<0) {
|
||||
temp.tv_sec = end.tv_sec-start.tv_sec-1;
|
||||
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
|
||||
} else {
|
||||
temp.tv_sec = end.tv_sec-start.tv_sec;
|
||||
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
CTR_Code CtreCanNode::GetRx(uint32_t arbId,uint8_t * dataBytes, uint32_t timeoutMs)
|
||||
{
|
||||
CTR_Code retval = CTR_OKAY;
|
||||
int32_t status = 0;
|
||||
uint8_t len = 0;
|
||||
uint32_t timeStamp;
|
||||
/* cap timeout at 999ms */
|
||||
if(timeoutMs > 999)
|
||||
timeoutMs = 999;
|
||||
FRC_NetworkCommunication_CANSessionMux_receiveMessage(&arbId,kFullMessageIDMask,dataBytes,&len,&timeStamp,&status);
|
||||
if(status == 0){
|
||||
/* fresh update */
|
||||
rxEvent_t & r = _rxRxEvents[arbId]; /* lookup entry or make a default new one with all zeroes */
|
||||
clock_gettime(2,&r.time); /* fill in time */
|
||||
memcpy(r.bytes, dataBytes, 8); /* fill in databytes */
|
||||
}else{
|
||||
/* did not get the message */
|
||||
rxRxEvents_t::iterator i = _rxRxEvents.find(arbId);
|
||||
if(i == _rxRxEvents.end()){
|
||||
/* we've never gotten this mesage */
|
||||
retval = CTR_RxTimeout;
|
||||
/* fill caller's buffer with zeros */
|
||||
memset(dataBytes,0,8);
|
||||
}else{
|
||||
/* we've gotten this message before but not recently */
|
||||
memcpy(dataBytes,i->second.bytes,8);
|
||||
/* get the time now */
|
||||
struct timespec temp;
|
||||
clock_gettime(2,&temp); /* get now */
|
||||
/* how long has it been? */
|
||||
temp = diff(i->second.time,temp); /* temp = now - last */
|
||||
if(temp.tv_sec > 0){
|
||||
retval = CTR_RxTimeout;
|
||||
}else if(temp.tv_nsec > ((int32_t)timeoutMs*1000*1000)){
|
||||
retval = CTR_RxTimeout;
|
||||
}else {
|
||||
/* our last update was recent enough */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
void CtreCanNode::FlushTx(uint32_t arbId)
|
||||
{
|
||||
int32_t status = 0;
|
||||
txJobs_t::iterator iter = _txJobs.find(arbId);
|
||||
if(iter != _txJobs.end())
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage( iter->second.arbId,
|
||||
iter->second.toSend,
|
||||
8,
|
||||
iter->second.periodMs,
|
||||
&status);
|
||||
}
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
|
||||
#include "CtreCanNode.h"
|
||||
#include "NetworkCommunication/CANSessionMux.h"
|
||||
#include <string.h> // memset
|
||||
#include <unistd.h> // usleep
|
||||
|
||||
static const UINT32 kFullMessageIDMask = 0x1fffffff;
|
||||
|
||||
CtreCanNode::CtreCanNode(UINT8 deviceNumber)
|
||||
{
|
||||
_deviceNumber = deviceNumber;
|
||||
}
|
||||
CtreCanNode::~CtreCanNode()
|
||||
{
|
||||
}
|
||||
void CtreCanNode::RegisterRx(uint32_t arbId)
|
||||
{
|
||||
/* no need to do anything, we just use new API to poll last received message */
|
||||
}
|
||||
void CtreCanNode::RegisterTx(uint32_t arbId, uint32_t periodMs)
|
||||
{
|
||||
int32_t status = 0;
|
||||
|
||||
txJob_t job = {0};
|
||||
job.arbId = arbId;
|
||||
job.periodMs = periodMs;
|
||||
_txJobs[arbId] = job;
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage( job.arbId,
|
||||
job.toSend,
|
||||
8,
|
||||
job.periodMs,
|
||||
&status);
|
||||
}
|
||||
timespec diff(const timespec & start, const timespec & end)
|
||||
{
|
||||
timespec temp;
|
||||
if ((end.tv_nsec-start.tv_nsec)<0) {
|
||||
temp.tv_sec = end.tv_sec-start.tv_sec-1;
|
||||
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
|
||||
} else {
|
||||
temp.tv_sec = end.tv_sec-start.tv_sec;
|
||||
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
CTR_Code CtreCanNode::GetRx(uint32_t arbId,uint8_t * dataBytes, uint32_t timeoutMs)
|
||||
{
|
||||
CTR_Code retval = CTR_OKAY;
|
||||
int32_t status = 0;
|
||||
uint8_t len = 0;
|
||||
uint32_t timeStamp;
|
||||
/* cap timeout at 999ms */
|
||||
if(timeoutMs > 999)
|
||||
timeoutMs = 999;
|
||||
FRC_NetworkCommunication_CANSessionMux_receiveMessage(&arbId,kFullMessageIDMask,dataBytes,&len,&timeStamp,&status);
|
||||
if(status == 0){
|
||||
/* fresh update */
|
||||
rxEvent_t & r = _rxRxEvents[arbId]; /* lookup entry or make a default new one with all zeroes */
|
||||
clock_gettime(2,&r.time); /* fill in time */
|
||||
memcpy(r.bytes, dataBytes, 8); /* fill in databytes */
|
||||
}else{
|
||||
/* did not get the message */
|
||||
rxRxEvents_t::iterator i = _rxRxEvents.find(arbId);
|
||||
if(i == _rxRxEvents.end()){
|
||||
/* we've never gotten this mesage */
|
||||
retval = CTR_RxTimeout;
|
||||
/* fill caller's buffer with zeros */
|
||||
memset(dataBytes,0,8);
|
||||
}else{
|
||||
/* we've gotten this message before but not recently */
|
||||
memcpy(dataBytes,i->second.bytes,8);
|
||||
/* get the time now */
|
||||
struct timespec temp;
|
||||
clock_gettime(2,&temp); /* get now */
|
||||
/* how long has it been? */
|
||||
temp = diff(i->second.time,temp); /* temp = now - last */
|
||||
if(temp.tv_sec > 0){
|
||||
retval = CTR_RxTimeout;
|
||||
}else if(temp.tv_nsec > ((int32_t)timeoutMs*1000*1000)){
|
||||
retval = CTR_RxTimeout;
|
||||
}else {
|
||||
/* our last update was recent enough */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
void CtreCanNode::FlushTx(uint32_t arbId)
|
||||
{
|
||||
int32_t status = 0;
|
||||
txJobs_t::iterator iter = _txJobs.find(arbId);
|
||||
if(iter != _txJobs.end())
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage( iter->second.arbId,
|
||||
iter->second.toSend,
|
||||
8,
|
||||
iter->second.periodMs,
|
||||
&status);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,121 +1,116 @@
|
||||
#ifndef CtreCanNode_H_
|
||||
#define CtreCanNode_H_
|
||||
#include "ctre.h" //BIT Defines + Typedefs
|
||||
#include "NetworkCommunication/CANSessionMux.h" //CAN Comm
|
||||
#include <pthread.h>
|
||||
#include <map>
|
||||
#include <string.h> // memcpy
|
||||
#include <sys/time.h>
|
||||
class CtreCanNode
|
||||
{
|
||||
public:
|
||||
CtreCanNode(UINT8 deviceNumber);
|
||||
~CtreCanNode();
|
||||
|
||||
UINT8 GetDeviceNumber()
|
||||
{
|
||||
return _deviceNumber;
|
||||
}
|
||||
protected:
|
||||
|
||||
|
||||
template <typename T> class txTask{
|
||||
public:
|
||||
uint32_t arbId;
|
||||
T * toSend;
|
||||
T * operator -> ()
|
||||
{
|
||||
return toSend;
|
||||
}
|
||||
T & operator*()
|
||||
{
|
||||
return *toSend;
|
||||
}
|
||||
bool IsEmpty()
|
||||
{
|
||||
if(toSend == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <typename T> class recMsg{
|
||||
public:
|
||||
uint32_t arbId;
|
||||
uint8_t bytes[8];
|
||||
CTR_Code err;
|
||||
T * operator -> ()
|
||||
{
|
||||
return (T *)bytes;
|
||||
}
|
||||
T & operator*()
|
||||
{
|
||||
return *(T *)bytes;
|
||||
}
|
||||
};
|
||||
UINT8 _deviceNumber;
|
||||
void RegisterRx(uint32_t arbId);
|
||||
void RegisterTx(uint32_t arbId, uint32_t periodMs);
|
||||
|
||||
CTR_Code GetRx(uint32_t arbId,uint8_t * dataBytes,uint32_t timeoutMs);
|
||||
void FlushTx(uint32_t arbId);
|
||||
|
||||
template<typename T> txTask<T> GetTx(uint32_t arbId)
|
||||
{
|
||||
txTask<T> retval = {0};
|
||||
txJobs_t::iterator i = _txJobs.find(arbId);
|
||||
if(i != _txJobs.end()){
|
||||
retval.arbId = i->second.arbId;
|
||||
retval.toSend = (T*)i->second.toSend;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
template<class T> void FlushTx(T & par)
|
||||
{
|
||||
FlushTx(par.arbId);
|
||||
}
|
||||
|
||||
template<class T> recMsg<T> GetRx(uint32_t arbId, uint32_t timeoutMs)
|
||||
{
|
||||
recMsg<T> retval;
|
||||
retval.err = GetRx(arbId,retval.bytes, timeoutMs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class txJob_t {
|
||||
public:
|
||||
uint32_t arbId;
|
||||
uint8_t toSend[8];
|
||||
uint32_t periodMs;
|
||||
txJob_t() : arbId(0),periodMs(0)
|
||||
{
|
||||
for(unsigned i=0;i<sizeof(toSend);++i)
|
||||
toSend[i] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class rxEvent_t{
|
||||
public:
|
||||
uint8_t bytes[8];
|
||||
struct timespec time;
|
||||
rxEvent_t()
|
||||
{
|
||||
bytes[0] = 0;
|
||||
bytes[1] = 0;
|
||||
bytes[2] = 0;
|
||||
bytes[3] = 0;
|
||||
bytes[4] = 0;
|
||||
bytes[5] = 0;
|
||||
bytes[6] = 0;
|
||||
bytes[7] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<uint32_t,txJob_t> txJobs_t;
|
||||
txJobs_t _txJobs;
|
||||
|
||||
typedef std::map<uint32_t,rxEvent_t> rxRxEvents_t;
|
||||
rxRxEvents_t _rxRxEvents;
|
||||
};
|
||||
#endif
|
||||
#ifndef CtreCanNode_H_
|
||||
#define CtreCanNode_H_
|
||||
#include "ctre.h" //BIT Defines + Typedefs
|
||||
#include <NetworkCommunication/CANSessionMux.h> //CAN Comm
|
||||
#include <pthread.h>
|
||||
#include <map>
|
||||
#include <string.h> // memcpy
|
||||
#include <sys/time.h>
|
||||
class CtreCanNode
|
||||
{
|
||||
public:
|
||||
CtreCanNode(UINT8 deviceNumber);
|
||||
~CtreCanNode();
|
||||
|
||||
UINT8 GetDeviceNumber()
|
||||
{
|
||||
return _deviceNumber;
|
||||
}
|
||||
protected:
|
||||
|
||||
|
||||
template <typename T> class txTask{
|
||||
public:
|
||||
uint32_t arbId;
|
||||
T * toSend;
|
||||
T * operator -> ()
|
||||
{
|
||||
return toSend;
|
||||
}
|
||||
T & operator*()
|
||||
{
|
||||
return *toSend;
|
||||
}
|
||||
bool IsEmpty()
|
||||
{
|
||||
if(toSend == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <typename T> class recMsg{
|
||||
public:
|
||||
uint32_t arbId;
|
||||
uint8_t bytes[8];
|
||||
CTR_Code err;
|
||||
T * operator -> ()
|
||||
{
|
||||
return (T *)bytes;
|
||||
}
|
||||
T & operator*()
|
||||
{
|
||||
return *(T *)bytes;
|
||||
}
|
||||
};
|
||||
UINT8 _deviceNumber;
|
||||
void RegisterRx(uint32_t arbId);
|
||||
void RegisterTx(uint32_t arbId, uint32_t periodMs);
|
||||
|
||||
CTR_Code GetRx(uint32_t arbId,uint8_t * dataBytes,uint32_t timeoutMs);
|
||||
void FlushTx(uint32_t arbId);
|
||||
|
||||
template<typename T> txTask<T> GetTx(uint32_t arbId)
|
||||
{
|
||||
txTask<T> retval = {0, nullptr};
|
||||
txJobs_t::iterator i = _txJobs.find(arbId);
|
||||
if(i != _txJobs.end()){
|
||||
retval.arbId = i->second.arbId;
|
||||
retval.toSend = (T*)i->second.toSend;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
template<class T> void FlushTx(T & par)
|
||||
{
|
||||
FlushTx(par.arbId);
|
||||
}
|
||||
|
||||
template<class T> recMsg<T> GetRx(uint32_t arbId, uint32_t timeoutMs)
|
||||
{
|
||||
recMsg<T> retval;
|
||||
retval.err = GetRx(arbId,retval.bytes, timeoutMs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class txJob_t {
|
||||
public:
|
||||
uint32_t arbId;
|
||||
uint8_t toSend[8];
|
||||
uint32_t periodMs;
|
||||
};
|
||||
|
||||
class rxEvent_t{
|
||||
public:
|
||||
uint8_t bytes[8];
|
||||
struct timespec time;
|
||||
rxEvent_t()
|
||||
{
|
||||
bytes[0] = 0;
|
||||
bytes[1] = 0;
|
||||
bytes[2] = 0;
|
||||
bytes[3] = 0;
|
||||
bytes[4] = 0;
|
||||
bytes[5] = 0;
|
||||
bytes[6] = 0;
|
||||
bytes[7] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<uint32_t,txJob_t> txJobs_t;
|
||||
txJobs_t _txJobs;
|
||||
|
||||
typedef std::map<uint32_t,rxEvent_t> rxRxEvents_t;
|
||||
rxRxEvents_t _rxRxEvents;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -6,11 +6,15 @@
|
||||
#define STATUS_1 0x8041400
|
||||
#define STATUS_2 0x8041440
|
||||
#define STATUS_3 0x8041480
|
||||
#define STATUS_ENERGY 0x8041740
|
||||
|
||||
#define CONTROL_1 0x08041C00 /* PDP_Control_ClearStats */
|
||||
|
||||
#define EXPECTED_RESPONSE_TIMEOUT_MS (50)
|
||||
#define GET_STATUS1() CtreCanNode::recMsg<PdpStatus1_t> rx = GetRx<PdpStatus1_t>(STATUS_1,EXPECTED_RESPONSE_TIMEOUT_MS)
|
||||
#define GET_STATUS2() CtreCanNode::recMsg<PdpStatus2_t> rx = GetRx<PdpStatus2_t>(STATUS_2,EXPECTED_RESPONSE_TIMEOUT_MS)
|
||||
#define GET_STATUS3() CtreCanNode::recMsg<PdpStatus3_t> rx = GetRx<PdpStatus3_t>(STATUS_3,EXPECTED_RESPONSE_TIMEOUT_MS)
|
||||
#define GET_STATUS_ENERGY() CtreCanNode::recMsg<PDP_Status_Energy_t> rx = GetRx<PDP_Status_Energy_t>(STATUS_ENERGY,EXPECTED_RESPONSE_TIMEOUT_MS)
|
||||
|
||||
/* encoder/decoders */
|
||||
typedef struct _PdpStatus1_t{
|
||||
@@ -56,6 +60,18 @@ typedef struct _PdpStatus3_t{
|
||||
unsigned busVoltage:8;
|
||||
unsigned temp:8;
|
||||
}PdpStatus3_t;
|
||||
typedef struct _PDP_Status_Energy_t {
|
||||
unsigned TmeasMs_likelywillbe20ms_:8;
|
||||
unsigned TotalCurrent_125mAperunit_h8:8;
|
||||
unsigned Power_125mWperunit_h4:4;
|
||||
unsigned TotalCurrent_125mAperunit_l4:4;
|
||||
unsigned Power_125mWperunit_m8:8;
|
||||
unsigned Energy_125mWPerUnitXTmeas_h4:4;
|
||||
unsigned Power_125mWperunit_l4:4;
|
||||
unsigned Energy_125mWPerUnitXTmeas_mh8:8;
|
||||
unsigned Energy_125mWPerUnitXTmeas_ml8:8;
|
||||
unsigned Energy_125mWPerUnitXTmeas_l8:8;
|
||||
} PDP_Status_Energy_t ;
|
||||
|
||||
PDP::PDP(UINT8 deviceNumber): CtreCanNode(deviceNumber)
|
||||
{
|
||||
@@ -128,6 +144,69 @@ CTR_Code PDP::GetTemperature(double &tempC)
|
||||
tempC = (double)raw * 1.03250836957542 - 67.8564500484966;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code PDP::GetTotalCurrent(double ¤tAmps)
|
||||
{
|
||||
GET_STATUS_ENERGY();
|
||||
uint32_t raw;
|
||||
raw = rx->TotalCurrent_125mAperunit_h8;
|
||||
raw <<= 4;
|
||||
raw |= rx->TotalCurrent_125mAperunit_l4;
|
||||
currentAmps = 0.125 * raw;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code PDP::GetTotalPower(double &powerWatts)
|
||||
{
|
||||
GET_STATUS_ENERGY();
|
||||
uint32_t raw;
|
||||
raw = rx->Power_125mWperunit_h4;
|
||||
raw <<= 8;
|
||||
raw |= rx->Power_125mWperunit_m8;
|
||||
raw <<= 4;
|
||||
raw |= rx->Power_125mWperunit_l4;
|
||||
powerWatts = 0.125 * raw;
|
||||
return rx.err;
|
||||
}
|
||||
CTR_Code PDP::GetTotalEnergy(double &energyJoules)
|
||||
{
|
||||
GET_STATUS_ENERGY();
|
||||
uint32_t raw;
|
||||
raw = rx->Energy_125mWPerUnitXTmeas_h4;
|
||||
raw <<= 8;
|
||||
raw |= rx->Energy_125mWPerUnitXTmeas_mh8;
|
||||
raw <<= 8;
|
||||
raw |= rx->Energy_125mWPerUnitXTmeas_ml8;
|
||||
raw <<= 8;
|
||||
raw |= rx->Energy_125mWPerUnitXTmeas_l8;
|
||||
energyJoules = 0.125 * raw; /* mW integrated every TmeasMs */
|
||||
energyJoules *= 0.001; /* convert from mW to W */
|
||||
energyJoules *= rx->TmeasMs_likelywillbe20ms_; /* multiplied by TmeasMs = joules */
|
||||
return rx.err;
|
||||
}
|
||||
/* Clear sticky faults.
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
*/
|
||||
CTR_Code PDP::ClearStickyFaults()
|
||||
{
|
||||
int32_t status = 0;
|
||||
uint8_t pdpControl[] = { 0x80 }; /* only bit set is ClearStickyFaults */
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_1 | GetDeviceNumber(), pdpControl, sizeof(pdpControl), 0, &status);
|
||||
if(status)
|
||||
return CTR_TxFailed;
|
||||
return CTR_OKAY;
|
||||
}
|
||||
|
||||
/* Reset Energy Signals
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
*/
|
||||
CTR_Code PDP::ResetEnergy()
|
||||
{
|
||||
int32_t status = 0;
|
||||
uint8_t pdpControl[] = { 0x40 }; /* only bit set is ResetEnergy */
|
||||
FRC_NetworkCommunication_CANSessionMux_sendMessage(CONTROL_1 | GetDeviceNumber(), pdpControl, sizeof(pdpControl), 0, &status);
|
||||
if(status)
|
||||
return CTR_TxFailed;
|
||||
return CTR_OKAY;
|
||||
}
|
||||
//------------------ C interface --------------------------------------------//
|
||||
extern "C" {
|
||||
void * c_PDP_Init(void)
|
||||
|
||||
@@ -38,6 +38,19 @@ public:
|
||||
* @Param - status - Temperature of PDP in Centigrade / Celcius (C)
|
||||
*/
|
||||
CTR_Code GetTemperature(double &status);
|
||||
|
||||
CTR_Code GetTotalCurrent(double ¤tAmps);
|
||||
CTR_Code GetTotalPower(double &powerWatts);
|
||||
CTR_Code GetTotalEnergy(double &energyJoules);
|
||||
/* Clear sticky faults.
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
*/
|
||||
CTR_Code ClearStickyFaults();
|
||||
|
||||
/* Reset Energy Signals
|
||||
* @Return - CTR_Code - Error code (if any)
|
||||
*/
|
||||
CTR_Code ResetEnergy();
|
||||
private:
|
||||
uint64_t ReadCurrents(uint8_t api);
|
||||
};
|
||||
|
||||
@@ -38,23 +38,13 @@ typedef unsigned int UINT;
|
||||
typedef unsigned long ULONG;
|
||||
|
||||
typedef enum {
|
||||
CTR_OKAY, //No Error - Function executed as expected
|
||||
CTR_RxTimeout, /*
|
||||
* Receive Timeout
|
||||
*
|
||||
* No module-specific CAN frames have been received in
|
||||
* the last 50ms. Function returns the latest received data
|
||||
* but may be STALE DATA.
|
||||
*/
|
||||
CTR_TxTimeout, /*
|
||||
* Transmission Timeout
|
||||
*
|
||||
* No module-specific CAN frames were transmitted in
|
||||
* the last 50ms. Parameters passed in by the user are loaded
|
||||
* for next transmission but have not sent.
|
||||
*/
|
||||
CTR_InvalidParamValue,
|
||||
CTR_UnexpectedArbId,
|
||||
CTR_OKAY, //!< No Error - Function executed as expected
|
||||
CTR_RxTimeout, //!< CAN frame has not been received within specified period of time.
|
||||
CTR_TxTimeout, //!< Not used.
|
||||
CTR_InvalidParamValue, //!< Caller passed an invalid param
|
||||
CTR_UnexpectedArbId, //!< Specified CAN Id is invalid.
|
||||
CTR_TxFailed, //!< Could not transmit the CAN frame.
|
||||
CTR_SigNotUpdated, //!< Have not received an value response for signal.
|
||||
}CTR_Code;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
#ifndef __I2C_LIB_H__
|
||||
#define __I2C_LIB_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int i2clib_open(const char *device);
|
||||
void i2clib_close(int handle);
|
||||
int i2clib_read(int handle, uint8_t dev_addr, char *recv_buf, int32_t recv_size);
|
||||
int i2clib_write(int handle, uint8_t dev_addr, const char *send_buf, int32_t send_size);
|
||||
int i2clib_writeread(int handle, uint8_t dev_addr, const char *send_buf, int32_t send_size, char *recv_buf, int32_t recv_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __I2C_LIB_H__ */
|
||||
@@ -1,12 +1,9 @@
|
||||
#ifndef __SPI_LIB_H__
|
||||
#define __SPI_LIB_H__
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int spilib_open(const char *device);
|
||||
void spilib_close(int handle);
|
||||
int spilib_setspeed(int handle, uint32_t speed);
|
||||
@@ -15,11 +12,8 @@ int spilib_setopts(int handle, int msb_first, int sample_on_trailing, int clk_id
|
||||
int spilib_read(int handle, char *recv_buf, int32_t size);
|
||||
int spilib_write(int handle, const char *send_buf, int32_t size);
|
||||
int spilib_writeread(int handle, const char *send_buf, char *recv_buf, int32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_LIB_H__ */
|
||||
|
||||
|
||||
#endif /* __SPI_LIB_H__ */
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ni-libraries/libniimaqdx.so
Executable file → Normal file
BIN
ni-libraries/libniimaqdx.so
Executable file → Normal file
Binary file not shown.
2
ni-libraries/libniimaqdx.so.14
Normal file
2
ni-libraries/libniimaqdx.so.14
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libniimaqdx.so.14.0 )
|
||||
2
ni-libraries/libniimaqdx.so.14.0
Normal file
2
ni-libraries/libniimaqdx.so.14.0
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libniimaqdx.so.14.0.0 )
|
||||
BIN
ni-libraries/libniimaqdx.so.14.0.0
Executable file
BIN
ni-libraries/libniimaqdx.so.14.0.0
Executable file
Binary file not shown.
BIN
ni-libraries/libnivision.so
Executable file → Normal file
BIN
ni-libraries/libnivision.so
Executable file → Normal file
Binary file not shown.
2
ni-libraries/libnivision.so.14
Normal file
2
ni-libraries/libnivision.so.14
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivision.so.14.0 )
|
||||
2
ni-libraries/libnivision.so.14.0
Normal file
2
ni-libraries/libnivision.so.14.0
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivision.so.14.0.0 )
|
||||
BIN
ni-libraries/libnivision.so.14.0.0
Executable file
BIN
ni-libraries/libnivision.so.14.0.0
Executable file
Binary file not shown.
BIN
ni-libraries/libnivissvc.so
Executable file → Normal file
BIN
ni-libraries/libnivissvc.so
Executable file → Normal file
Binary file not shown.
2
ni-libraries/libnivissvc.so.14
Normal file
2
ni-libraries/libnivissvc.so.14
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivissvc.so.14.0 )
|
||||
2
ni-libraries/libnivissvc.so.14.0
Normal file
2
ni-libraries/libnivissvc.so.14.0
Normal file
@@ -0,0 +1,2 @@
|
||||
OUTPUT_FORMAT(elf32-littlearm)
|
||||
GROUP ( libnivissvc.so.14.0.0 )
|
||||
BIN
ni-libraries/libnivissvc.so.14.0.0
Executable file
BIN
ni-libraries/libnivissvc.so.14.0.0
Executable file
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,11 @@ lib.dir = $(prefix)/lib
|
||||
plugin.dir = $(lib.dir)/frcsim/plugins
|
||||
build.dir = build
|
||||
|
||||
ext = .so
|
||||
ifeq ($(shell uname), Darwin)
|
||||
ext = .dylib
|
||||
endif
|
||||
|
||||
.PHONY : all build copy-plugins clean docs clean-docs
|
||||
|
||||
all: build copy-plugins
|
||||
@@ -20,15 +25,15 @@ build:
|
||||
|
||||
copy-plugins:
|
||||
mkdir -p plugins
|
||||
cp msgs/build/libgz_msgs.so plugins
|
||||
cp dc_motor/build/libgz_dc_motor.so plugins
|
||||
cp pneumatic_piston/build/libgz_pneumatic_piston.so plugins
|
||||
cp potentiometer/build/libgz_potentiometer.so plugins
|
||||
cp rangefinder/build/libgz_rangefinder.so plugins
|
||||
cp encoder/build/libgz_encoder.so plugins
|
||||
cp gyro/build/libgz_gyro.so plugins
|
||||
cp limit_switch/build/libgz_limit_switch.so plugins
|
||||
cp clock/build/libgz_clock.so plugins
|
||||
cp msgs/build/libgz_msgs$(ext) plugins
|
||||
cp dc_motor/build/libgz_dc_motor$(ext) plugins
|
||||
cp pneumatic_piston/build/libgz_pneumatic_piston$(ext) plugins
|
||||
cp potentiometer/build/libgz_potentiometer$(ext) plugins
|
||||
cp rangefinder/build/libgz_rangefinder$(ext) plugins
|
||||
cp encoder/build/libgz_encoder$(ext) plugins
|
||||
cp gyro/build/libgz_gyro$(ext) plugins
|
||||
cp limit_switch/build/libgz_limit_switch$(ext) plugins
|
||||
cp clock/build/libgz_clock$(ext) plugins
|
||||
|
||||
clean: clean-docs
|
||||
cd msgs && make clean
|
||||
|
||||
@@ -7,11 +7,12 @@ if (PKG_CONFIG_FOUND)
|
||||
endif()
|
||||
|
||||
find_package(gazebo REQUIRED)
|
||||
find_library(GZ_MSGS libgz_msgs.so ../msgs/build)
|
||||
find_package(Boost COMPONENTS system REQUIRED)
|
||||
find_library(GZ_MSGS NAMES gz_msgs PATHS ../msgs/build)
|
||||
|
||||
file(GLOB_RECURSE SRC_FILES src/*.cpp)
|
||||
include_directories(src ${Boost_INCLUDE_DIR} ${GAZEBO_INCLUDE_DIRS} ../msgs/src)
|
||||
add_library(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
|
||||
link_directories(${GAZEBO_LIBRARY_DIRS} ../msgs/build/)
|
||||
target_link_libraries(${PROJECT_NAME} ${GZ_MSGS} ${GAZEBO_LIBRARIES})
|
||||
target_link_libraries(${PROJECT_NAME} ${GZ_MSGS} ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES})
|
||||
|
||||
@@ -3,6 +3,11 @@ lib.dir = $(prefix)/lib
|
||||
plugin.dir = $(lib.dir)/frcsim/plugins
|
||||
build.dir = build
|
||||
|
||||
ext = .so
|
||||
ifeq ($(shell uname), Darwin)
|
||||
ext = .dylib
|
||||
endif
|
||||
|
||||
all:
|
||||
mkdir -p $(build.dir)
|
||||
cd ${build.dir} && cmake .. && make
|
||||
@@ -12,4 +17,4 @@ clean:
|
||||
|
||||
install: all
|
||||
mkdir -p $(DESTDIR)$(plugin.dir)
|
||||
install $(build.dir)/libgz_clock.so $(DESTDIR)$(plugin.dir)
|
||||
install $(build.dir)/libgz_clock$(ext) $(DESTDIR)$(plugin.dir)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user