Merge a fix for a Task bug which breaks the RWLock test
diff --git a/aos/atom_code/input/FRCComm.h b/aos/atom_code/input/FRCComm.h
index 14c4602..98619de 100644
--- a/aos/atom_code/input/FRCComm.h
+++ b/aos/atom_code/input/FRCComm.h
@@ -124,7 +124,6 @@
UINT32 FPGAChecksum3;
char versionData[8];
-};
-
+} __attribute__((packed));
#endif
diff --git a/aos/atom_code/output/MotorOutput.cpp b/aos/atom_code/output/MotorOutput.cpp
deleted file mode 100644
index ecbdf8b..0000000
--- a/aos/atom_code/output/MotorOutput.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "aos/atom_code/output/MotorOutput.h"
-
-#include <math.h>
-
-#include "aos/common/Configuration.h"
-#include "aos/aos_core.h"
-#include "aos/common/byteorder.h"
-#include "aos/common/control_loop/Timing.h"
-#include "aos/atom_code/messages/DriverStationDisplay.h"
-
-namespace aos {
-
-MotorOutput::MotorOutput() : sock(NetworkPort::kMotors,
- configuration::GetIPAddress(configuration::NetworkDevice::kCRIO)) {}
-
-void MotorOutput::Run() {
- while (true) {
- time::PhasedLoopXMS(5, 1000);
- RunIteration();
-
- // doesn't matter if multiple instances end up running this loop at the same
- // time because the queue handles the race conditions
- while (true) {
- const aos::DriverStationDisplay *line = aos::DriverStationDisplay::GetNext();
- if (line != NULL) {
- AddDSLine(line->line, line->data);
- LOG(DEBUG, "got a line %hhd that said '%s'\n", line->line, line->data);
- aos::DriverStationDisplay::Free(line);
- } else {
- break;
- }
- }
-
- if (sock.SendHoldMsg() == -1) {
- LOG(WARNING, "sending outputs failed\n");
- continue;
- } else {
- LOG(DEBUG, "sent outputs\n");
- }
- }
-}
-
-int MotorOutput::AddMotor(char type, uint8_t num, float value) {
- if (sock.hold_msg_len_ + 4 > sock.MAX_MSG) {
- return -1;
- }
- sock.hold_msg_[sock.hold_msg_len_ ++] = type;
- sock.hold_msg_[sock.hold_msg_len_ ++] = num;
- to_network(&value, &sock.hold_msg_[sock.hold_msg_len_]);
- sock.hold_msg_len_ += 4;
- return 0;
-}
-int MotorOutput::AddSolenoid(uint8_t port, bool on) {
- if (sock.hold_msg_len_ + 3 > sock.MAX_MSG) {
- return -1;
- }
- sock.hold_msg_[sock.hold_msg_len_ ++] = 's';
- sock.hold_msg_[sock.hold_msg_len_ ++] = port;
- sock.hold_msg_[sock.hold_msg_len_ ++] = on ? 1 : 0;
- return 0;
-}
-
-int MotorOutput::AddDSLine(uint8_t line, const char *data) {
- size_t data_len = strlen(data); // doesn't include terminating NULL
- if (sock.hold_msg_len_ + 3 + data_len > sock.MAX_MSG) {
- return -1;
- }
-
- sock.hold_msg_[sock.hold_msg_len_ ++] = 'd';
- sock.hold_msg_[sock.hold_msg_len_ ++] = line;
- sock.hold_msg_[sock.hold_msg_len_ ++] = data_len;
- memcpy(&sock.hold_msg_[sock.hold_msg_len_], data, data_len);
- sock.hold_msg_len_ += data_len;
- return 0;
-}
-
-} // namespace aos
-
diff --git a/aos/atom_code/output/MotorOutput.h b/aos/atom_code/output/MotorOutput.h
deleted file mode 100644
index 925139a..0000000
--- a/aos/atom_code/output/MotorOutput.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
-#define AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
-
-#include <stdint.h>
-#include <string.h>
-#include <algorithm>
-#include <string>
-
-#include "aos/common/network/SendSocket.h"
-#include "aos/common/byteorder.h"
-#include "aos/common/type_traits.h"
-
-namespace aos {
-
-class MotorOutput {
- public:
- MotorOutput();
- void Run();
-
- // Constants for the first argument of AddMotor.
- static const char VICTOR = 'v';
- static const char TALON = 't';
-
- protected:
- // num is 1-indexed
- int AddMotor(char type, uint8_t num, float value);
- int AddSolenoid(uint8_t num, bool on);
- int AddDSLine(uint8_t line, const char *data);
- // loop_queuegroup is expected to be a control loop queue group.
- // This function will fetch the latest goal and serve it.
- template <class T>
- int AddControlLoopGoal(T *loop_queuegroup);
-
- virtual void RunIteration() = 0;
-
- private:
- SendSocket sock;
-};
-
-template <class T>
-int MotorOutput::AddControlLoopGoal(T *loop_queuegroup) {
- typedef typename std::remove_reference<
- decltype(*(loop_queuegroup->goal.MakeMessage().get()))>::type GoalType;
- // TODO(aschuh): This assumes a static serialized message size.
- const uint16_t size = GoalType::Size();
- if (sock.hold_msg_len_ + 4 + 1 + size > sock.MAX_MSG) {
- return -1;
- }
-
- const bool zero = !loop_queuegroup->goal.FetchLatest();
-
- sock.hold_msg_[sock.hold_msg_len_ ++] = 'g';
- const uint32_t hash = loop_queuegroup->hash();
-
- // Place the loop hash at the front.
- to_network(&hash, &sock.hold_msg_[sock.hold_msg_len_]);
- sock.hold_msg_len_ += 4;
-
- if (zero) {
- GoalType zero_message;
- zero_message.Zero();
- zero_message.Serialize(&sock.hold_msg_[sock.hold_msg_len_]);
- sock.hold_msg_len_ += size;
- return -1;
- } else {
- loop_queuegroup->goal->Serialize(&sock.hold_msg_[sock.hold_msg_len_]);
- sock.hold_msg_len_ += size;
- return 0;
- }
-}
-
-} // namespace aos
-
-#endif
diff --git a/aos/atom_code/output/motor_output.cc b/aos/atom_code/output/motor_output.cc
new file mode 100644
index 0000000..d7022f5
--- /dev/null
+++ b/aos/atom_code/output/motor_output.cc
@@ -0,0 +1,85 @@
+#include "aos/atom_code/output/motor_output.h"
+
+#include <math.h>
+
+#include "aos/common/Configuration.h"
+#include "aos/aos_core.h"
+#include "aos/common/control_loop/Timing.h"
+
+namespace aos {
+
+// 255 = 2.5ms, 0 = 0.5ms (or something close to that)
+// got 211->210 as first transition that made a speed difference and 53->54 on
+// the other side
+// going 2 to each side to make sure we get the full range
+const MotorOutput::MotorControllerBounds MotorOutput::kTalonBounds
+ {213, 135, 132, 129, 50};
+
+uint8_t MotorOutput::MotorControllerBounds::Map(double value) const {
+ if (value == 0.0) return kCenter;
+ if (value > 0.0) {
+ return static_cast<uint8_t>(kDeadbandMax + (value * (kMax - kDeadbandMax)) +
+ 0.5);
+ } else {
+ return static_cast<uint8_t>(kDeadbandMin + (value * (kDeadbandMin - kMin)) +
+ 0.5);
+ }
+}
+
+MotorOutput::MotorOutput() : socket_(NetworkPort::kMotors,
+ configuration::GetIPAddress(configuration::NetworkDevice::kCRIO)) {}
+
+void MotorOutput::Run() {
+ while (true) {
+ time::PhasedLoopXMS(5, 1000);
+
+ values_.digital_module = -1;
+ // 0 means output disabled.
+ memset(&values_.pwm_outputs, 0x00, sizeof(values_.pwm_outputs));
+ values_.digital_output_enables = 0;
+ values_.digital_output_values = 0;
+ values_.pressure_switch_channel = 0;
+ values_.compressor_channel = 0;
+ values_.solenoid_module = -1;
+ values_.solenoid_values = 0;
+
+ RunIteration();
+
+ values_.digital_output_enables = hton(values_.digital_output_enables);
+ values_.digital_output_values = hton(values_.digital_output_values);
+
+ values_.FillInHashValue();
+ values_.hash_value = hton(values_.hash_value);
+
+ if (socket_.Send(&values_, sizeof(values_)) != sizeof(values_)) {
+ LOG(WARNING, "sending outputs failed\n");
+ } else {
+ LOG(DEBUG, "sent outputs\n");
+ }
+ }
+}
+
+void MotorOutput::SetSolenoid(uint8_t channel, bool set) {
+ if (set) {
+ values_.solenoid_values |= 1 << (channel - 1);
+ }
+}
+
+void MotorOutput::SetPWMOutput(uint8_t channel, double value,
+ const MotorControllerBounds &bounds) {
+ values_.pwm_outputs[channel - 1] = bounds.Map(value);
+}
+
+void MotorOutput::DisablePWMOutput(uint8_t channel) {
+ values_.pwm_outputs[channel - 1] = 0;
+}
+
+void MotorOutput::SetDigitalOutput(uint8_t channel, bool value) {
+ const uint8_t shift_amount = 15 - channel;
+ values_.digital_output_enables |= 1 << shift_amount;
+ if (value) {
+ values_.digital_output_values |= 1 << shift_amount;
+ }
+}
+
+} // namespace aos
diff --git a/aos/atom_code/output/motor_output.h b/aos/atom_code/output/motor_output.h
new file mode 100644
index 0000000..2c1af1d
--- /dev/null
+++ b/aos/atom_code/output/motor_output.h
@@ -0,0 +1,68 @@
+#ifndef AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
+#define AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <algorithm>
+#include <string>
+
+#include "aos/common/network/SendSocket.h"
+#include "aos/common/byteorder.h"
+#include "aos/common/type_traits.h"
+#include "aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h"
+
+namespace aos {
+
+// A class for sending output values to a cRIO.
+// values_ gets completely reset each time through, so RunIteration() needs to
+// set everything each time.
+class MotorOutput {
+ public:
+ MotorOutput();
+ void Run();
+
+ // A container for holding the constants that WPILib uses for each type of
+ // motor controller to map floating point values to 1-byte PWM values to
+ // actually use.
+ struct MotorControllerBounds {
+ // What 1.0 maps to.
+ const uint8_t kMax;
+ // The smallest value to map a positive speed to.
+ const uint8_t kDeadbandMax;
+ // What 0.0 maps to.
+ const uint8_t kCenter;
+ // The biggest value to map a negative speed to.
+ const uint8_t kDeadbandMin;
+ // What -1.0 maps to.
+ const uint8_t kMin;
+
+ // Applies the mapping.
+ uint8_t Map(double value) const;
+ };
+
+ protected:
+ // Brian got the values here by trying values with hardware on 11/23/12.
+ static const MotorControllerBounds kTalonBounds;
+
+ // Helper methods for filling out values_.
+ // All channels are the 1-indexed numbers that usually go into WPILib.
+ void SetSolenoid(uint8_t channel, bool set);
+ void SetPWMOutput(uint8_t channel, double value,
+ const MotorControllerBounds &bounds);
+ void DisablePWMOutput(uint8_t channel);
+ void SetDigitalOutput(uint8_t channel, bool value);
+
+ // The struct that's going to get sent over.
+ // Gets reset (everything set so that it won't do anything) each time through.
+ NetworkRobotValues values_;
+
+ private:
+ // Subclasses need to actually fill out values_ here.
+ virtual void RunIteration() = 0;
+
+ SendSocket socket_;
+};
+
+} // namespace aos
+
+#endif
diff --git a/aos/atom_code/output/motor_output_test.cc b/aos/atom_code/output/motor_output_test.cc
new file mode 100644
index 0000000..1e8d5fb
--- /dev/null
+++ b/aos/atom_code/output/motor_output_test.cc
@@ -0,0 +1,21 @@
+#include "gtest/gtest.h"
+
+#include "aos/atom_code/output/motor_output.h"
+
+namespace aos {
+namespace testing {
+
+TEST(MotorControllerBoundsTest, Limits) {
+ MotorOutput::MotorControllerBounds test_bounds
+ {200, 135, 128, 126, 110};
+ EXPECT_EQ(test_bounds.Map(1.0), test_bounds.kMax);
+ EXPECT_EQ(test_bounds.Map(-1.0), test_bounds.kMin);
+ EXPECT_EQ(test_bounds.Map(0.0), test_bounds.kCenter);
+ EXPECT_EQ(test_bounds.Map(0.55), 171);
+ EXPECT_EQ(test_bounds.Map(0.5), 168);
+ EXPECT_EQ(test_bounds.Map(0.45), 164);
+ EXPECT_EQ(test_bounds.Map(-0.5), 118);
+}
+
+} // namespace testing
+} // namespace aos
diff --git a/aos/atom_code/output/output.gyp b/aos/atom_code/output/output.gyp
index 65232b6..2dfcd0e 100644
--- a/aos/atom_code/output/output.gyp
+++ b/aos/atom_code/output/output.gyp
@@ -24,14 +24,28 @@
'target_name': 'motor_output',
'type': 'static_library',
'sources': [
- 'MotorOutput.cpp',
+ 'motor_output.cc',
],
'dependencies': [
'<(AOS)/common/network/network.gyp:socket',
- '<(AOS)/build/aos.gyp:aos_core',
+ '<(AOS)/common/common.gyp:common',
+ '<(AOS)/common/common.gyp:timing',
+ '<(EXTERNALS):WPILib-NetworkRobotValues',
],
'export_dependent_settings': [
'<(AOS)/common/network/network.gyp:socket',
+ '<(EXTERNALS):WPILib-NetworkRobotValues',
+ ],
+ },
+ {
+ 'target_name': 'motor_output_test',
+ 'type': 'executable',
+ 'sources': [
+ 'motor_output_test.cc',
+ ],
+ 'dependencies': [
+ 'motor_output',
+ '<(EXTERNALS):gtest',
],
},
],
diff --git a/aos/atom_code/starter/netconsole.cc b/aos/atom_code/starter/netconsole.cc
new file mode 100644
index 0000000..e2ba2b4
--- /dev/null
+++ b/aos/atom_code/starter/netconsole.cc
@@ -0,0 +1,219 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/Configuration.h"
+
+namespace aos {
+namespace {
+
+struct FDsToCopy {
+ const int input;
+ const int output;
+
+ const struct sockaddr_in *const interface_address;
+};
+
+void *FDCopyThread(void *to_copy_in) {
+ FDsToCopy *to_copy = static_cast<FDsToCopy *>(to_copy_in);
+
+ char buffer[32768];
+ ssize_t position = 0;
+ while (true) {
+ assert(position >= 0);
+ assert(position <= static_cast<ssize_t>(sizeof(buffer)));
+ if (position != sizeof(buffer)) {
+ ssize_t read_bytes;
+ bool good_data = true;
+ if (to_copy->interface_address != NULL) {
+ char control_buffer[0x100];
+ struct msghdr header;
+ memset(static_cast<void *>(&header), 0, sizeof(header));
+ header.msg_control = control_buffer;
+ header.msg_controllen = sizeof(control_buffer);
+ struct iovec iovecs[1];
+ iovecs[0].iov_base = buffer + position;
+ iovecs[0].iov_len = position - sizeof(buffer);
+ header.msg_iov = iovecs;
+ header.msg_iovlen = sizeof(iovecs) / sizeof(iovecs[0]);
+ read_bytes = recvmsg(to_copy->input, &header, 0);
+ if (read_bytes != -1) {
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&header);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&header, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_PKTINFO) {
+ unsigned char *data = CMSG_DATA(cmsg);
+ struct in_pktinfo *pktinfo;
+ memcpy(&pktinfo, &data, sizeof(void *));
+ good_data = pktinfo->ipi_spec_dst.s_addr ==
+ to_copy->interface_address->sin_addr.s_addr;
+ }
+ }
+ }
+ } else {
+ read_bytes = read(to_copy->input,
+ buffer + position, position - sizeof(buffer));
+ }
+ if (read_bytes == -1) {
+ if (errno != EINTR) {
+ LOG(FATAL, "read(%d, %p, %zd) failed with %d: %s\n",
+ to_copy->input, buffer + position, position - sizeof(buffer),
+ errno, strerror(errno));
+ }
+ } else if (read_bytes == 0 && to_copy->interface_address == NULL) {
+ // read(2) says that this means EOF
+ return NULL;
+ }
+ if (good_data) {
+ position += read_bytes;
+ }
+ }
+
+ assert(position >= 0);
+ assert(position <= static_cast<ssize_t>(sizeof(buffer)));
+ if (position > 0) {
+ ssize_t sent_bytes = write(to_copy->output, buffer, position);
+ if (sent_bytes == -1) {
+ if (errno != EINTR) {
+ LOG(FATAL, "write(%d, %p, %zd) failed with %d: %s\n",
+ to_copy->output, buffer, position, errno, strerror(errno));
+ }
+ } else if (sent_bytes != 0) {
+ if (sent_bytes == position) {
+ position = 0;
+ } else {
+ memmove(buffer, buffer + sent_bytes, position - sent_bytes);
+ position -= sent_bytes;
+ }
+ }
+ }
+ }
+}
+
+int NetconsoleMain(int argc, char **argv) {
+ logging::Init();
+
+ int input, output;
+ if (argc > 1) {
+ output = open(argv[1], O_APPEND | O_CREAT | O_WRONLY | O_TRUNC, 0666);
+ if (output == -1) {
+ if (errno == EACCES || errno == ELOOP || errno == ENOSPC ||
+ errno == ENOTDIR || errno == EROFS || errno == ETXTBSY) {
+ fprintf(stderr, "Opening output file '%s' failed because of %s.\n",
+ argv[1], strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ LOG(FATAL, "open('%s', stuff, 0644) failed with %d: %s\n", argv[1],
+ errno, strerror(errno));
+ }
+ fprintf(stderr, "Writing output to '%s'.\n", argv[1]);
+ input = -1;
+ fprintf(stderr, "Not taking any input.\n");
+ } else {
+ output = STDOUT_FILENO;
+ fprintf(stderr, "Writing output to stdout.\n");
+ input = STDIN_FILENO;
+ fprintf(stderr, "Reading stdin.\n");
+ }
+
+ int on = 1;
+
+ int from_crio = socket(AF_INET, SOCK_DGRAM, 0);
+ if (from_crio == -1) {
+ LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
+ errno, strerror(errno));
+ }
+ if (setsockopt(from_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+ LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
+ on, from_crio, errno, strerror(errno));
+ }
+ if (setsockopt(from_crio, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
+ LOG(FATAL, "SOL_SOCKET::SO_BROADCAST=%d(%d) failed with %d: %s\n",
+ on, from_crio, errno, strerror(errno));
+ }
+ if (setsockopt(from_crio, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)) == -1) {
+ LOG(FATAL, "IPROTO_IP::IP_PKTINFO=%d(%d) failed with %d: %s\n",
+ on, from_crio, errno, strerror(errno));
+ }
+ union {
+ struct sockaddr_in in;
+ struct sockaddr addr;
+ } address;
+ address.in.sin_family = AF_INET;
+ address.in.sin_port = htons(6666);
+ address.in.sin_addr.s_addr = INADDR_ANY;
+ if (bind(from_crio, &address.addr, sizeof(address)) == -1) {
+ LOG(FATAL, "bind(%d, %p, %zu) failed with %d: %s\n",
+ from_crio, &address.addr, sizeof(address), errno, strerror(errno));
+ }
+
+ pthread_t input_thread, output_thread;
+
+ if (input != -1) {
+ int to_crio = socket(AF_INET, SOCK_DGRAM, 0);
+ if (to_crio == -1) {
+ LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
+ errno, strerror(errno));
+ }
+ if (setsockopt(to_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+ LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
+ on, to_crio, errno, strerror(errno));
+ }
+ address.in.sin_port = htons(6668);
+ if (inet_aton(
+ configuration::GetIPAddress(configuration::NetworkDevice::kCRIO),
+ &address.in.sin_addr) == 0) {
+ LOG(FATAL, "inet_aton(%s, %p) failed with %d: %s\n",
+ configuration::GetIPAddress(configuration::NetworkDevice::kCRIO),
+ &address.in.sin_addr, errno, strerror(errno));
+ }
+ if (connect(to_crio, &address.addr, sizeof(address)) == -1) {
+ LOG(FATAL, "connect(%d, %p, %zu) failed with %d: %s\n",
+ to_crio, &address.addr, sizeof(address), errno, strerror(errno));
+ }
+ FDsToCopy input_fds{input, to_crio, NULL};
+ if (pthread_create(&input_thread, NULL, FDCopyThread, &input_fds) == -1) {
+ LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
+ &input_thread, FDCopyThread, &input_fds, errno, strerror(errno));
+ }
+ }
+
+ fprintf(stderr, "Using cRIO IP %s.\n",
+ configuration::GetIPAddress(configuration::NetworkDevice::kCRIO));
+
+ if (inet_aton(
+ configuration::GetIPAddress(configuration::NetworkDevice::kSelf),
+ &address.in.sin_addr) == 0) {
+ LOG(FATAL, "inet_aton(%s, %p) failed with %d: %s\n",
+ configuration::GetIPAddress(configuration::NetworkDevice::kSelf),
+ &address.in.sin_addr, errno, strerror(errno));
+ }
+ FDsToCopy output_fds{from_crio, output, &address.in};
+ if (pthread_create(&output_thread, NULL, FDCopyThread, &output_fds) == -1) {
+ LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
+ &output_thread, FDCopyThread, &output_fds, errno, strerror(errno));
+ }
+
+ // input_thread will finish when stdin gets an EOF
+ if (pthread_join((input == -1) ? output_thread : input_thread, NULL) == -1) {
+ LOG(FATAL, "pthread_join(a_thread, NULL) failed with %d: %s\n",
+ errno, strerror(errno));
+ }
+ exit(EXIT_SUCCESS);
+}
+
+} // namespace
+} // namespace aos
+
+int main(int argc, char **argv) {
+ return ::aos::NetconsoleMain(argc, argv);
+}
diff --git a/aos/atom_code/starter/starter.gyp b/aos/atom_code/starter/starter.gyp
index 91b5870..183ec9e 100644
--- a/aos/atom_code/starter/starter.gyp
+++ b/aos/atom_code/starter/starter.gyp
@@ -1,6 +1,17 @@
{
'targets': [
{
+ 'target_name': 'netconsole',
+ 'type': 'executable',
+ 'sources': [
+ 'netconsole.cc',
+ ],
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/common/common.gyp:common',
+ ],
+ },
+ {
'target_name': 'starter_exe',
'type': 'executable',
'sources': [
diff --git a/aos/atom_code/starter/starter.sh b/aos/atom_code/starter/starter.sh
index 63ccd42..33906c3 100755
--- a/aos/atom_code/starter/starter.sh
+++ b/aos/atom_code/starter/starter.sh
@@ -17,4 +17,4 @@
#DUMPCAP_STDOUT_FILE=$(date "/home/driver/tmp/robot_logs/stdout_dumpcap.%F_%H-%M-%S")
#chrt -o 0 bash -c "dumpcap -i eth0 -w ${DUMPCAP_LOG_FILE} -f 'not port 8080 and not net 10.9.71.13' > ${DUMPCAP_STDOUT_FILE}" &
-#chrt -o 0 bash -c 'while true; do socat UDP4-RECV:6666,reuseaddr STDIO > /home/driver/tmp/robot_logs/netconsole-`date +%s`; done' &
+chrt -o 0 bash -c 'while true; /home/driver/robot_code/bin/netconsole /home/driver/tmp/robot_logs/netconsole-`date +%s`; done' &
diff --git a/aos/build/aos.gyp b/aos/build/aos.gyp
index 9e464a5..1996cd8 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -8,7 +8,6 @@
'conditions': [
['OS=="crio"', {
'libaos_source_files': [
- '<(AOS)/crio/Talon.cpp',
],
}, {
'libaos_source_files': [
diff --git a/aos/build/aos.gypi b/aos/build/aos.gypi
index 662a419..3f71412 100644
--- a/aos/build/aos.gypi
+++ b/aos/build/aos.gypi
@@ -44,6 +44,8 @@
'defines': [
'__STDC_FORMAT_MACROS',
'_FORTIFY_SOURCE=2',
+ '__STDC_CONSTANT_MACROS',
+ '__STDC_LIMIT_MACROS',
],
'ldflags': [
'-pipe',
@@ -119,12 +121,14 @@
],
],
'ldflags': [
- '-mcpu=603',
+ '-mcpu=603e',
'-mstrict-align',
'-mlongcall',
],
'cflags': [
- '-mcpu=603',
+ # The Freescale MPC5200B (cRIO-FRC) and MPC5125 (cRIO-FRC II) both
+ # have MPC603e cores according to Freescale docs.
+ '-mcpu=603e',
'-mstrict-align',
'-mlongcall',
'-isystem', '<(aos_abs)/externals/gccdist/WindRiver/gnu/3.4.4-vxworks-6.3/x86-win32/lib/gcc/powerpc-wrs-vxworks/3.4.4/include/',
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 1d283a9..b4116f4 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -15,7 +15,8 @@
'../atom_code/core/core.gyp:*',
#'../atom_code/async_action:*', # TODO(brians) fix this broken test
'../atom_code/ipc_lib/ipc_lib.gyp:*',
- '../atom_code/starter/starter.gyp:*',
+ '../atom_code/starter/starter.gyp:starter_exe',
+ '../atom_code/starter/starter.gyp:netconsole',
'../crio/crio.gyp:unsafe_queue_test',
'../common/common.gyp:queue_test',
#'../common/messages/messages.gyp:*', # TODO(brians) did this test ever exist?
diff --git a/aos/build/build.sh b/aos/build/build.sh
index cc349d0..c1cf166 100755
--- a/aos/build/build.sh
+++ b/aos/build/build.sh
@@ -58,24 +58,32 @@
if [ "${ACTION}" == "clean" ]; then
rm -r ${OUTDIR}
else
- if [ "${ACTION}" != "deploy" -a "${ACTION}" != "tests" ]; then
- GYP_ACTION=${ACTION}
- else
- GYP_ACTION=
- fi
+ if [ "${ACTION}" != "deploy" -a "${ACTION}" != "tests" -a "${ACTION}" != "redeploy" ]; then
+ GYP_ACTION=${ACTION}
+ else
+ GYP_ACTION=
+ fi
${NINJA} -C ${OUTDIR}/Default ${GYP_ACTION}
- case ${ACTION} in
- deploy)
- [ ${PLATFORM} == atom ] && \
- rsync --progress -c -r --rsync-path=/home/driver/bin/rsync \
- ${OUTDIR}/Default/outputs/* \
- driver@`${AOS}/build/get_ip fitpc`:/home/driver/robot_code/bin
- [ ${PLATFORM} == crio ] && \
- ncftpput `${AOS}/build/get_ip robot` / \
- ${OUTDIR}/Default/lib/FRC_UserProgram.out
- ;;
- tests)
- find ${OUTDIR}/Default/tests -executable -exec {} \;
- ;;
- esac
+ if [[ ${ACTION} == deploy || ${ACTION} == redeploy ]]; then
+ [ ${PLATFORM} == atom ] && \
+ rsync --progress -c -r --rsync-path=/home/driver/bin/rsync \
+ ${OUTDIR}/Default/outputs/* \
+ driver@`${AOS}/build/get_ip fitpc`:/home/driver/robot_code/bin
+ [ ${PLATFORM} == crio ] && \
+ ncftpput `${AOS}/build/get_ip robot` / \
+ ${OUTDIR}/Default/lib/FRC_UserProgram.out
+ fi
+ if [[ ${ACTION} == redeploy ]]; then
+ if [[ ${PLATFORM} != crio ]]; then
+ echo "Platform ${PLATFORM} does not support redeploy." 1>&2
+ exit 1
+ fi
+ ${OUTDIR}/../out_atom/Default/outputs/netconsole <<"END"
+unld "FRC_UserProgram.out"
+ld < FRC_UserProgram.out
+END
+ fi
+ if [[ ${ACTION} == tests ]]; then
+ find ${OUTDIR}/Default/tests -executable -exec {} \;
+ fi
fi
diff --git a/aos/build/download_externals.sh b/aos/build/download_externals.sh
index 5e07d02..4f999b2 100755
--- a/aos/build/download_externals.sh
+++ b/aos/build/download_externals.sh
@@ -89,3 +89,10 @@
CFLAGS='-m32' CXXFLAGS='-m32' LDFLAGS='-m32' \
bash -c "cd ${LIBUSB_DIR} && ./configure \
--prefix=`readlink -f ${LIBUSB_PREFIX}` && make && make install"
+
+# get the LLVM Compiler-RT source
+COMPILER_RT_TAG=RELEASE_32/final
+COMPILER_RT_VERSION=`echo ${COMPILER_RT_TAG} | sed s:/:_:`
+COMPILER_RT_DIR=${EXTERNALS}/compiler-rt-${COMPILER_RT_VERSION}
+COMPILER_RT_URL=http://llvm.org/svn/llvm-project/compiler-rt/tags/${COMPILER_RT_TAG}
+[ -d ${COMPILER_RT_DIR} ] || svn checkout ${COMPILER_RT_URL} ${COMPILER_RT_DIR}
diff --git a/aos/build/externals.gyp b/aos/build/externals.gyp
index 0fa5fed..06bd225 100644
--- a/aos/build/externals.gyp
+++ b/aos/build/externals.gyp
@@ -13,6 +13,7 @@
'gflags_version': '2.0',
'libusb_version': '1.0.9',
'libusb_apiversion': '1.0',
+ 'compiler_rt_version': 'RELEASE_32_final',
},
'targets': [
{
@@ -42,6 +43,21 @@
},
},
{
+ 'target_name': 'WPILib-NetworkRobotValues',
+ 'type': 'static_library',
+ 'sources': [
+ '<(externals)/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp'
+ ],
+ 'include_dirs': [
+ '<(externals)/WPILib',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(externals)/WPILib',
+ ],
+ },
+ },
+ {
'target_name': 'onejar',
'type': 'none',
'direct_dependent_settings': {
@@ -187,4 +203,7 @@
},
},
],
+ 'includes': [
+ 'libgcc-additions/libgcc-additions.gypi',
+ ],
}
diff --git a/aos/build/libgcc-additions/README b/aos/build/libgcc-additions/README
new file mode 100644
index 0000000..6a9a665
--- /dev/null
+++ b/aos/build/libgcc-additions/README
@@ -0,0 +1,26 @@
+This directory contains the stuff necessary to deal with the fact that the
+ libgcc.a from the old GCC that the cRIO has doesn't have all of the functions
+ that newer GCCs expect from it.
+
+[extra functions necessary for 4.5.2]
+I generated this diff with `powerpc-wrs-vxworks-nm \
+ gccdist/WindRiver/gnu/3.4.4-vxworks-6.3/x86-win32/lib/gcc/powerpc-wrs-vxworks/3.4.4/libgcc.a \
+ | fgrep '000 T' | awk ' { print $NF }'` (and using the same command with the
+ gccdist.a from the 4.5.2 GCC.
+ I then gave the outputs from both of those to diff and edited out by hand the
+ functions that just moved.
+__powisf2
+__powidf2
+__mulsc3
+__muldc3
+__divsc3
+__divdc3
+__bswapsi2
+__bswapdi2
+__floatundisf
+__floatundidf
+__eprintf
+
+eprintf looks like it's not needed.
+Compiler-RT thinks that bswapsi2 and bswapdi2 are only needed on arm, so it
+ only has arm assembly for them.
diff --git a/aos/build/libgcc-additions/_bswapdi2.o b/aos/build/libgcc-additions/_bswapdi2.o
new file mode 100644
index 0000000..8bc6f56
--- /dev/null
+++ b/aos/build/libgcc-additions/_bswapdi2.o
Binary files differ
diff --git a/aos/build/libgcc-additions/_bswapsi2.o b/aos/build/libgcc-additions/_bswapsi2.o
new file mode 100644
index 0000000..4700ad8
--- /dev/null
+++ b/aos/build/libgcc-additions/_bswapsi2.o
Binary files differ
diff --git a/aos/build/libgcc-additions/libgcc-additions.gypi b/aos/build/libgcc-additions/libgcc-additions.gypi
new file mode 100644
index 0000000..afa81f6
--- /dev/null
+++ b/aos/build/libgcc-additions/libgcc-additions.gypi
@@ -0,0 +1,35 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'libgcc-4.5.2',
+ 'type': 'static_library',
+ 'variables': {
+ 'compiler-rt': '<(externals)/compiler-rt-<(compiler_rt_version)',
+ },
+ 'include_dirs': [
+ '<(compiler-rt)/lib',
+ ],
+ 'defines': [
+ '_YUGA_BIG_ENDIAN=1',
+ '_YUGA_LITTLE_ENDIAN=0',
+ 'UINT64_C(c)=c##ULL',
+ ],
+ 'sources': [
+ '<(compiler-rt)/lib/powisf2.c',
+ '<(compiler-rt)/lib/powidf2.c',
+ '<(compiler-rt)/lib/mulsc3.c',
+ '<(compiler-rt)/lib/muldc3.c',
+ '<(compiler-rt)/lib/divsc3.c',
+ '<(compiler-rt)/lib/divdc3.c',
+ #'<(compiler-rt)/lib/bswapsi2.c',
+ '_bswapsi2.o',
+ #'<(compiler-rt)/lib/bswapdi2.c',
+ '_bswapdi2.o',
+ '<(compiler-rt)/lib/floatundisf.c',
+ '<(compiler-rt)/lib/floatundidf.c',
+
+ 'libm.c',
+ ],
+ },
+ ],
+}
diff --git a/aos/build/libgcc-additions/libm.c b/aos/build/libgcc-additions/libm.c
new file mode 100644
index 0000000..ce04ce5
--- /dev/null
+++ b/aos/build/libgcc-additions/libm.c
@@ -0,0 +1,47 @@
+// Some of the Compiler-RT implementations we use want to use functions from
+// libm. However, GCC provides builtins for them, so they don't need to show up
+// in <math.h> Unfortunately, those GCC builtins seem to (sometimes, at least)
+// just emit calls to the functions themselves, so we have our own
+// implementations here. They might not be as fast as they could be, but they
+// are correct as far as I can tell.
+
+#include <stdint.h>
+
+double fmax(double a, double b) {
+ if (__builtin_isnan(a)) return b;
+ if (__builtin_isnan(b)) return a;
+ if (a > b) return a;
+ return b;
+}
+float fmaxf(float a, float b) {
+ if (__builtin_isnan(a)) return b;
+ if (__builtin_isnan(b)) return a;
+ if (a > b) return a;
+ return b;
+}
+
+double scalbn(double x, int exp) {
+ return x * (2 << exp);
+}
+float scalbnf(float x, int exp) {
+ return x * (2 << exp);
+}
+
+float logbf(float x) {
+ union {
+ float f;
+ int32_t d;
+ } converter;
+ converter.f = x;
+ int32_t ix = converter.d & 0x7fffffff;
+ int32_t rix;
+
+ if (ix == 0) {
+ return (float)-1.0 / __builtin_fabsf(x);
+ } else if (ix >= 0x7f800000) {
+ return x * x;
+ } else if (__builtin_expect((rix = ix >> 23) == 0, 0)) {
+ rix -= __builtin_clz(ix) - 9;
+ }
+ return (float)(rix - 127);
+}
diff --git a/aos/common/Configuration.cpp b/aos/common/Configuration.cpp
index 90de05d..f379aac 100644
--- a/aos/common/Configuration.cpp
+++ b/aos/common/Configuration.cpp
@@ -15,9 +15,32 @@
#include <ifaddrs.h>
#endif
-#include "aos/aos_core.h"
#ifndef __VXWORKS__
+#include "aos/common/logging/logging.h"
#include "aos/common/unique_malloc_ptr.h"
+#else
+#include <taskLib.h>
+#undef ERROR
+enum LogLevel {
+ DEBUG,
+ INFO,
+ WARNING,
+ ERROR = -1,
+ FATAL,
+};
+#define LOG(level, format, args...) do { \
+ fprintf(stderr, #level ": " format, ##args); \
+ if (level == FATAL) { \
+ printf("I am 0x%x suspending for debugging purposes.\n", taskIdSelf()); \
+ printf("\t`tt 0x%x` will give you a stack trace.\n", taskIdSelf()); \
+ fputs("\t`lkAddr` will reverse lookup a symbol for you.\n", stdout); \
+ fputs("\t`dbgHelp` and `help` have some useful commands in them.\n", stdout); \
+ taskSuspend(0); \
+ printf("You weren't supposed to resume 0x%x!!. Going to really die now.\n", \
+ taskIdSelf()); \
+ abort(); \
+ } \
+} while (0)
#endif
#include "aos/common/once.h"
@@ -142,6 +165,10 @@
return RawIPAddress(179);
case NetworkDevice::kCRIO:
return RawIPAddress(2);
+ case NetworkDevice::kSelf:
+ char *ret = static_cast<char *>(malloc(kMaxAddrLength));
+ if (!GetOwnIPAddress(ret, kMaxAddrLength)) return NULL;
+ return ret;
}
LOG(FATAL, "Unknown network device.");
return NULL;
diff --git a/aos/common/Configuration.h b/aos/common/Configuration.h
index 2f7e777..e1b7ef5 100644
--- a/aos/common/Configuration.h
+++ b/aos/common/Configuration.h
@@ -31,6 +31,8 @@
// The computer that the cRIO talks to.
kAtom,
kCRIO,
+ // Whatever device this is being run on.
+ kSelf,
};
// Returns the IP address to get to the specified machine.
// The return value should be passed to free(3) if it is no longer needed.
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 7ae3f61..2151064 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -47,7 +47,6 @@
'Configuration.cpp',
],
'dependencies': [
- '<(AOS)/build/aos.gyp:logging',
'once',
],
'export_dependent_settings': [
@@ -57,7 +56,12 @@
['OS=="crio"', {
'dependencies': [
'<(EXTERNALS):WPILib',
- ]}],
+ ],
+ }, {
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ }],
],
},
{
diff --git a/aos/common/network/SendSocket.cpp b/aos/common/network/SendSocket.cpp
index 78aca79..c30ec32 100644
--- a/aos/common/network/SendSocket.cpp
+++ b/aos/common/network/SendSocket.cpp
@@ -25,18 +25,7 @@
last_ret_ = 1;
}
- hold_msg_len_ = 0;
return last_ret_ = 0;
}
-int SendSocket::SendHoldMsg() {
- if (hold_msg_len_ > 0) {
- int val = Send(hold_msg_, hold_msg_len_);
- hold_msg_len_ = 0;
- return val;
- }
- return -1;
-}
-
-} // namespace aos
-
+} // namespace aos
diff --git a/aos/common/network/SendSocket.h b/aos/common/network/SendSocket.h
index a0c1fe2..fca904a 100644
--- a/aos/common/network/SendSocket.h
+++ b/aos/common/network/SendSocket.h
@@ -7,7 +7,6 @@
class SendSocket : public Socket {
public:
- //inline int Send(const void *buf, int length) { return Socket::Send(buf, length); }
// Connect must be called before use.
SendSocket() {}
// Calls Connect automatically.
@@ -15,11 +14,6 @@
Connect(port, robot_ip);
}
int Connect(NetworkPort port, const char *robot_ip, int type = SOCK_DGRAM);
-
- static const size_t MAX_MSG = 4096;
- char hold_msg_[MAX_MSG];
- size_t hold_msg_len_;
- int SendHoldMsg();
};
} // namespace aos
diff --git a/aos/crio/Talon.cpp b/aos/crio/Talon.cpp
deleted file mode 100644
index 54ef459..0000000
--- a/aos/crio/Talon.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "aos/crio/Talon.h"
-#include "WPILib/DigitalModule.h"
-
-Talon::Talon(UINT32 channel) : SafePWM(channel) {
- // 255 = 2.5ms, 0 = 0.5ms (or something close to that)
- // these numbers were determined empirically with real hardware by Brian
- // on 11/23/12
- // got 211->210 as first transition that made a speed difference and
- // 53->54 on the other side
- // going 2 to each side to make sure we get the full range
- SetBounds(213, 137, 132, 127, 50);
- // 1X = every 5.05ms, 2X and 4x are multiples of that
- SetPeriodMultiplier(kPeriodMultiplier_1X);
- SetRaw(m_centerPwm);
-}
-
-void Talon::Set(float speed, UINT8 /*syncGroup*/) { SetSpeed(speed); }
-float Talon::Get() { return GetSpeed(); }
-void Talon::Disable() { SetRaw(kPwmDisabled); }
-
-void Talon::PIDWrite(float output) { Set(output); }
diff --git a/aos/crio/Talon.h b/aos/crio/Talon.h
deleted file mode 100644
index 5942a77..0000000
--- a/aos/crio/Talon.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef AOS_CRIO_TALON_H_
-#define AOS_CRIO_TALON_H_
-
-#include "WPILib/SafePWM.h"
-#include "WPILib/SpeedController.h"
-#include "WPILib/PIDOutput.h"
-
-// Used for controlling a Talon speed controller. Non-standard API and
-// namespace so that the likely WPILib version will be drop-in replaceable.
-class Talon : public SafePWM, public SpeedController {
- public:
- explicit Talon(UINT32 channel);
-
- virtual void Set(float value, UINT8 syncGroup=0);
- virtual float Get();
- virtual void Disable();
-
- virtual void PIDWrite(float output);
-};
-
-#endif // AOS_CRIO_TALON_H_
diff --git a/aos/crio/motor_server/MotorControllerOutput.h b/aos/crio/motor_server/MotorControllerOutput.h
index 41c3546..6caebf7 100644
--- a/aos/crio/motor_server/MotorControllerOutput.h
+++ b/aos/crio/motor_server/MotorControllerOutput.h
@@ -3,8 +3,7 @@
#include "aos/crio/motor_server/OutputDevice.h"
-#include "aos/crio/Talon.h"
-
+#include "WPILib/Talon.h"
#include "WPILib/SpeedController.h"
#include "WPILib/Jaguar.h"
#include "WPILib/CANJaguar.h"
diff --git a/aos/externals/.gitignore b/aos/externals/.gitignore
index 7f6c98d..64c3acf 100644
--- a/aos/externals/.gitignore
+++ b/aos/externals/.gitignore
@@ -21,3 +21,4 @@
/libusb-1.0.9-prefix/
/libusb-1.0.9.tar.bz2
/libusb-1.0.9/
+/compiler-rt-RELEASE_32_final/
diff --git a/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp b/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp
index 9880e0e..2a4040e 100644
--- a/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp
+++ b/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp
@@ -73,7 +73,7 @@
*/
ADXL345_I2C::AllAxes ADXL345_I2C::GetAccelerations()
{
- AllAxes data = {0.0};
+ AllAxes data = {0.0, 0.0, 0.0};
INT16 rawData[3];
if (m_i2c)
{
diff --git a/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp b/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp
index 789448d..42d8dea 100644
--- a/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp
+++ b/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp
@@ -157,7 +157,7 @@
*/
ADXL345_SPI::AllAxes ADXL345_SPI::GetAccelerations()
{
- AllAxes data = {0.0};
+ AllAxes data = {0.0, 0.0, 0.0};
INT16 rawData[3];
if (m_spi)
{
diff --git a/aos/externals/WPILib/WPILib/AnalogChannel.cpp b/aos/externals/WPILib/WPILib/AnalogChannel.cpp
index 834837f..26708c1 100644
--- a/aos/externals/WPILib/WPILib/AnalogChannel.cpp
+++ b/aos/externals/WPILib/WPILib/AnalogChannel.cpp
@@ -38,9 +38,9 @@
}
snprintf(buf, 64, "Analog Input %d (Module: %d)", channel, moduleNumber);
- if (channels->Allocate((moduleNumber - 1) * kAnalogChannels + channel - 1, buf) == ~0ul)
+ if (channels->Allocate((moduleNumber - 1) * kAnalogChannels +
+ channel - 1, buf, this) == ~0ul)
{
- CloneError(channels);
return;
}
m_channel = channel;
@@ -86,7 +86,8 @@
*/
AnalogChannel::~AnalogChannel()
{
- channels->Free((m_module->GetNumber() - 1) * kAnalogChannels + m_channel - 1);
+ channels->Free((m_module->GetNumber() - 1) * kAnalogChannels + m_channel - 1,
+ this);
}
/**
diff --git a/aos/externals/WPILib/WPILib/AnalogModule.cpp b/aos/externals/WPILib/WPILib/AnalogModule.cpp
index d685c13..68e7940 100644
--- a/aos/externals/WPILib/WPILib/AnalogModule.cpp
+++ b/aos/externals/WPILib/WPILib/AnalogModule.cpp
@@ -14,7 +14,8 @@
const long AnalogModule::kDefaultOversampleBits;
const long AnalogModule::kDefaultAverageBits;
const float AnalogModule::kDefaultSampleRate;
-SEM_ID AnalogModule::m_registerWindowSemaphore = NULL;
+// Needs to be global since the protected resource spans both module singletons.
+ReentrantSemaphore AnalogModule::m_registerWindowSemaphore;
/**
* Get an instance of an Analog Module.
@@ -71,12 +72,6 @@
SetAverageBits(i + 1, kDefaultAverageBits);
SetOversampleBits(i + 1, kDefaultOversampleBits);
}
-
- if (m_registerWindowSemaphore == NULL)
- {
- // Needs to be global since the protected resource spans both module singletons.
- m_registerWindowSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
- }
}
/**
diff --git a/aos/externals/WPILib/WPILib/AnalogModule.h b/aos/externals/WPILib/WPILib/AnalogModule.h
index 3f69685..3ed44ca 100644
--- a/aos/externals/WPILib/WPILib/AnalogModule.h
+++ b/aos/externals/WPILib/WPILib/AnalogModule.h
@@ -48,7 +48,7 @@
virtual ~AnalogModule();
private:
- static SEM_ID m_registerWindowSemaphore;
+ static ReentrantSemaphore m_registerWindowSemaphore;
UINT32 GetNumActiveChannels();
UINT32 GetNumChannelsToActivate();
diff --git a/aos/externals/WPILib/WPILib/AnalogTrigger.cpp b/aos/externals/WPILib/WPILib/AnalogTrigger.cpp
index ffe45c1..2deeb54 100644
--- a/aos/externals/WPILib/WPILib/AnalogTrigger.cpp
+++ b/aos/externals/WPILib/WPILib/AnalogTrigger.cpp
@@ -21,10 +21,9 @@
void AnalogTrigger::InitTrigger(UINT8 moduleNumber, UINT32 channel)
{
Resource::CreateResourceObject(&triggers, tAnalogTrigger::kNumSystems);
- UINT32 index = triggers->Allocate("Analog Trigger");
+ UINT32 index = triggers->Allocate("Analog Trigger", this);
if (index == ~0ul)
{
- CloneError(triggers);
return;
}
m_index = (UINT8)index;
@@ -74,7 +73,7 @@
AnalogTrigger::~AnalogTrigger()
{
- triggers->Free(m_index);
+ triggers->Free(m_index, this);
delete m_trigger;
}
diff --git a/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h b/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h
index 35c82a5..0f8afe4 100644
--- a/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h
+++ b/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h
@@ -14,8 +14,6 @@
#ifndef __JaguarCANDriver_h__
#define __JaguarCANDriver_h__
-#include <VxWorks.h>
-
#ifdef __cplusplus
extern "C"
{
diff --git a/aos/externals/WPILib/WPILib/CANJaguar.cpp b/aos/externals/WPILib/WPILib/CANJaguar.cpp
index cf03370..ec55460 100644
--- a/aos/externals/WPILib/WPILib/CANJaguar.cpp
+++ b/aos/externals/WPILib/WPILib/CANJaguar.cpp
@@ -1223,7 +1223,7 @@
void CANJaguar::GetDescription(char *desc)
{
- sprintf(desc, "CANJaguar ID %d", m_deviceNumber);
+ snprintf(desc, 64, "CANJaguar ID %d", m_deviceNumber);
}
/**
diff --git a/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h b/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h
index 46786e8..d5008e1 100644
--- a/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h
+++ b/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h
@@ -12,9 +12,9 @@
tSystemInterface(){}
virtual ~tSystemInterface(){}
- virtual const uint16_t getExpectedFPGAVersion()=0;
- virtual const uint32_t getExpectedFPGARevision()=0;
- virtual const uint32_t * const getExpectedFPGASignature()=0;
+ virtual uint16_t getExpectedFPGAVersion()=0;
+ virtual uint32_t getExpectedFPGARevision()=0;
+ virtual uint32_t * getExpectedFPGASignature()=0;
virtual void getHardwareFpgaSignature(uint32_t *guid_ptr, tRioStatusCode *status)=0;
virtual uint32_t getLVHandle(tRioStatusCode *status)=0;
virtual uint32_t getHandle()=0;
diff --git a/aos/externals/WPILib/WPILib/Counter.cpp b/aos/externals/WPILib/WPILib/Counter.cpp
index a11c268..e7c17ad 100644
--- a/aos/externals/WPILib/WPILib/Counter.cpp
+++ b/aos/externals/WPILib/WPILib/Counter.cpp
@@ -20,10 +20,9 @@
void Counter::InitCounter(Mode mode)
{
Resource::CreateResourceObject(&counters, tCounter::kNumSystems);
- UINT32 index = counters->Allocate("Counter");
+ UINT32 index = counters->Allocate("Counter", this);
if (index == ~0ul)
{
- CloneError(counters);
return;
}
m_index = index;
@@ -183,7 +182,7 @@
}
delete m_counter;
m_counter = NULL;
- counters->Free(m_index);
+ counters->Free(m_index, this);
}
/**
diff --git a/aos/externals/WPILib/WPILib/Dashboard.cpp b/aos/externals/WPILib/WPILib/Dashboard.cpp
index c513baf..2235b99 100644
--- a/aos/externals/WPILib/WPILib/Dashboard.cpp
+++ b/aos/externals/WPILib/WPILib/Dashboard.cpp
@@ -29,7 +29,7 @@
{
m_userStatusData = new char[kMaxDashboardDataSize];
m_localBuffer = new char[kMaxDashboardDataSize];
- m_localPrintBuffer = new char[kMaxDashboardDataSize * 2];
+ m_localPrintBuffer = new char[kLocalPrintBufferSize];
m_localPrintBuffer[0] = 0;
m_packPtr = m_localBuffer;
m_printSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
@@ -275,7 +275,7 @@
va_start (args, writeFmt);
{
Synchronized sync(m_printSemaphore);
- vsprintf(m_localPrintBuffer + strlen(m_localPrintBuffer), writeFmt, args);
+ vsnprintf(m_localPrintBuffer + strlen(m_localPrintBuffer), kLocalPrintBufferSize - strlen(m_localPrintBuffer), writeFmt, args);
size = strlen(m_localPrintBuffer);
}
if (size > kMaxDashboardDataSize)
diff --git a/aos/externals/WPILib/WPILib/Dashboard.h b/aos/externals/WPILib/WPILib/Dashboard.h
index dfcf989..1856660 100644
--- a/aos/externals/WPILib/WPILib/Dashboard.h
+++ b/aos/externals/WPILib/WPILib/Dashboard.h
@@ -50,6 +50,7 @@
void Flush() {}
private:
static const INT32 kMaxDashboardDataSize = USER_STATUS_DATA_SIZE - sizeof(UINT32) * 3 - sizeof(UINT8); // 13 bytes needed for 3 size parameters and the sequence number
+ static const size_t kLocalPrintBufferSize = kMaxDashboardDataSize * 2;
// Usage Guidelines...
DISALLOW_COPY_AND_ASSIGN(Dashboard);
diff --git a/aos/externals/WPILib/WPILib/DigitalModule.cpp b/aos/externals/WPILib/WPILib/DigitalModule.cpp
index 3e7f728..d715c65 100644
--- a/aos/externals/WPILib/WPILib/DigitalModule.cpp
+++ b/aos/externals/WPILib/WPILib/DigitalModule.cpp
@@ -63,7 +63,7 @@
if (m_fpgaDIO->readLoopTiming(&localStatus) != kExpectedLoopTiming)
{
char err[128];
- sprintf(err, "DIO LoopTiming: %d, expecting: %d\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming);
+ snprintf(err, sizeof(err), "DIO LoopTiming: %d, expecting: %d\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming);
wpi_setWPIErrorWithContext(LoopTimingError, err);
}
m_fpgaDIO->writePWMConfig_Period(PWM::kDefaultPwmPeriod, &localStatus);
@@ -154,18 +154,7 @@
*/
void DigitalModule::SetRelayForward(UINT32 channel, bool on)
{
- tRioStatusCode localStatus = NiFpga_Status_Success;
- CheckRelayChannel(channel);
- {
- Synchronized sync(m_relaySemaphore);
- UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
- if (on)
- forwardRelays |= 1 << (channel - 1);
- else
- forwardRelays &= ~(1 << (channel - 1));
- m_fpgaDIO->writeSlowValue_RelayFwd(forwardRelays, &localStatus);
- }
- wpi_setError(localStatus);
+ SetRelaysForward(1 << (channel - 1), on ? 0xFF : 0x00);
}
/**
@@ -175,18 +164,47 @@
*/
void DigitalModule::SetRelayReverse(UINT32 channel, bool on)
{
- tRioStatusCode localStatus = NiFpga_Status_Success;
- CheckRelayChannel(channel);
- {
- Synchronized sync(m_relaySemaphore);
- UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
- if (on)
- reverseRelays |= 1 << (channel - 1);
- else
- reverseRelays &= ~(1 << (channel - 1));
- m_fpgaDIO->writeSlowValue_RelayRev(reverseRelays, &localStatus);
- }
- wpi_setError(localStatus);
+ SetRelaysReverse(1 << (channel - 1), on ? 0xFF : 0x00);
+}
+
+/**
+ * Set the state of multiple relays at the same time.
+ * For both parameters, 0b100000000 is channel 1 and 0b00000001 is channel 8.
+ * @param mask which relays to set
+ * @param values what to set the relays to
+ */
+void DigitalModule::SetRelaysForward(UINT8 mask, UINT8 values) {
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ {
+ Synchronized sync(m_relaySemaphore);
+ UINT8 current = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
+ // Clearr all of the bits that we're messing with first.
+ current &= ~mask;
+ // Then set only the ones that are supposed to be set.
+ current |= (mask & values);
+ m_fpgaDIO->writeSlowValue_RelayFwd(current, &localStatus);
+ }
+ wpi_setError(localStatus);
+}
+
+/**
+ * Set the state of multiple relays at the same time.
+ * For both parameters, 0b100000000 is channel 1 and 0b00000001 is channel 8.
+ * @param mask which relays to set
+ * @param values what to set the relays to
+ */
+void DigitalModule::SetRelaysReverse(UINT8 mask, UINT8 values) {
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ {
+ Synchronized sync(m_relaySemaphore);
+ UINT8 current = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
+ // Clearr all of the bits that we're messing with first.
+ current &= ~mask;
+ // Then set only the ones that are supposed to be set.
+ current |= (mask & values);
+ m_fpgaDIO->writeSlowValue_RelayRev(current, &localStatus);
+ }
+ wpi_setError(localStatus);
}
/**
@@ -248,7 +266,8 @@
{
char buf[64];
snprintf(buf, 64, "DIO %d (Module %d)", channel, m_moduleNumber);
- if (DIOChannels->Allocate(kDigitalChannels * (m_moduleNumber - 1) + channel - 1, buf) == ~0ul) return false;
+ if (DIOChannels->Allocate(kDigitalChannels * (m_moduleNumber - 1) +
+ channel - 1, buf, this) == ~0ul) return false;
tRioStatusCode localStatus = NiFpga_Status_Success;
{
Synchronized sync(m_digitalSemaphore);
@@ -276,7 +295,29 @@
*/
void DigitalModule::FreeDIO(UINT32 channel)
{
- DIOChannels->Free(kDigitalChannels * (m_moduleNumber - 1) + channel - 1);
+ DIOChannels->Free(kDigitalChannels * (m_moduleNumber - 1) + channel - 1,
+ this);
+}
+
+/**
+ * Write multiple digital I/O bits to the FPGA at the same time.
+ * For both parameters, 0x0001 is channel 16 and 0x8000 is channel 1.
+ *
+ * @param mask Which bits to modify.
+ * @param values What to set all of the bits in mask to.
+ */
+void DigitalModule::SetDIOs(UINT16 mask, UINT16 values) {
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ {
+ Synchronized sync(m_digitalSemaphore);
+ UINT16 current = m_fpgaDIO->readDO(&localStatus);
+ // Clear all of the bits that we're messing with first.
+ current &= ~mask;
+ // Then set only the ones that are supposed to be set.
+ current |= (mask & values);
+ m_fpgaDIO->writeDO(current, &localStatus);
+ }
+ wpi_setError(localStatus);
}
/**
@@ -286,29 +327,9 @@
* @param channel The Digital I/O channel
* @param value The state to set the digital channel (if it is configured as an output)
*/
-void DigitalModule::SetDIO(UINT32 channel, short value)
+void DigitalModule::SetDIO(UINT32 channel, bool value)
{
- if (value != 0 && value != 1)
- {
- wpi_setWPIError(NonBinaryDigitalValue);
- if (value != 0)
- value = 1;
- }
- tRioStatusCode localStatus = NiFpga_Status_Success;
- {
- Synchronized sync(m_digitalSemaphore);
- UINT16 currentDIO = m_fpgaDIO->readDO(&localStatus);
- if(value == 0)
- {
- currentDIO = currentDIO & ~(1 << RemapDigitalChannel(channel - 1));
- }
- else if (value == 1)
- {
- currentDIO = currentDIO | (1 << RemapDigitalChannel(channel - 1));
- }
- m_fpgaDIO->writeDO(currentDIO, &localStatus);
- }
- wpi_setError(localStatus);
+ SetDIOs(1 << RemapDigitalChannel(channel - 1), value ? 0xFFFF : 0x0000);
}
/**
@@ -421,7 +442,7 @@
/**
* Allocate a DO PWM Generator.
- * Allocate PWM generators so that they are not accidently reused.
+ * Allocate PWM generators so that they are not accidentally reused.
*
* @return PWM Generator refnum
*/
@@ -429,7 +450,7 @@
{
char buf[64];
snprintf(buf, 64, "DO_PWM (Module: %d)", m_moduleNumber);
- return DO_PWMGenerators[(m_moduleNumber - 1)]->Allocate(buf);
+ return DO_PWMGenerators[(m_moduleNumber - 1)]->Allocate(buf, this);
}
/**
@@ -440,7 +461,7 @@
void DigitalModule::FreeDO_PWM(UINT32 pwmGenerator)
{
if (pwmGenerator == ~0ul) return;
- DO_PWMGenerators[(m_moduleNumber - 1)]->Free(pwmGenerator);
+ DO_PWMGenerators[(m_moduleNumber - 1)]->Free(pwmGenerator, this);
}
/**
@@ -461,7 +482,7 @@
}
/**
- * Configure which DO channel the PWM siganl is output on
+ * Configure which DO channel the PWM signal is output on
*
* @param pwmGenerator The generator index reserved by AllocateDO_PWM()
* @param channel The Digital Output channel to output on
diff --git a/aos/externals/WPILib/WPILib/DigitalModule.h b/aos/externals/WPILib/WPILib/DigitalModule.h
index e80beb6..44f617f 100644
--- a/aos/externals/WPILib/WPILib/DigitalModule.h
+++ b/aos/externals/WPILib/WPILib/DigitalModule.h
@@ -29,13 +29,16 @@
void SetPWMPeriodScale(UINT32 channel, UINT32 squelchMask);
void SetRelayForward(UINT32 channel, bool on);
void SetRelayReverse(UINT32 channel, bool on);
+ void SetRelaysForward(UINT8 mask, UINT8 values);
+ void SetRelaysReverse(UINT8 mask, UINT8 values);
bool GetRelayForward(UINT32 channel);
UINT8 GetRelayForward();
bool GetRelayReverse(UINT32 channel);
UINT8 GetRelayReverse();
bool AllocateDIO(UINT32 channel, bool input);
void FreeDIO(UINT32 channel);
- void SetDIO(UINT32 channel, short value);
+ void SetDIOs(UINT16 mask, UINT16 values);
+ void SetDIO(UINT32 channel, bool value);
bool GetDIO(UINT32 channel);
UINT16 GetDIO();
bool GetDIODirection(UINT32 channel);
diff --git a/aos/externals/WPILib/WPILib/DigitalOutput.cpp b/aos/externals/WPILib/WPILib/DigitalOutput.cpp
index a571066..4f45ee7 100644
--- a/aos/externals/WPILib/WPILib/DigitalOutput.cpp
+++ b/aos/externals/WPILib/WPILib/DigitalOutput.cpp
@@ -214,10 +214,9 @@
void DigitalOutput::RequestInterrupts(tInterruptHandler handler, void *param)
{
if (StatusIsFatal()) return;
- UINT32 index = interruptsResource->Allocate("Sync Interrupt");
+ UINT32 index = interruptsResource->Allocate("Sync Interrupt", this);
if (index == ~0ul)
{
- CloneError(interruptsResource);
return;
}
m_interruptIndex = index;
@@ -245,10 +244,9 @@
void DigitalOutput::RequestInterrupts()
{
if (StatusIsFatal()) return;
- UINT32 index = interruptsResource->Allocate("Sync Interrupt");
+ UINT32 index = interruptsResource->Allocate("Sync Interrupt", this);
if (index == ~0ul)
{
- CloneError(interruptsResource);
return;
}
m_interruptIndex = index;
diff --git a/aos/externals/WPILib/WPILib/DigitalSource.cpp b/aos/externals/WPILib/WPILib/DigitalSource.cpp
index 3c1e3a8..862b439 100644
--- a/aos/externals/WPILib/WPILib/DigitalSource.cpp
+++ b/aos/externals/WPILib/WPILib/DigitalSource.cpp
@@ -24,7 +24,7 @@
{
delete m_manager;
delete m_interrupt;
- interruptsResource->Free(m_interruptIndex);
+ interruptsResource->Free(m_interruptIndex, this);
}
}
@@ -39,10 +39,9 @@
void DigitalSource::RequestInterrupts(tInterruptHandler handler, void *param)
{
if (StatusIsFatal()) return;
- UINT32 index = interruptsResource->Allocate("Async Interrupt");
+ UINT32 index = interruptsResource->Allocate("Async Interrupt", this);
if (index == ~0ul)
{
- CloneError(interruptsResource);
return;
}
m_interruptIndex = index;
@@ -70,10 +69,9 @@
void DigitalSource::RequestInterrupts()
{
if (StatusIsFatal()) return;
- UINT32 index = interruptsResource->Allocate("Sync Interrupt");
+ UINT32 index = interruptsResource->Allocate("Sync Interrupt", this);
if (index == ~0ul)
{
- CloneError(interruptsResource);
return;
}
m_interruptIndex = index;
diff --git a/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp b/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp
index d05c7ff..4b3e473 100644
--- a/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp
+++ b/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp
@@ -37,15 +37,15 @@
Resource::CreateResourceObject(&m_allocated, tSolenoid::kNumDO7_0Elements * kSolenoidChannels);
snprintf(buf, 64, "Solenoid %d (Module %d)", m_forwardChannel, m_moduleNumber);
- if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels + m_forwardChannel - 1, buf) == ~0ul)
+ if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels +
+ m_forwardChannel - 1, buf, this) == ~0ul)
{
- CloneError(m_allocated);
return;
}
snprintf(buf, 64, "Solenoid %d (Module %d)", m_reverseChannel, m_moduleNumber);
- if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels + m_reverseChannel - 1, buf) == ~0ul)
+ if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels +
+ m_reverseChannel - 1, buf, this) == ~0ul)
{
- CloneError(m_allocated);
return;
}
m_forwardMask = 1 << (m_forwardChannel - 1);
@@ -90,11 +90,10 @@
*/
DoubleSolenoid::~DoubleSolenoid()
{
- if (CheckSolenoidModule(m_moduleNumber))
- {
- m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_forwardChannel - 1);
- m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_reverseChannel - 1);
- }
+ m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels +
+ m_forwardChannel - 1, this);
+ m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels +
+ m_reverseChannel - 1, this);
}
/**
diff --git a/aos/externals/WPILib/WPILib/DoubleSolenoid.h b/aos/externals/WPILib/WPILib/DoubleSolenoid.h
index f9920a1..a4cd03a 100644
--- a/aos/externals/WPILib/WPILib/DoubleSolenoid.h
+++ b/aos/externals/WPILib/WPILib/DoubleSolenoid.h
@@ -38,7 +38,7 @@
ITable * GetTable();
private:
- virtual void InitSolenoid();
+ void InitSolenoid();
UINT32 m_forwardChannel; ///< The forward channel on the module to control.
UINT32 m_reverseChannel; ///< The reverse channel on the module to control.
diff --git a/aos/externals/WPILib/WPILib/DriverStation.cpp b/aos/externals/WPILib/WPILib/DriverStation.cpp
index ffe0e66..9c2cb91 100644
--- a/aos/externals/WPILib/WPILib/DriverStation.cpp
+++ b/aos/externals/WPILib/WPILib/DriverStation.cpp
@@ -19,8 +19,8 @@
const UINT32 DriverStation::kBatteryChannel;
const UINT32 DriverStation::kJoystickPorts;
const UINT32 DriverStation::kJoystickAxes;
-const float DriverStation::kUpdatePeriod;
DriverStation* DriverStation::m_instance = NULL;
+ReentrantSemaphore DriverStation::m_instanceSemaphore;
UINT8 DriverStation::m_updateNumber = 0;
/**
@@ -58,32 +58,7 @@
m_waitForDataSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
- m_controlData = new FRCCommonControlData;
-
- // initialize packet number and control words to zero;
- m_controlData->packetIndex = 0;
- m_controlData->control = 0;
-
- // set all joystick axis values to neutral; buttons to OFF
- m_controlData->stick0Axis1 = m_controlData->stick0Axis2 = m_controlData->stick0Axis3 = 0;
- m_controlData->stick1Axis1 = m_controlData->stick1Axis2 = m_controlData->stick1Axis3 = 0;
- m_controlData->stick2Axis1 = m_controlData->stick2Axis2 = m_controlData->stick2Axis3 = 0;
- m_controlData->stick3Axis1 = m_controlData->stick3Axis2 = m_controlData->stick3Axis3 = 0;
- m_controlData->stick0Axis4 = m_controlData->stick0Axis5 = m_controlData->stick0Axis6 = 0;
- m_controlData->stick1Axis4 = m_controlData->stick1Axis5 = m_controlData->stick1Axis6 = 0;
- m_controlData->stick2Axis4 = m_controlData->stick2Axis5 = m_controlData->stick2Axis6 = 0;
- m_controlData->stick3Axis4 = m_controlData->stick3Axis5 = m_controlData->stick3Axis6 = 0;
- m_controlData->stick0Buttons = 0;
- m_controlData->stick1Buttons = 0;
- m_controlData->stick2Buttons = 0;
- m_controlData->stick3Buttons = 0;
-
- // initialize the analog and digital data.
- m_controlData->analog1 = 0;
- m_controlData->analog2 = 0;
- m_controlData->analog3 = 0;
- m_controlData->analog4 = 0;
- m_controlData->dsDigitalIn = 0;
+ m_controlData = new FRCCommonControlData();
m_batteryChannel = new AnalogChannel(kBatteryModuleNumber, kBatteryChannel);
@@ -97,6 +72,7 @@
DriverStation::~DriverStation()
{
+ Synchronized sync(m_instanceSemaphore);
m_task.Stop();
semDelete(m_statusDataSemaphore);
delete m_batteryChannel;
@@ -106,6 +82,15 @@
// Unregister our semaphore.
setNewDataSem(0);
semDelete(m_packetDataAvailableSem);
+ semDelete(m_newControlData);
+}
+
+/**
+ * Gets a read lock on all of the data. Be careful with this; holding one of
+ * these locks prevents any new data from being read.
+ */
+RWLock::Locker DriverStation::GetDataReadLock() {
+ return RWLock::Locker(&m_dataLock, false);
}
void DriverStation::InitTask(DriverStation *ds)
@@ -113,6 +98,9 @@
ds->Run();
}
+/**
+ * Gets called in a separate task to deal with actually reading any new data.
+ */
void DriverStation::Run()
{
int period = 0;
@@ -128,14 +116,17 @@
MotorSafetyHelper::CheckMotors();
period = 0;
}
- if (m_userInDisabled)
- FRC_NetworkCommunication_observeUserProgramDisabled();
- if (m_userInAutonomous)
- FRC_NetworkCommunication_observeUserProgramAutonomous();
- if (m_userInTeleop)
- FRC_NetworkCommunication_observeUserProgramTeleop();
- if (m_userInTest)
- FRC_NetworkCommunication_observeUserProgramTest();
+ {
+ RWLock::Locker userStateLocker(&m_userStateLock, false);
+ if (m_userInDisabled)
+ FRC_NetworkCommunication_observeUserProgramDisabled();
+ if (m_userInAutonomous)
+ FRC_NetworkCommunication_observeUserProgramAutonomous();
+ if (m_userInTeleop)
+ FRC_NetworkCommunication_observeUserProgramTeleop();
+ if (m_userInTest)
+ FRC_NetworkCommunication_observeUserProgramTest();
+ }
}
}
@@ -144,6 +135,7 @@
*/
DriverStation* DriverStation::GetInstance()
{
+ Synchronized sync(m_instanceSemaphore);
if (m_instance == NULL)
{
m_instance = new DriverStation();
@@ -152,14 +144,18 @@
}
/**
- * Copy data from the DS task for the user.
- * If no new data exists, it will just be returned, otherwise
- * the data will be copied from the DS polling loop.
+ * Copy data from the DS task for the rest of the code to use.
*/
void DriverStation::GetData()
{
static bool lastEnabled = false;
- getCommonControlData(m_controlData, WAIT_FOREVER);
+ {
+ // Only have to lock right around reading the data because we're the only
+ // task that ever modifies it.
+ RWLock::Locker write_lock(&m_dataLock, true);
+ // Have to const_cast away the volatile and the const.
+ getCommonControlData(const_cast<FRCCommonControlData *>(m_controlData), WAIT_FOREVER);
+ }
if (!lastEnabled && IsEnabled())
{
// If starting teleop, assume that autonomous just took up 15 seconds
@@ -177,7 +173,7 @@
}
/**
- * Copy status data from the DS task for the user.
+ * Copy status data to the DS task from the user.
*/
void DriverStation::SetData()
{
@@ -308,6 +304,7 @@
if (channel < 1 || channel > 4)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 4");
+ // TODO: Fix the lack of thread safety here (for reported_mask).
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
@@ -340,6 +337,7 @@
if (channel < 1 || channel > 8)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
+ // TODO: Fix the lack of thread safety here (for reported_mask).
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
@@ -364,6 +362,8 @@
if (channel < 1 || channel > 8)
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
+ // TODO: Fix the lack of thread safety here (for both reported_mask and
+ // m_digitalOut).
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
@@ -388,35 +388,76 @@
return ((m_digitalOut >> (channel-1)) & 0x1) ? true : false;;
}
+/**
+ * @return Whether or not the robot is currently enabled by the field controls.
+ */
bool DriverStation::IsEnabled()
{
return m_controlData->enabled;
}
+/**
+ * @return Whether or not the robot is currently disabled by the field controls.
+ */
bool DriverStation::IsDisabled()
{
return !m_controlData->enabled;
}
+/**
+ * Determines if the robot is currently in autonomous mode. Does not check
+ * whether the robot is enabled.
+ * @return Whether or not the robot is currently in autonomous mode.
+ */
bool DriverStation::IsAutonomous()
{
return m_controlData->autonomous;
}
+/**
+ * Determines if the robot is currently in teleoperated mode. Does not check
+ * whether the robot is enabled.
+ * @return Whether or not the robot is currently in teleoperated mode.
+ */
bool DriverStation::IsOperatorControl()
{
+ RWLock::Locker data_locker(GetDataReadLock());
return !(m_controlData->autonomous || m_controlData->test);
}
+/**
+ * Determines if the robot is currently in test mode. Does not check
+ * whether the robot is enabled.
+ * @return Whether or not the robot is currently in test mode.
+ */
bool DriverStation::IsTest()
{
return m_controlData->test;
}
/**
+ * @return What state the robot is currently in.
+ */
+DriverStation::FMSState DriverStation::GetCurrentState() {
+ RWLock::Locker data_locker(GetDataReadLock());
+ if (IsDisabled()) {
+ return FMSState::kDisabled;
+ // Or else it must be enabled (for all of the other ones).
+ } else if (IsAutonomous()) {
+ return FMSState::kAutonomous;
+ } else if (IsTest()) {
+ return FMSState::kTestMode;
+ } else { // IsOperatorControl() has to return true now
+ return FMSState::kTeleop;
+ }
+}
+
+/**
* Has a new control packet from the driver station arrived since the last time this function was called?
* Warning: If you call this function from more than one place at the same time,
- * you will not get the get the intended behavior
+ * you will not get the get the intended behavior unless that behavior is
+ * exactly 1 of the places that you call it from getting a true after a packet
+ * arrives.
* @return True if the control data has been updated since the last call.
*/
bool DriverStation::IsNewControlData()
@@ -437,7 +478,6 @@
/**
* Return the DS packet number.
* The packet number is the index of this set of data returned by the driver station.
- * Each time new data is received, the packet number (included with the sent data) is returned.
* @return The driver station packet number
*/
UINT32 DriverStation::GetPacketNumber()
@@ -447,13 +487,14 @@
/**
* Return the alliance that the driver station says it is on.
- * This could return kRed or kBlue
* @return The Alliance enum
*/
DriverStation::Alliance DriverStation::GetAlliance()
{
- if (m_controlData->dsID_Alliance == 'R') return kRed;
- if (m_controlData->dsID_Alliance == 'B') return kBlue;
+ // Read it first to make sure that it doesn't change in between the checks.
+ char alliance = m_controlData->dsID_Alliance;
+ if (alliance == 'R') return kRed;
+ if (alliance == 'B') return kBlue;
wpi_assert(false);
return kInvalid;
}
@@ -465,8 +506,9 @@
*/
UINT32 DriverStation::GetLocation()
{
- wpi_assert ((m_controlData->dsID_Position >= '1') && (m_controlData->dsID_Position <= '3'));
- return m_controlData->dsID_Position - '0';
+ char position = m_controlData->dsID_Position;
+ wpi_assert ((position >= '1') && (position <= '3'));
+ return position - '0';
}
/**
diff --git a/aos/externals/WPILib/WPILib/DriverStation.h b/aos/externals/WPILib/WPILib/DriverStation.h
index acd9b16..1fcf1e8 100644
--- a/aos/externals/WPILib/WPILib/DriverStation.h
+++ b/aos/externals/WPILib/WPILib/DriverStation.h
@@ -11,6 +11,9 @@
#include "DriverStationEnhancedIO.h"
#include "SensorBase.h"
#include "Task.h"
+#include "Synchronized.h"
+#include "RWLock.h"
+#include "Base.h"
struct FRCCommonControlData;
class AnalogChannel;
@@ -23,14 +26,35 @@
public:
enum Alliance {kRed, kBlue, kInvalid};
- virtual ~DriverStation();
+ // Represents all of the states that FMS thinks of a robot as being in.
+ // NOTE: All of the ones except kDisabled mean that the robot is enabled too.
+ enum FMSState {
+ kDisabled,
+ kAutonomous,
+ kTeleop,
+ kTestMode,
+ };
+
static DriverStation *GetInstance();
+ RWLock::Locker GetDataReadLock();
+
static const UINT32 kBatteryModuleNumber = 1;
static const UINT32 kBatteryChannel = 8;
static const UINT32 kJoystickPorts = 4;
static const UINT32 kJoystickAxes = 6;
+ /**
+ * Returns the pointer to all of the data. This pointer will never change, but
+ * its contents will, so make sure to GetDataReadLock() if you want to make
+ * sure that it doesn't change while you're using it.
+ *
+ * You may NOT modify the contents!
+ */
+ const volatile struct FRCCommonControlData *GetControlData() {
+ return m_controlData;
+ }
+
float GetStickAxis(UINT32 stick, UINT32 axis);
short GetStickButtons(UINT32 stick);
@@ -41,9 +65,10 @@
bool IsEnabled();
bool IsDisabled();
- bool IsAutonomous();
+ bool IsAutonomous();
bool IsOperatorControl();
- bool IsTest();
+ bool IsTest();
+ FMSState GetCurrentState();
bool IsNewControlData();
bool IsFMSAttached();
@@ -93,23 +118,47 @@
* for diagnostic purposes only
* @param entering If true, starting test code; if false, leaving test code */
void InTest(bool entering) {m_userInTest=entering;}
+ /**
+ * Get a pointer to the lock used for the data set by the In* methods.
+ * Creating write locks on this is useful if you want to atomically modify the
+ * information about what code you claim to be executing.
+ * @return A pointer to the lock. Be aware that the code that looks at this
+ * state (using a read lock) runs after the code that reads new packets and
+ * must finish before a new one can be read.
+ * @see #InDisabled(bool)
+ * @see #InAutonomous(bool)
+ * @see #InOperatorControl(bool)
+ * @see #InTest(bool)
+ */
+ RWLock *GetUserStateLock() { return &m_userStateLock; }
protected:
DriverStation();
+ virtual ~DriverStation();
void GetData();
void SetData();
private:
- static void InitTask(DriverStation *ds);
static DriverStation *m_instance;
+ static ReentrantSemaphore m_instanceSemaphore;
static UINT8 m_updateNumber;
- ///< TODO: Get rid of this and use the semaphore signaling
- static const float kUpdatePeriod = 0.02;
+ static void InitTask(DriverStation *ds);
void Run();
- struct FRCCommonControlData *m_controlData;
+ // Volatile because it gets modified by GetData() in a separate task. Be
+ // careful using values out of here (2-byte and 4-byte accesses are safe as
+ // long as they're aligned, which all of the ones in here should be). If you
+ // need consistent data, use m_dataLock.
+ // Const because it should never be modifed except by getCommonControlData,
+ // and that call has to const_cast away the volatile anyways.
+ const volatile struct FRCCommonControlData *m_controlData;
+ // A lock for *m_controlData.
+ // Read (not write) RWLock::Lockers for this get given out to users so that
+ // they can prevent updates to the data while they are doing stuff with it.
+ RWLock m_dataLock;
+
UINT8 m_digitalOut;
AnalogChannel *m_batteryChannel;
SEM_ID m_statusDataSemaphore;
@@ -118,15 +167,24 @@
Dashboard m_dashboardLow;
DashboardBase* m_dashboardInUseHigh; // the current dashboard packers in use
DashboardBase* m_dashboardInUseLow;
+ // Used to indicate when there is new control data available for
+ // IsNewControlData(). A semaphore instead of just a bool to avoid race
+ // conditions resulting in missed packets.
SEM_ID m_newControlData;
SEM_ID m_packetDataAvailableSem;
DriverStationEnhancedIO m_enhancedIO;
+ // Always empty. Gets semFlushed when there is new data available so that
+ // multiple tasks waiting for it can be woken at the same time.
SEM_ID m_waitForDataSem;
double m_approxMatchTimeOffset;
+
+ RWLock m_userStateLock;
bool m_userInDisabled;
bool m_userInAutonomous;
bool m_userInTeleop;
bool m_userInTest;
+
+ DISALLOW_COPY_AND_ASSIGN(DriverStation);
};
#endif
diff --git a/aos/externals/WPILib/WPILib/DriverStationLCD.h b/aos/externals/WPILib/WPILib/DriverStationLCD.h
index 3ea90a5..1a3731f 100644
--- a/aos/externals/WPILib/WPILib/DriverStationLCD.h
+++ b/aos/externals/WPILib/WPILib/DriverStationLCD.h
@@ -25,7 +25,6 @@
static const INT32 kNumLines = 6;
enum Line {kMain_Line6=0, kUser_Line1=0, kUser_Line2=1, kUser_Line3=2, kUser_Line4=3, kUser_Line5=4, kUser_Line6=5};
- virtual ~DriverStationLCD();
static DriverStationLCD *GetInstance();
void UpdateLCD();
@@ -38,6 +37,7 @@
protected:
DriverStationLCD();
+ virtual ~DriverStationLCD();
private:
static void InitTask(DriverStationLCD *ds);
diff --git a/aos/externals/WPILib/WPILib/Encoder.cpp b/aos/externals/WPILib/WPILib/Encoder.cpp
index cdf1dba..cfb96c9 100644
--- a/aos/externals/WPILib/WPILib/Encoder.cpp
+++ b/aos/externals/WPILib/WPILib/Encoder.cpp
@@ -27,14 +27,11 @@
{
m_encodingType = encodingType;
tRioStatusCode localStatus = NiFpga_Status_Success;
- switch (encodingType)
- {
- case k4X:
+ if (encodingType == k4X) {
Resource::CreateResourceObject(&quadEncoders, tEncoder::kNumSystems);
- UINT32 index = quadEncoders->Allocate("4X Encoder");
+ UINT32 index = quadEncoders->Allocate("4X Encoder", this);
if (index == ~0ul)
{
- CloneError(quadEncoders);
return;
}
if (m_aSource->StatusIsFatal())
@@ -59,12 +56,9 @@
m_encoder->writeConfig_Reverse(reverseDirection, &localStatus);
m_encoder->writeTimerConfig_AverageSize(4, &localStatus);
m_counter = NULL;
- break;
- case k1X:
- case k2X:
+ } else {
m_counter = new Counter(m_encodingType, m_aSource, m_bSource, reverseDirection);
m_index = m_counter->GetIndex();
- break;
}
m_distancePerPulse = 1.0;
m_pidSource = kDistance;
@@ -195,7 +189,7 @@
}
else
{
- quadEncoders->Free(m_index);
+ quadEncoders->Free(m_index, this);
delete m_encoder;
}
}
diff --git a/aos/externals/WPILib/WPILib/Error.cpp b/aos/externals/WPILib/WPILib/Error.cpp
index 30f0cbc..9e83de7 100644
--- a/aos/externals/WPILib/WPILib/Error.cpp
+++ b/aos/externals/WPILib/WPILib/Error.cpp
@@ -25,8 +25,32 @@
Error::~Error()
{}
-void Error::Clone(Error &error)
+/**
+ * Clones another error into this if this is currently clear. If not, does
+ * nothing.
+ * This is necessary because just using "if (!IsClear()) Clone(error)" has a
+ * race condition which this method does not.
+ * Cloning 2 errors into each other at the same time can lead to deadlocks!
+ */
+void Error::CloneIfClear(const Error &error) {
+ Synchronized sync(m_semaphore);
+ if (IsClear()) {
+ DoClone(error);
+ }
+}
+
+/**
+ * Clones another error into this object.
+ * Cloning 2 errors into each other at the same time can lead to deadlocks!
+ */
+void Error::Clone(const Error &error) {
+ Synchronized sync(m_semaphore);
+ DoClone(error);
+}
+
+void Error::DoClone(const Error &error)
{
+ Synchronized sync(error.m_semaphore);
m_code = error.m_code;
m_message = error.m_message;
m_filename = error.m_filename;
@@ -36,17 +60,19 @@
m_timestamp = error.m_timestamp;
}
+bool Error::IsClear() const { return GetCode() == 0; }
+
Error::Code Error::GetCode() const
{ return m_code; }
-const char * Error::GetMessage() const
-{ return m_message.c_str(); }
+std::string Error::GetMessage() const
+{ return m_message; }
-const char * Error::GetFilename() const
-{ return m_filename.c_str(); }
+std::string Error::GetFilename() const
+{ return m_filename; }
-const char * Error::GetFunction() const
-{ return m_function.c_str(); }
+std::string Error::GetFunction() const
+{ return m_function; }
UINT32 Error::GetLineNumber() const
{ return m_lineNumber; }
@@ -57,8 +83,10 @@
double Error::GetTime() const
{ return m_timestamp; }
-void Error::Set(Code code, const char* contextMessage, const char* filename, const char* function, UINT32 lineNumber, const ErrorBase* originatingObject)
-{
+void Error::Set(Code code, const char* contextMessage, const char* filename,
+ const char* function, UINT32 lineNumber,
+ const ErrorBase* originatingObject) {
+ Synchronized sync(m_semaphore);
m_code = code;
m_message = contextMessage;
m_filename = filename;
@@ -69,39 +97,40 @@
Report();
- if (m_suspendOnErrorEnabled) taskSuspend(0);
+ if (m_suspendOnErrorEnabled) taskSuspend(0 /*self*/);
}
-void Error::Report()
+void Error::Report() const
{
// Error string buffers
- char *error = new char[256];
- char *error_with_code = new char[256];
+ char error[256];
+ char error_with_code[256];
// Build error strings
- if (m_code != -1)
+ if (m_code != -1 && m_code != 1)
{
- snprintf(error, 256, "%s: status = %d (0x%08X) %s ...in %s() in %s at line %d\n",
- m_code < 0 ? "ERROR" : "WARNING", (INT32)m_code, (UINT32)m_code, m_message.c_str(),
- m_function.c_str(), m_filename.c_str(), m_lineNumber);
- sprintf(error_with_code,"<Code>%d %s", (INT32)m_code, error);
+ snprintf(error, sizeof(error),
+ "%s: status = %d (0x%08X) %s ...in %s() in %s at line %d\n",
+ m_code < 0 ? "ERROR" : "WARNING", (INT32)m_code,
+ (UINT32)m_code, m_message.c_str(),
+ m_function.c_str(), m_filename.c_str(), m_lineNumber);
+ snprintf(error_with_code, sizeof(error_with_code),
+ "<Code>%d %s", (INT32)m_code, error);
} else {
- snprintf(error, 256, "ERROR: %s ...in %s() in %s at line %d\n", m_message.c_str(),
- m_function.c_str(), m_filename.c_str(), m_lineNumber);
- strcpy(error_with_code, error);
+ snprintf(error, sizeof(error),
+ "%s: %s ...in %s() in %s at line %d\n",
+ m_code < 0 ? "ERROR" : "WARNING", m_message.c_str(),
+ m_function.c_str(), m_filename.c_str(), m_lineNumber);
+ strncpy(error_with_code, error, sizeof(error_with_code));
}
// TODO: Add logging to disk
// Send to the DriverStation
setErrorData(error_with_code, strlen(error_with_code), 100);
- delete [] error_with_code;
-
// Print to console
printf("\n\n>>>>%s", error);
- delete [] error;
-
if (m_stackTraceEnabled)
{
printf("-----------<Stack Trace>----------------\n");
@@ -111,6 +140,7 @@
void Error::Clear()
{
+ Synchronized sync(m_semaphore);
m_code = 0;
m_message = "";
m_filename = "";
diff --git a/aos/externals/WPILib/WPILib/Error.h b/aos/externals/WPILib/WPILib/Error.h
index a80fe06..9636262 100644
--- a/aos/externals/WPILib/WPILib/Error.h
+++ b/aos/externals/WPILib/WPILib/Error.h
@@ -10,37 +10,55 @@
#include "Base.h"
#include "ChipObject/NiRio.h"
#include <string>
-#include <vxWorks.h>
+#include "Synchronized.h"
// Forward declarations
class ErrorBase;
/**
- * Error object represents a library error.
+ * Represents an error or warning.
+ *
+ * All methods that can change instance data are protected by a lock so
+ * that it is safe to call any methods from multiple tasks at the same time.
*/
class Error
{
public:
+ // -1 is other error, 1 is other warning.
typedef tRioStatusCode Code;
Error();
~Error();
- void Clone(Error &error);
- Code GetCode() const;
- const char *GetMessage() const;
- const char *GetFilename() const;
- const char *GetFunction() const;
- UINT32 GetLineNumber() const;
- const ErrorBase* GetOriginatingObject() const;
- double GetTime() const;
+
+ void CloneIfClear(const Error &error);
+ void Clone(const Error &error);
void Clear();
void Set(Code code, const char* contextMessage, const char* filename,
const char *function, UINT32 lineNumber, const ErrorBase* originatingObject);
+
+ bool IsClear() const;
+ Code GetCode() const;
+ // Have to return by value to avoid race conditions using the result for all
+ // of these methods.
+ std::string GetMessage() const;
+ std::string GetFilename() const;
+ std::string GetFunction() const;
+ UINT32 GetLineNumber() const;
+ const ErrorBase* GetOriginatingObject() const;
+ double GetTime() const;
+
+ // Enable or disable printing out a stack trace on the console whenever there
+ // is an error.
static void EnableStackTrace(bool enable) { m_stackTraceEnabled=enable; }
+ // Enable or disable having any task that gets an error suspend itself.
static void EnableSuspendOnError(bool enable) { m_suspendOnErrorEnabled=enable; }
private:
- void Report();
+ // Deals with notifying other code of this error.
+ void Report() const;
+ // Actually implements cloning.
+ // Does not lock m_semaphore, so callers must.
+ void DoClone(const Error &error);
Code m_code;
std::string m_message;
@@ -49,6 +67,10 @@
UINT32 m_lineNumber;
const ErrorBase* m_originatingObject;
double m_timestamp;
+ // Used for protecting all modifications to instance data.
+ // This means that all non-const methods should lock this for (at least most)
+ // of their implementations!
+ ReentrantSemaphore m_semaphore;
static bool m_stackTraceEnabled;
static bool m_suspendOnErrorEnabled;
diff --git a/aos/externals/WPILib/WPILib/ErrorBase.cpp b/aos/externals/WPILib/WPILib/ErrorBase.cpp
index 2722963..5f766c7 100644
--- a/aos/externals/WPILib/WPILib/ErrorBase.cpp
+++ b/aos/externals/WPILib/WPILib/ErrorBase.cpp
@@ -14,8 +14,8 @@
#include <symLib.h>
#include <sysSymTbl.h>
-SEM_ID ErrorBase::_globalErrorMutex = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
Error ErrorBase::_globalError;
+
/**
* @brief Initialize the instance status to 0 for now.
*/
@@ -26,15 +26,10 @@
{}
/**
- * @brief Retrieve the current error.
- * Get the current error information associated with this sensor.
+ * @brief Retrieve the error associated this object.
+ * Get the error information associated with this sensor.
*/
-Error& ErrorBase::GetError()
-{
- return m_error;
-}
-
-const Error& ErrorBase::GetError() const
+Error& ErrorBase::GetError() const
{
return m_error;
}
@@ -48,7 +43,10 @@
}
/**
- * @brief Set error information associated with a C library call that set an error to the "errno" global variable.
+ * @brief Set error information associated with a C library call that set an
+ * error to the "errno" "global variable" (it's really a macro that calls a
+ * function under VxWorks so that it's thread safe).
+ * Will still set an error even if errno is 0.
*
* @param contextMessage A custom message from the code that set the error.
* @param filename Filename of the error source
@@ -62,7 +60,7 @@
int errNo = errnoGet();
if (errNo == 0)
{
- sprintf(err, "OK: %s", contextMessage);
+ snprintf(err, sizeof(err), "OK: %s", contextMessage);
}
else
{
@@ -71,24 +69,21 @@
SYM_TYPE ptype;
symFindByValue(statSymTbl, errNo, statName, &pval, &ptype);
if (pval != errNo)
- snprintf(err, 256, "Unknown errno 0x%08X: %s", errNo, contextMessage);
+ snprintf(err, sizeof(err), "Unknown errno 0x%08X: %s", errNo, contextMessage);
else
- snprintf(err, 256, "%s (0x%08X): %s", statName, errNo, contextMessage);
+ snprintf(err, sizeof(err), "%s (0x%08X): %s", statName, errNo, contextMessage);
delete [] statName;
}
// Set the current error information for this object.
m_error.Set(-1, err, filename, function, lineNumber, this);
- // Update the global error if there is not one already set.
- Synchronized mutex(_globalErrorMutex);
- if (_globalError.GetCode() == 0) {
- _globalError.Clone(m_error);
- }
+ _globalError.CloneIfClear(m_error);
}
/**
* @brief Set the current error information associated from the nivision Imaq API.
+ * Does nothing of success is > 0.
*
* @param success The return from the function
* @param contextMessage A custom message from the code that set the error.
@@ -101,21 +96,18 @@
// If there was an error
if (success <= 0) {
char err[256];
- sprintf(err, "%s: %s", contextMessage, imaqGetErrorText(imaqGetLastError()));
+ snprintf(err, sizeof(err), "%s: %s", contextMessage, imaqGetErrorText(imaqGetLastError()));
// Set the current error information for this object.
m_error.Set(imaqGetLastError(), err, filename, function, lineNumber, this);
- // Update the global error if there is not one already set.
- Synchronized mutex(_globalErrorMutex);
- if (_globalError.GetCode() == 0) {
- _globalError.Clone(m_error);
- }
+ _globalError.CloneIfClear(m_error);
}
}
/**
- * @brief Set the current error information associated with this sensor.
+ * @brief Set the current error information associated with this object.
+ * Does nothing if code is 0.
*
* @param code The error code
* @param contextMessage A custom message from the code that set the error.
@@ -131,16 +123,12 @@
// Set the current error information for this object.
m_error.Set(code, contextMessage, filename, function, lineNumber, this);
- // Update the global error if there is not one already set.
- Synchronized mutex(_globalErrorMutex);
- if (_globalError.GetCode() == 0) {
- _globalError.Clone(m_error);
- }
+ _globalError.CloneIfClear(m_error);
}
}
/**
- * @brief Set the current error information associated with this sensor.
+ * @brief Set the current error information associated with this object.
*
* @param errorMessage The error message from WPIErrors.h
* @param contextMessage A custom message from the code that set the error.
@@ -152,19 +140,15 @@
const char* filename, const char* function, UINT32 lineNumber) const
{
char err[256];
- sprintf(err, "%s: %s", errorMessage, contextMessage);
+ snprintf(err, sizeof(err), "%s: %s", errorMessage, contextMessage);
// Set the current error information for this object.
m_error.Set(-1, err, filename, function, lineNumber, this);
- // Update the global error if there is not one already set.
- Synchronized mutex(_globalErrorMutex);
- if (_globalError.GetCode() == 0) {
- _globalError.Clone(m_error);
- }
+ _globalError.CloneIfClear(m_error);
}
-void ErrorBase::CloneError(ErrorBase *rhs) const
+void ErrorBase::CloneError(const ErrorBase *rhs) const
{
m_error.Clone(rhs->GetError());
}
@@ -179,37 +163,49 @@
return m_error.GetCode() < 0;
}
+/**
+ * @brief Set the current global error information.
+ * Does nothing if code is 0.
+ * TODO: think about getting rid of this because nothing uses it any more
+ *
+ * @param code The error code
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
void ErrorBase::SetGlobalError(Error::Code code, const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber)
{
- // If there was an error
- if (code != 0) {
- Synchronized mutex(_globalErrorMutex);
-
- // Set the current error information for this object.
- _globalError.Set(code, contextMessage, filename, function, lineNumber, NULL);
- }
+ if (code != 0) {
+ // Set the current error information for this object.
+ _globalError.Set(code, contextMessage, filename, function, lineNumber, NULL);
+ }
}
+/**
+ * @brief Set the current global error information.
+ *
+ * @param errorMessage The error message from WPIErrors.h
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
void ErrorBase::SetGlobalWPIError(const char *errorMessage, const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber)
{
char err[256];
- sprintf(err, "%s: %s", errorMessage, contextMessage);
+ snprintf(err, sizeof(err), "%s: %s", errorMessage, contextMessage);
- Synchronized mutex(_globalErrorMutex);
- if (_globalError.GetCode() != 0) {
- _globalError.Clear();
- }
_globalError.Set(-1, err, filename, function, lineNumber, NULL);
}
/**
- * Retrieve the current global error.
+ * Retrieve the global error.
*/
-Error& ErrorBase::GetGlobalError()
+const Error& ErrorBase::GetGlobalError()
{
- Synchronized mutex(_globalErrorMutex);
return _globalError;
}
diff --git a/aos/externals/WPILib/WPILib/ErrorBase.h b/aos/externals/WPILib/WPILib/ErrorBase.h
index f967562..9d032cb 100644
--- a/aos/externals/WPILib/WPILib/ErrorBase.h
+++ b/aos/externals/WPILib/WPILib/ErrorBase.h
@@ -10,59 +10,86 @@
#include "Base.h"
#include "ChipObject/NiRio.h"
#include "Error.h"
-#include <semLib.h>
-#include <vxWorks.h>
-#define wpi_setErrnoErrorWithContext(context) (this->SetErrnoError((context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setErrnoError() (wpi_setErrnoErrorWithContext(""))
-#define wpi_setImaqErrorWithContext(code, context) (this->SetImaqError((code), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setErrorWithContext(code, context) (this->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+// Helper macros to fill in the context information for you. See the
+// documentation for the methods that they call for details.
+#define wpi_setErrnoErrorWithContext(context) \
+ (this->SetErrnoError((context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setErrnoError() \
+ (wpi_setErrnoErrorWithContext(""))
+#define wpi_setImaqErrorWithContext(code, context) \
+ (this->SetImaqError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setErrorWithContext(code, context) \
+ (this->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
#define wpi_setError(code) (wpi_setErrorWithContext(code, ""))
-#define wpi_setStaticErrorWithContext(object, code, context) (object->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setStaticError(object, code) (wpi_setStaticErrorWithContext(object, code, ""))
-#define wpi_setGlobalErrorWithContext(code, context) (ErrorBase::SetGlobalError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticErrorWithContext(object, code, context) \
+ (object->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticError(object, code) \
+ (wpi_setStaticErrorWithContext(object, code, ""))
+#define wpi_setGlobalErrorWithContext(code, context) \
+ (ErrorBase::SetGlobalError((code), (context), \
+ __FILE__, __FUNCTION__, __LINE__))
#define wpi_setGlobalError(code) (wpi_setGlobalErrorWithContext(code, ""))
-#define wpi_setWPIErrorWithContext(error, context) (this->SetWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setWPIErrorWithContext(error, context) \
+ (this->SetWPIError((wpi_error_s_##error), (context), \
+ __FILE__, __FUNCTION__, __LINE__))
#define wpi_setWPIError(error) (wpi_setWPIErrorWithContext(error, ""))
-#define wpi_setStaticWPIErrorWithContext(object, error, context) (object->SetWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setStaticWPIError(object, error) (wpi_setStaticWPIErrorWithContext(object, error, ""))
-#define wpi_setGlobalWPIErrorWithContext(error, context) (ErrorBase::SetGlobalWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setGlobalWPIError(error) (wpi_setGlobalWPIErrorWithContext(error, ""))
+#define wpi_setStaticWPIErrorWithContext(object, error, context) \
+ (object->SetWPIError((wpi_error_s_##error), (context), \
+ __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticWPIError(object, error) \
+ (wpi_setStaticWPIErrorWithContext(object, error, ""))
+#define wpi_setGlobalWPIErrorWithContext(error, context) \
+ (ErrorBase::SetGlobalWPIError((wpi_error_s_##error), (context), \
+ __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setGlobalWPIError(error) \
+ (wpi_setGlobalWPIErrorWithContext(error, ""))
/**
* Base class for most objects.
* ErrorBase is the base class for most objects since it holds the generated error
* for that object. In addition, there is a single instance of a global error object
+ *
+ * BE AWARE: This does include a mutable instance variable! This means that even
+ * if you make an object const it's not really. However, all modification to
+ * that instance variable is protected by a semaphore, so it does not create any
+ * thread safety issues.
+ *
+ * All of the Set*Error methods will update the global error if there is nothing
+ * there already.
*/
class ErrorBase
{
-//TODO: Consider initializing instance variables and cleanup in destructor
public:
+ ErrorBase();
virtual ~ErrorBase();
- virtual Error& GetError();
- virtual const Error& GetError() const;
- virtual void SetErrnoError(const char *contextMessage,
+
+ Error& GetError() const;
+ void SetErrnoError(const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber) const;
- virtual void SetImaqError(int success, const char *contextMessage,
+ void SetImaqError(int success, const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber) const;
- virtual void SetError(Error::Code code, const char *contextMessage,
+ void SetError(Error::Code code, const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber) const;
- virtual void SetWPIError(const char *errorMessage, const char *contextMessage,
+ void SetWPIError(const char *errorMessage, const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber) const;
- virtual void CloneError(ErrorBase *rhs) const;
- virtual void ClearError() const;
- virtual bool StatusIsFatal() const;
+ void CloneError(const ErrorBase *rhs) const;
+ void ClearError() const;
+ bool StatusIsFatal() const;
static void SetGlobalError(Error::Code code, const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber);
static void SetGlobalWPIError(const char *errorMessage, const char *contextMessage,
const char* filename, const char* function, UINT32 lineNumber);
- static Error& GetGlobalError();
+ static const Error& GetGlobalError();
+
protected:
+ // This mutable is safe because Error guarantees that all modifications are
+ // protected with an internal lock.
mutable Error m_error;
- // TODO: Replace globalError with a global list of all errors.
- static SEM_ID _globalErrorMutex;
+ // TODO: Replace globalError with a global list of all errors, but make sure
+ // that it's thread safe.
static Error _globalError;
- ErrorBase();
+
private:
DISALLOW_COPY_AND_ASSIGN(ErrorBase);
};
diff --git a/aos/externals/WPILib/WPILib/Global.cpp b/aos/externals/WPILib/WPILib/Global.cpp
new file mode 100644
index 0000000..0d48129
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/Global.cpp
@@ -0,0 +1,155 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "Global.h"
+#include "Utility.h"
+
+Global *Global::instance;
+ReentrantSemaphore Global::instance_lock;
+
+Global *Global::GetInstance() {
+ Synchronized sync(instance_lock);
+ if (instance == NULL) {
+ instance = new Global();
+ }
+ return instance;
+}
+
+Global::Global() {
+ tRioStatusCode status = NiFpga_Status_Success;
+ global_.reset(tGlobal::create(&status));
+ wpi_setError(status);
+
+ AddToSingletonList();
+}
+
+Global::~Global() {
+ Synchronized sync(instance_lock);
+ instance = NULL;
+}
+
+/**
+ * Return the FPGA Version number.
+ * For now, expect this to be competition year.
+ * @return FPGA Version number.
+ */
+UINT16 Global::GetFPGAVersion()
+{
+ tRioStatusCode status = NiFpga_Status_Success;
+ UINT16 version = global_->readVersion(&status);
+ wpi_setError(status);
+ return version;
+}
+
+/**
+ * Return the FPGA Revision number.
+ * The format of the revision is 3 numbers.
+ * The 12 most significant bits are the Major Revision.
+ * the next 8 bits are the Minor Revision.
+ * The 12 least significant bits are the Build Number.
+ * @return FPGA Revision number.
+ */
+UINT32 Global::GetFPGARevision()
+{
+ tRioStatusCode status = NiFpga_Status_Success;
+ UINT32 revision = global_->readRevision(&status);
+ wpi_setError(status);
+ return revision;
+}
+
+/**
+ * Read the microsecond-resolution timer on the FPGA.
+ *
+ * @return The current time in microseconds according to the FPGA (since FPGA reset).
+ */
+UINT32 Global::GetFPGATime()
+{
+ tRioStatusCode status = NiFpga_Status_Success;
+ UINT32 time = global_->readLocalTime(&status);
+ wpi_setError(status);
+ return time;
+}
+
+// RT hardware access functions exported from ni_emb.out
+extern "C"
+{
+ INT32 UserSwitchInput(INT32 nSwitch);
+ INT32 LedInput(INT32 led);
+ INT32 LedOutput(INT32 led, INT32 value);
+}
+
+/**
+ * Read the value of the USER1 DIP switch on the cRIO.
+ */
+INT32 Global::GetRIOUserSwitch()
+{
+ INT32 switchValue = UserSwitchInput(0);
+ wpi_assert(switchValue >= 0);
+ return switchValue > 0;
+}
+
+/**
+ * Set the state of the USER1 status LED on the cRIO.
+ */
+void Global::SetRIOUserLED(UINT32 state)
+{
+ LedOutput(0, state > 0);
+}
+
+/**
+ * Get the current state of the USER1 status LED on the cRIO.
+ * @return The curent state of the USER1 LED.
+ */
+INT32 Global::GetRIOUserLED()
+{
+ return LedInput(0);
+}
+
+/**
+ * Toggle the state of the USER1 status LED on the cRIO.
+ * @return The new state of the USER1 LED.
+ */
+INT32 Global::ToggleRIOUserLED()
+{
+ Synchronized sync(led_toggle_lock_);
+ INT32 ledState = !GetRIOUserLED();
+ SetRIOUserLED(ledState);
+ return ledState;
+}
+
+/**
+ * Set the state of the FPGA status LED on the cRIO.
+ */
+void Global::SetRIO_FPGA_LED(UINT32 state)
+{
+ tRioStatusCode status = NiFpga_Status_Success;
+ global_->writeFPGA_LED(state, &status);
+ wpi_setError(status);
+}
+
+/**
+ * Get the current state of the FPGA status LED on the cRIO.
+ * @return The curent state of the FPGA LED.
+ */
+INT32 Global::GetRIO_FPGA_LED()
+{
+ tRioStatusCode status = NiFpga_Status_Success;
+ bool ledValue = global_->readFPGA_LED(&status);
+ wpi_setError(status);
+ return ledValue;
+}
+
+/**
+ * Toggle the state of the FPGA status LED on the cRIO.
+ * @return The new state of the FPGA LED.
+ */
+INT32 Global::ToggleRIO_FPGA_LED()
+{
+ Synchronized sync(led_toggle_lock_);
+ INT32 ledState = !GetRIO_FPGA_LED();
+ SetRIO_FPGA_LED(ledState);
+ return ledState;
+}
diff --git a/aos/externals/WPILib/WPILib/Global.h b/aos/externals/WPILib/WPILib/Global.h
new file mode 100644
index 0000000..4857e09
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/Global.h
@@ -0,0 +1,42 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include <memory>
+
+#include "SensorBase.h"
+#include "Synchronized.h"
+#include "ChipObject.h"
+
+#ifndef WPILIB_GLOBAL_H_
+#define WPILIB_GLOBAL_H_
+
+class Global : public SensorBase {
+ public:
+ static Global *GetInstance();
+
+ UINT16 GetFPGAVersion();
+ UINT32 GetFPGARevision();
+ UINT32 GetFPGATime();
+ INT32 GetRIOUserSwitch();
+ void SetRIOUserLED(UINT32 state);
+ INT32 GetRIOUserLED();
+ INT32 ToggleRIOUserLED();
+ void SetRIO_FPGA_LED(UINT32 state);
+ INT32 GetRIO_FPGA_LED();
+ INT32 ToggleRIO_FPGA_LED();
+
+ private:
+ Global();
+ ~Global();
+
+ static Global *instance;
+ static ReentrantSemaphore instance_lock;
+
+ ::std::auto_ptr<tGlobal> global_;
+ ReentrantSemaphore led_toggle_lock_;
+};
+
+#endif // WPILIB_GLOBAL_H_
diff --git a/aos/externals/WPILib/WPILib/IterativeRobot.cpp b/aos/externals/WPILib/WPILib/IterativeRobot.cpp
index 96e9b71..579ea2b 100644
--- a/aos/externals/WPILib/WPILib/IterativeRobot.cpp
+++ b/aos/externals/WPILib/WPILib/IterativeRobot.cpp
@@ -61,7 +61,7 @@
/**
* Get the period for the periodic functions.
- * Returns 0.0 if configured to syncronize with DS control data packets.
+ * Returns 0.0 if configured to synchronize with DS control data packets.
* @return Period of the periodic function calls
*/
double IterativeRobot::GetPeriod()
@@ -87,8 +87,7 @@
*
* This specific StartCompetition() implements "main loop" behavior like that of the FRC
* control system in 2008 and earlier, with a primary (slow) loop that is
- * called periodically, and a "fast loop" (a.k.a. "spin loop") that is
- * called as fast as possible with no delay between calls.
+ * called periodically.
*/
void IterativeRobot::StartCompetition()
{
diff --git a/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h b/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h
index 7237afa..b3e620e 100644
--- a/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h
+++ b/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h
@@ -24,6 +24,10 @@
this->subsystem = subsystem;
this->name = name;
this->isSensor = isSensor;
+#else
+ (void)subsystem;
+ (void)name;
+ (void)isSensor;
#endif
}
};
diff --git a/aos/externals/WPILib/WPILib/Module.cpp b/aos/externals/WPILib/WPILib/Module.cpp
index 99e26ea..2d01ea5 100644
--- a/aos/externals/WPILib/WPILib/Module.cpp
+++ b/aos/externals/WPILib/WPILib/Module.cpp
@@ -8,6 +8,9 @@
#include "AnalogModule.h"
#include "DigitalModule.h"
//#include "SolenoidModule.h"
+#include "Utility.h"
+
+ReentrantSemaphore Module::m_semaphore;
Module* Module::m_modules[kMaxModules] = {NULL};
@@ -21,6 +24,7 @@
: m_moduleType (type)
, m_moduleNumber (number)
{
+ Synchronized sync(m_semaphore);
m_modules[ToIndex(type, number)] = this;
}
@@ -29,6 +33,7 @@
*/
Module::~Module()
{
+ m_modules[ToIndex(m_moduleType, m_moduleNumber)] = NULL;
}
/**
@@ -39,6 +44,7 @@
*/
Module* Module::GetModule(nLoadOut::tModuleType type, UINT8 number)
{
+ Synchronized sync(m_semaphore);
if (m_modules[ToIndex(type, number)] == NULL)
{
switch(type)
@@ -70,7 +76,20 @@
*/
UINT8 Module::ToIndex(nLoadOut::tModuleType type, UINT8 number)
{
- if (number == 0 || number > kMaxModuleNumber) return 0;
- if (type < nLoadOut::kModuleType_Analog || type > nLoadOut::kModuleType_Solenoid) return 0;
+ if (number == 0 || number > kMaxModuleNumber) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "Trying to get index for invalid module %d",
+ static_cast<int>(number));
+ wpi_assertWithMessage(false, buf);
+ return 0;
+ }
+ if (type < nLoadOut::kModuleType_Analog ||
+ type > nLoadOut::kModuleType_Solenoid) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "Trying to get index for invalid module type %d",
+ static_cast<int>(type));
+ wpi_assertWithMessage(false, buf);
+ return 0;
+ }
return (type * kMaxModuleNumber) + (number - 1);
}
diff --git a/aos/externals/WPILib/WPILib/Module.h b/aos/externals/WPILib/WPILib/Module.h
index 1c9f976..45b10ff 100644
--- a/aos/externals/WPILib/WPILib/Module.h
+++ b/aos/externals/WPILib/WPILib/Module.h
@@ -9,6 +9,7 @@
#include "SensorBase.h"
#include "NetworkCommunication/LoadOut.h"
+#include "Synchronized.h"
#define kMaxModules (nLoadOut::kModuleType_Solenoid * kMaxModuleNumber + (kMaxModuleNumber - 1))
@@ -20,7 +21,7 @@
static Module *GetModule(nLoadOut::tModuleType type, UINT8 number);
protected:
- explicit Module(nLoadOut::tModuleType type, UINT8 number);
+ Module(nLoadOut::tModuleType type, UINT8 number);
virtual ~Module();
nLoadOut::tModuleType m_moduleType; ///< The type of module represented.
@@ -29,6 +30,7 @@
private:
static UINT8 ToIndex(nLoadOut::tModuleType type, UINT8 number);
static Module* m_modules[kMaxModules];
+ static ReentrantSemaphore m_semaphore;
};
#endif
diff --git a/aos/externals/WPILib/WPILib/MotorSafety.h b/aos/externals/WPILib/WPILib/MotorSafety.h
index 15481d8..a13ac38 100644
--- a/aos/externals/WPILib/WPILib/MotorSafety.h
+++ b/aos/externals/WPILib/WPILib/MotorSafety.h
@@ -16,6 +16,7 @@
virtual void StopMotor() = 0;
virtual void SetSafetyEnabled(bool enabled) = 0;
virtual bool IsSafetyEnabled() = 0;
+ // May write to the first 64 bytes of desc.
virtual void GetDescription(char *desc) = 0;
};
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp
new file mode 100644
index 0000000..51dc68a
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp
@@ -0,0 +1,410 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "WPILib/NetworkRobot/NetworkRobot.h"
+
+#include <sockLib.h>
+#include <stdint.h>
+#include <selectLib.h>
+#include <assert.h>
+
+#include "WPILib/Utility.h"
+#include "WPILib/WPIErrors.h"
+#include "WPILib/SensorBase.h"
+#include "WPILib/DriverStation.h"
+#include "WPILib/Timer.h"
+
+const double NetworkRobot::kDisableTime = 0.15;
+
+NetworkRobot::NetworkRobot(UINT16 receive_port, const char *sender_address,
+ UINT16 send_port, const char *receiver_address)
+ : receive_port_(receive_port), sender_address_(sender_address),
+ send_port_(send_port), receiver_address_(receiver_address),
+ receive_socket_(-1), send_socket_(-1),
+ joystick_values_(),
+ send_task_("DS_Send", reinterpret_cast<FUNCPTR>(StaticSendLoop)),
+ last_received_timestamp_(0.0),
+ digital_modules_(), solenoid_bases_(),
+ allocated_digital_outputs_() {
+}
+
+NetworkRobot::~NetworkRobot() {
+ // Nothing we can really do about any errors for either of these.
+ if (receive_socket_ != -1) {
+ close(receive_socket_);
+ }
+ if (send_socket_ != -1) {
+ close(send_socket_);
+ }
+
+ for (size_t i = 0;
+ i < sizeof(solenoid_bases_) / sizeof(solenoid_bases_[0]);
+ ++i) {
+ delete solenoid_bases_[i];
+ }
+ for (size_t module = 0;
+ module < sizeof(digital_modules_) / sizeof(digital_modules_[0]);
+ ++module) {
+ for (int i = 0; i < 16; ++i) {
+ if (allocated_digital_outputs_[module] & (1 << i)) {
+ digital_modules_[module]->FreeDIO(15 - i);
+ }
+ }
+ }
+}
+
+bool NetworkRobot::FillinInAddr(const char *const_ip, in_addr *inet_address) {
+ // A copy of the passed in address string because vxworks has the function
+ // signature without the const and I don't really trust it not to do something
+ // weird and change it.
+ // The size is the maximum length of an IP address (including the terminating
+ // NUL) (ie "123.456.789.123").
+ char non_const_ip[3 + 1 + 3 + 1 + 3 + 1 + 3 + 1];
+ size_t ip_length = strlen(const_ip);
+ if (ip_length >= sizeof(non_const_ip)) {
+ char buf[128];
+ snprintf(buf, sizeof(buf), "IP address '%s' is %zd bytes long"
+ " but should only be %zd", const_ip,
+ ip_length, sizeof(non_const_ip) - 1);
+ wpi_setErrorWithContext(-1, buf);
+ return false;
+ }
+ memcpy(non_const_ip, const_ip, ip_length + 1);
+ errno = 0;
+ if (inet_aton(non_const_ip, inet_address) != 0) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "inet_aton(%s)", const_ip);
+ wpi_setErrnoErrorWithContext(buf);
+ return false;
+ }
+ return true;
+}
+
+void NetworkRobot::StartCompetition() {
+ // Multiplied by 2 to give ourselves plenty of time to get around to feeding
+ // it before it completely cuts out everything.
+ m_watchdog.SetExpiration(kDisableTime * 2);
+
+ CreateReceiveSocket();
+ if (StatusIsFatal()) return;
+ CreateSendSocket();
+ if (StatusIsFatal()) return;
+
+ send_task_.Start(reinterpret_cast<uintptr_t>(this));
+
+ if (!FillinInAddr(sender_address_, &expected_sender_address_)) return;
+
+ while (!StatusIsFatal()) {
+ if ((Timer::GetPPCTimestamp() - last_received_timestamp_) > kDisableTime) {
+ StopOutputs();
+ }
+ ReceivePacket();
+ }
+ StopOutputs();
+}
+
+void NetworkRobot::StopOutputs() {
+ for (size_t module = 0;
+ module < sizeof(digital_modules_) / sizeof(digital_modules_[0]);
+ ++module) {
+ DigitalModule *digital_module = digital_modules_[module];
+
+ if (digital_module != NULL) {
+ for (int i = 0; i < 10; ++i) {
+ // 0 means stop sending anything.
+ digital_module->SetPWM(i + 1, 0);
+ }
+
+ // Turn off all of the ones that we're responsible for.
+ digital_module->SetDIOs(allocated_digital_outputs_[module], 0);
+
+ // Turn off all of the relays (both directions).
+ digital_module->SetRelaysForward(0xFF, 0);
+ digital_module->SetRelaysReverse(0xFF, 0);
+ }
+ }
+
+ // Can't do anything intelligent with solenoids. Turning them off can be just
+ // as dangerous as leaving them on.
+
+ // We took care of it, so we don't want the watchdog to permanently disable
+ // us.
+ m_watchdog.Feed();
+}
+
+bool NetworkRobot::WaitForData() {
+ assert(kDisableTime < 1.0);
+
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = kDisableTime *
+ 1000.0 /*seconds to mseconds*/ *
+ 1000.0 /*mseconds to useconds*/;
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(receive_socket_, &fds);
+
+ int ret = select(receive_socket_ + 1,
+ &fds, // read fds
+ NULL, // write fds
+ NULL, // exception fds (not supported)
+ &timeout);
+ if (ret == 0) {
+ // timeout
+ return false;
+ } else if (ret == 1) {
+ return true;
+ }
+ wpi_setErrnoErrorWithContext("waiting until the socket has data");
+ return false;
+}
+
+void NetworkRobot::ReceivePacket() {
+ if (!WaitForData()) return;
+
+ union {
+ sockaddr addr;
+ sockaddr_in in;
+ } sender_address;
+ int sender_address_length = sizeof(sender_address);
+ int received = recvfrom(receive_socket_,
+ reinterpret_cast<char *>(&values_),
+ sizeof(values_),
+ 0,
+ &sender_address.addr,
+ &sender_address_length);
+ if (received == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ||
+ errno == ETIMEDOUT) {
+ // All of these are various kinds of timing out.
+ return;
+ }
+ wpi_setErrnoErrorWithContext("recvfrom on motor value socket");
+ return;
+ }
+ wpi_assert(static_cast<size_t>(sender_address_length) >=
+ sizeof(sender_address.in));
+ if (sender_address.in.sin_addr.s_addr !=
+ expected_sender_address_.s_addr) {
+ char address[INET_ADDR_LEN];
+ inet_ntoa_b(sender_address.in.sin_addr, address);
+ char buf[64];
+ snprintf(buf, sizeof(buf), "Received packet from wrong IP %s", address);
+ wpi_setErrorWithContext(1, buf);
+ return;
+ }
+
+ if (received != sizeof(values_)) {
+ char buf[64];
+ snprintf(buf, sizeof(buf),
+ "Got packet with %zd bytes but expected %zd\n",
+ received, sizeof(values_));
+ wpi_setErrorWithContext(1, buf);
+ return;
+ }
+ if (values_.CheckHashValue()) {
+ ProcessPacket();
+ } else {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "Hash Value Is %x", values_.hash_value);
+ wpi_setErrorWithContext(1, buf);
+ return;
+ }
+}
+
+void NetworkRobot::ProcessPacket() {
+ int8_t digital_number = values_.digital_module;
+ if (digital_number != -1) {
+ if (digital_number == 0) {
+ digital_number = SensorBase::GetDefaultDigitalModule();
+ }
+ if (digital_number < 1 || digital_number > 2) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "Got Digital Module %d",
+ static_cast<int>(digital_number));
+ wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, buf);
+ return;
+ }
+ DigitalModule *digital_module = digital_modules_[digital_number - 1];
+ if (digital_module == NULL) {
+ digital_module = digital_modules_[digital_number - 1] =
+ DigitalModule::GetInstance(digital_number);
+ for (int i = 0; i < 10; ++i) {
+ digital_module->SetPWMPeriodScale(i + 1, 0);
+ }
+ }
+
+ for (int i = 0; i < 10; ++i) {
+ digital_module->SetPWM(i + 1, values_.pwm_outputs[i]);
+ }
+
+ uint16_t old_allocated = allocated_digital_outputs_[digital_number - 1];
+ // Have to keep track of which ones we've (de)allocated as we go through in
+ // case we have trouble allocating one of them in the middle.
+ for (int i = 0; i < 16; ++i) {
+ // If we have it allocated and this packet says we shouldn't.
+ if ((old_allocated & (1 << i)) &&
+ !(values_.digital_output_enables & (1 << i))) {
+ digital_module->FreeDIO(15 - i);
+ allocated_digital_outputs_[digital_number - 1] &= ~(1 << i);
+ // If this packet says we should have it allocated and we don't.
+ } else if ((values_.digital_output_enables & (1 << i)) &&
+ !(old_allocated & (1 << i))) {
+ if (!digital_module->AllocateDIO(15 - i, false /*input*/)) return;
+ allocated_digital_outputs_[digital_number - 1] |= 1 << i;
+ }
+ }
+ wpi_assertEqual(allocated_digital_outputs_[digital_number - 1],
+ values_.digital_output_enables);
+ digital_module->SetDIOs(values_.digital_output_enables,
+ values_.digital_output_values);
+
+ if (values_.pressure_switch_channel != 0 &&
+ values_.compressor_channel != 0) {
+ digital_module->SetRelayForward(values_.compressor_channel,
+ !digital_module->GetDIO(
+ values_.pressure_switch_channel));
+ }
+ }
+
+ int8_t solenoid_number = values_.solenoid_module;
+ if (solenoid_number != -1) {
+ if (solenoid_number == 0) {
+ solenoid_number = SensorBase::GetDefaultSolenoidModule();
+ }
+ if (solenoid_number < 1 || solenoid_number > 2) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "Got Solenoid Module %d",
+ static_cast<int>(solenoid_number));
+ wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, buf);
+ return;
+ }
+ SolenoidBase *solenoid_base = solenoid_bases_[solenoid_number - 1];
+ if (solenoid_base == NULL) {
+ solenoid_base = solenoid_bases_[solenoid_number - 1] =
+ new SolenoidBase(solenoid_number);
+ }
+
+ solenoid_base->SetAll(values_.solenoid_values);
+ }
+
+ // This code can only assume that whatever is sending it values knows what
+ // state it should be in.
+ DriverStation::FMSState state = m_ds->GetCurrentState();
+ {
+ // Don't have to synchronize around getting the current state too because
+ // it's ok if we're 1 cycle off. It would just be bad if we reported not
+ // being in any state or in 2 states at once.
+ RWLock::Locker state_locker(m_ds->GetUserStateLock(), true);
+ m_ds->InDisabled(state == DriverStation::FMSState::kDisabled);
+ m_ds->InAutonomous(state == DriverStation::FMSState::kAutonomous);
+ m_ds->InOperatorControl(state == DriverStation::FMSState::kTeleop);
+ m_ds->InTest(state == DriverStation::FMSState::kTestMode);
+ }
+
+ m_watchdog.Feed();
+ last_received_timestamp_ = Timer::GetPPCTimestamp();
+}
+
+void NetworkRobot::SendLoop() {
+ while (!StatusIsFatal()) {
+ m_ds->WaitForData();
+
+ {
+ RWLock::Locker data_locker(m_ds->GetDataReadLock());
+ // Get a pointer to the data and then cast away the volatile because it's
+ // annoying to propagate it all over and it's unnecessary here because
+ // we have a lock on the data so it's not going to change.
+ const FRCCommonControlData *data =
+ const_cast<const FRCCommonControlData *>(m_ds->GetControlData());
+ CopyStickValues(0, data->stick0Axes, data->stick0Buttons);
+ CopyStickValues(1, data->stick1Axes, data->stick1Buttons);
+ CopyStickValues(2, data->stick2Axes, data->stick2Buttons);
+ CopyStickValues(3, data->stick3Axes, data->stick3Buttons);
+
+ joystick_values_.control_packet_index = data->packetIndex;
+
+ joystick_values_.team_number = data->teamID;
+ joystick_values_.alliance = data->dsID_Alliance;
+ joystick_values_.position = data->dsID_Position;
+
+ joystick_values_.control.set_test_mode(data->test);
+ joystick_values_.control.set_fms_attached(data->fmsAttached);
+ joystick_values_.control.set_autonomous(data->autonomous);
+ joystick_values_.control.set_enabled(data->enabled);
+ }
+
+ ++joystick_values_.index;
+
+ joystick_values_.FillInHashValue();
+ }
+}
+
+void NetworkRobot::CopyStickValues(int number,
+ const INT8 (&axes)[6],
+ UINT16 buttons) {
+ for (int i = 0; i < 6; ++i) {
+ joystick_values_.joysticks[number].axes[i] = axes[i];
+ }
+ joystick_values_.joysticks[number].buttons = buttons;
+}
+
+void NetworkRobot::CreateReceiveSocket() {
+ wpi_assertEqual(receive_socket_, -1);
+
+ receive_socket_ = socket(AF_INET, SOCK_DGRAM, 0);
+ if (receive_socket_ < 0) {
+ wpi_setErrnoErrorWithContext("Creating UDP Socket");
+ receive_socket_ = -1;
+ return;
+ }
+
+ union {
+ sockaddr_in in;
+ sockaddr addr;
+ } address;
+ memset(&address, 0, sizeof(address));
+ address.in.sin_family = AF_INET;
+ address.in.sin_port = receive_port_;
+ address.in.sin_addr.s_addr = INADDR_ANY;
+ if (bind(receive_socket_, &address.addr, sizeof(address.addr)) == ERROR) {
+ wpi_setErrnoErrorWithContext("Binding Socket");
+ return;
+ }
+
+ int on = 1;
+ errno = 0;
+ if (ioctl(receive_socket_, FIONBIO, reinterpret_cast<int>(&on)) < 0) {
+ wpi_setErrnoErrorWithContext("Setting Socket to Non-Blocking Mode");
+ return;
+ }
+}
+
+void NetworkRobot::CreateSendSocket() {
+ wpi_assertEqual(send_socket_, -1);
+
+ send_socket_ = socket(AF_INET, SOCK_DGRAM, 0);
+ if (send_socket_ < 0) {
+ wpi_setErrnoErrorWithContext("Creating UDP Socket");
+ send_socket_ = -1;
+ return;
+ }
+
+ union {
+ sockaddr_in in;
+ sockaddr addr;
+ } address;
+ memset(&address, 0, sizeof(address));
+ address.in.sin_family = AF_INET;
+ address.in.sin_port = send_port_;
+ if (!FillinInAddr(receiver_address_, &address.in.sin_addr)) return;
+
+ if (bind(send_socket_, &address.addr, sizeof(address.addr)) == ERROR) {
+ wpi_setErrnoErrorWithContext("Binding Socket");
+ return;
+ }
+}
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h
new file mode 100644
index 0000000..b17bef2
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h
@@ -0,0 +1,124 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_H_
+#define WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_H_
+
+#include <inetLib.h>
+
+#include "WPILib/NetworkRobot/NetworkRobotValues.h"
+#include "WPILib/RobotBase.h"
+#include "WPILib/Base.h"
+#include "WPILib/DigitalModule.h"
+#include "WPILib/ErrorBase.h"
+#include "WPILib/SolenoidBase.h"
+#include "WPILib/Task.h"
+
+// A simple implementation of receiving motor values over UDP and sending
+// joystick data back out.
+// Deals with turning a compressor on and off at the same time.
+// You should not try to change any of the outputs on any of the modules that
+// you have this class control outside of this class.
+// This class takes care of disabling outputs when no packets are received in
+// kDisableTime and feeding the Watchdog correctly.
+//
+// The receiving interface consists of receiving NetworkRobotValues structs on
+// a given UDP port from a given sender address. Each time a set of motor values
+// is received, this class will update all output values.
+//
+// The sending interface consists of sending NetworkRobotJoysticks structs on a
+// given UDP port to a given sender address. Each time a new Driver's Station
+// packet is received from the FMS code this class will send out another packet
+// with the new values.
+class NetworkRobot : public RobotBase, public ErrorBase {
+ protected:
+ // Does not take ownership of *sender_address or *receiver_address.
+ NetworkRobot(UINT16 receive_port, const char *sender_address,
+ UINT16 send_port, const char *receiver_address);
+ virtual ~NetworkRobot();
+
+ virtual void StartCompetition();
+
+ // Called when a valid packet has been received into values_.
+ // Subclasses can override if they want to do more, however they still need to
+ // call this implementation.
+ virtual void ProcessPacket();
+
+ // Called when no packet has been received for too long and it's time to stop
+ // everything.
+ // Subclasses can override if they want to do more, however they still need to
+ // call this implementation.
+ virtual void StopOutputs();
+
+ private:
+ // How long between packets we wait until we disable all of the outputs (in
+ // seconds).
+ // Must stay less than 1 second with the current implementation.
+ static const double kDisableTime;
+
+ // Deals with calling inet_aton and passing any errors on to the logging
+ // system. A helper is necessary here because inet_aton is normally just kind
+ // of annoying to deal with, but under vxworks, you have to make a copy of the
+ // string before doing anything with etc etc so it's really complicated.
+ // Returns whether or not it was successful. If it returns false, then an
+ // error will have already been recorded.
+ bool FillinInAddr(const char *const_ip, in_addr *inet_address);
+
+ // Waits for receive_socket_ to become readable for up to kDisableTime.
+ // Returns whether or not there is readable data available.
+ bool WaitForData();
+
+ // Receives a packet and calls ProcessPacket() if it's a good one.
+ void ReceivePacket();
+
+ // Gets run in a separate task to take DS data and send it out.
+ void SendLoop();
+ static void StaticSendLoop(void *self) {
+ static_cast<NetworkRobot *>(self)->SendLoop();
+ }
+
+ // Sets receive_socket_ to an opened socket listening on receive_port_ to UDP
+ // packets from sender_address_.
+ void CreateReceiveSocket();
+ // Sets send_socket_ to an opened socket sending UDP packets on send_port_ to
+ // receiver_address_.
+ void CreateSendSocket();
+
+ const UINT16 receive_port_;
+ const char *const sender_address_;
+ struct in_addr expected_sender_address_;
+
+ const UINT16 send_port_;
+ const char *const receiver_address_;
+
+ int receive_socket_;
+ NetworkRobotValues values_;
+
+ int send_socket_;
+ NetworkRobotJoysticks joystick_values_;
+ Task send_task_;
+
+ // Helper function to copy all of the data for a single joystick into
+ // joystick_values_.
+ // axes and buttons get copied into joystick_values_.joysticks[number].
+ void CopyStickValues(int number, const INT8 (&axes)[6], UINT16 buttons);
+
+ // Using Timer::GetPPCTimestamp() values.
+ double last_received_timestamp_;
+
+ // Ownership of the pointers stored here stays in DigitalModule.
+ DigitalModule *digital_modules_[2];
+ // This object owns pointers stored here.
+ SolenoidBase *solenoid_bases_[2];
+
+ // A bitmask of all of the digital outputs that we have currently allocated.
+ // In hardware order.
+ UINT16 allocated_digital_outputs_[2];
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkRobot);
+};
+
+#endif // WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_H_
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp
new file mode 100644
index 0000000..1d2e805
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp
@@ -0,0 +1,45 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include "WPILib/NetworkRobot/NetworkRobotValues.h"
+
+#include <string.h>
+
+namespace {
+
+uint32_t CalculateHashValue(const char *data, size_t size) {
+ uint32_t hash_value = 0;
+ for (size_t i = 0; i < size; ++i) {
+ hash_value = (((hash_value << 8) & 0xFF00) + (data[i] & 0xFF)) % 0x04C11DB7;
+ }
+ return hash_value;
+}
+
+} // namespace
+
+void NetworkRobotValues::FillInHashValue() {
+ hash_value = CalculateHashValue(reinterpret_cast<const char *>(this) +
+ sizeof(hash_value),
+ sizeof(*this) - sizeof(hash_value));
+}
+
+bool NetworkRobotValues::CheckHashValue() const {
+ return hash_value == CalculateHashValue(reinterpret_cast<const char *>(this) +
+ sizeof(hash_value),
+ sizeof(*this) - sizeof(hash_value));
+}
+
+void NetworkRobotJoysticks::FillInHashValue() {
+ hash_value = CalculateHashValue(reinterpret_cast<const char *>(this) +
+ sizeof(hash_value),
+ sizeof(*this) - sizeof(hash_value));
+}
+
+bool NetworkRobotJoysticks::CheckHashValue() const {
+ return hash_value == CalculateHashValue(reinterpret_cast<const char *>(this) +
+ sizeof(hash_value),
+ sizeof(*this) - sizeof(hash_value));
+}
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h
new file mode 100644
index 0000000..b66ad7c
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h
@@ -0,0 +1,128 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_VALUES_H_
+#define WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_VALUES_H_
+
+#include <stdint.h>
+
+// This file needs to not have any dependencies on any other parts of WPILib so
+// that it can be #included by other code (like that which sends these values).
+// All multi-byte values are sent over the network in big endian (network)
+// byte order.
+
+// The structure that actually gets sent over the network to the cRIO with motor
+// values.
+// All channel and module numbers are 1-based like usual.
+struct NetworkRobotValues {
+ // A hash value to make sure that corrupted packets don't get used.
+ // IMPORTANT: Must stay at the beginning.
+ uint32_t hash_value;
+
+ // Which digital module this packet has values for (1 or 2).
+ // 0 means the default one and -1 means to not update any one.
+ int8_t digital_module;
+ // Raw values for all 10 PWM outputs.
+ uint8_t pwm_outputs[10];
+
+ // Bitmasks for enabling digital outputs and what values to set them to.
+ // See DigitalModule::SetDIOs for which bit is which.
+ uint16_t digital_output_enables;
+ uint16_t digital_output_values;
+
+ // Channels for a presssure switch and compressor to turn on and off based on
+ // the value from it.
+ // Whatever compressor channel is sent will be updated with the value from the
+ // corresponding pressure switch channel when the packet is received.
+ // 0 means to not do anything.
+ uint8_t pressure_switch_channel, compressor_channel;
+
+ // Which solenoid module this packet has values for (1 or 2).
+ // 0 means the default one and -1 means to not update any one.
+ int8_t solenoid_module;
+ // 1 bit for each solenoid output.
+ uint8_t solenoid_values;
+
+ // Sets hash_value to the correct value for the rest of the data.
+ void FillInHashValue();
+ // Returns whether or not hash_value matches the rest of the data. Any byte
+ // order conversion must be performed ONLY on the hash_value field before
+ // calling this.
+ bool CheckHashValue() const;
+} __attribute__((packed));
+
+// The structure that the cRIO sends out with joystick positions etc.
+struct NetworkRobotJoysticks {
+ // A hash value to make sure that corrupted packets don't get used.
+ // IMPORTANT: Must stay at the beginning.
+ uint32_t hash_value;
+
+ // A structure that stores the information about a joystick and instances for
+ // each of the 4 joysticks.
+ struct Joystick {
+ // Bitmask of the button values.
+ // The LSB is button 1 and only a maximum of 12 are supported.
+ uint16_t buttons;
+ // Raw values for each of the 6 joystick axes.
+ // The range of values depends on the joystick.
+ int8_t axes[6];
+ } __attribute__((packed)) joysticks[4];
+
+ // The index number from the DS.
+ uint16_t control_packet_index;
+ // An index for this structure. Gets incremented by 1 for each one of these
+ // structures that is sent.
+ uint16_t index;
+
+ // The team number that the DS is configured for.
+ uint16_t team_number;
+ // Which alliance the robot is on. Should be 'R' or 'B'.
+ char alliance;
+ // Which position the DS is in on the field. Should be '1', '2', or '3'.
+ char position;
+
+ // A structure that efficiently stores the control data bits from the DS and
+ // has methods for safely and easily getting and setting them and an instance
+ // of it for actually sending the information.
+ // Not just a C bitfield because those are a mess for portability.
+ struct ControlInformation {
+ bool test_mode() { return GetBit(kTestMode); }
+ void set_test_mode(bool value) { SetBit(kTestMode, value); }
+ bool fms_attached() { return GetBit(kFmsAttached); }
+ void set_fms_attached(bool value) { SetBit(kFmsAttached, value); }
+ bool autonomous() { return GetBit(kAutonomous); }
+ void set_autonomous(bool value) { SetBit(kAutonomous, value); }
+ bool enabled() { return GetBit(kEnabled); }
+ void set_enabled(bool value) { SetBit(kEnabled, value); }
+
+ private:
+ // Constants for which bit is which.
+ static const int kTestMode = 0;
+ static const int kFmsAttached = 1;
+ static const int kAutonomous = 2;
+ static const int kEnabled = 3;
+
+ bool GetBit(int bit) {
+ return bits & (1 << bit);
+ }
+ void SetBit(int bit, bool value) {
+ uint8_t mask = 1 << bit;
+ bits &= ~mask;
+ bits |= (mask & (value ? 0xFF : 0x00));
+ }
+
+ uint8_t bits;
+ } __attribute__((packed)) control;
+
+ // Sets hash_value to the correct value for the rest of the data.
+ void FillInHashValue();
+ // Returns whether or not hash_value matches the rest of the data. Any byte
+ // order conversion must be performed ONLY on the hash_value field before
+ // calling this.
+ bool CheckHashValue() const;
+} __attribute__((packed));
+
+#endif // WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_VALUES_H_
diff --git a/aos/externals/WPILib/WPILib/PWM.cpp b/aos/externals/WPILib/WPILib/PWM.cpp
index 05bf16d..7ae5370 100644
--- a/aos/externals/WPILib/WPILib/PWM.cpp
+++ b/aos/externals/WPILib/WPILib/PWM.cpp
@@ -42,9 +42,9 @@
}
snprintf(buf, 64, "PWM %d (Module: %d)", channel, moduleNumber);
- if (allocated->Allocate((moduleNumber - 1) * kPwmChannels + channel - 1, buf) == ~0ul)
+ if (allocated->Allocate((moduleNumber - 1) * kPwmChannels + channel - 1,
+ buf, this) == ~0ul)
{
- CloneError(allocated);
return;
}
m_channel = channel;
@@ -92,7 +92,8 @@
if (m_module)
{
m_module->SetPWM(m_channel, kPwmDisabled);
- allocated->Free((m_module->GetNumber() - 1) * kPwmChannels + m_channel - 1);
+ allocated->Free((m_module->GetNumber() - 1) * kPwmChannels + m_channel - 1,
+ this);
}
}
diff --git a/aos/externals/WPILib/WPILib/RWLock.cpp b/aos/externals/WPILib/WPILib/RWLock.cpp
new file mode 100644
index 0000000..9918af0
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/RWLock.cpp
@@ -0,0 +1,202 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#include <taskLib.h>
+#include <intLib.h>
+#include <assert.h>
+
+#include "RWLock.h"
+
+// A wrapper for assert that allows it to be easily turned off just in this
+// file. That configuration is recommended for normal use because it means less
+// code that gets executed with the scheduler locked.
+#if 1
+#define rwlock_assert(expression) assert(expression)
+// A macro to easily assert that some expression (possibly with side effects)
+// is 0.
+#define rwlock_assert_success(expression) do { \
+ int ret = (expression); \
+ assert(ret == 0); \
+} while (false)
+#else
+#define rwlock_assert(expression) ((void)0)
+#define rwlock_assert_success(expression) ((void)(expression))
+#endif
+
+/**
+ * Class that locks the scheduler and then unlocks it in the destructor.
+ */
+class TaskSchedulerLocker {
+ public:
+ TaskSchedulerLocker() {
+ rwlock_assert_success(taskLock());
+ }
+ ~TaskSchedulerLocker() {
+ rwlock_assert_success(taskUnlock());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TaskSchedulerLocker);
+};
+
+RWLock::Locker::Locker(RWLock *lock, bool write)
+ : lock_(lock), num_(lock_->Lock(write)) {
+}
+
+RWLock::Locker::Locker(const Locker &other)
+ : lock_(other.lock_), num_(lock_->AddLock()) {
+}
+
+RWLock::Locker::~Locker() {
+ lock_->Unlock(num_);
+}
+
+// RWLock is implemented by just locking the scheduler while doing anything
+// because that is the only way under vxworks to do much of anything atomically.
+
+RWLock::RWLock()
+ : number_of_write_locks_(0),
+ number_of_writers_pending_(0),
+ number_of_readers_(0),
+ reader_tasks_(),
+ read_ready_(semBCreate(SEM_Q_PRIORITY, SEM_EMPTY)),
+ write_ready_(semBCreate(SEM_Q_PRIORITY, SEM_EMPTY)) {
+ rwlock_assert(read_ready_ != NULL);
+ rwlock_assert(write_ready_ != NULL);
+}
+
+RWLock::~RWLock() {
+ // Make sure that nobody else currently has a lock or will ever be able to.
+ Lock(true);
+
+ rwlock_assert_success(semDelete(read_ready_));
+ rwlock_assert_success(semDelete(write_ready_));
+}
+
+int RWLock::Lock(bool write) {
+ assert(!intContext());
+
+ int current_task = taskIdSelf();
+ // It's safe to do this check up here (outside of locking the scheduler)
+ // because we only care whether the current task is in there or not and that
+ // can't be changed because it's the task doing the checking.
+ bool current_task_holds_already = TaskOwns(current_task);
+
+ TaskSchedulerLocker scheduler_locker;
+
+ // We can't be reading and writing at the same time.
+ rwlock_assert(!((number_of_write_locks_ > 0) && (number_of_readers_ > 0)));
+
+ if (write) {
+ assert(!current_task_holds_already);
+ // If somebody else already has it locked.
+ // Don't have to worry about another task getting scheduled after
+ // write_ready_ gets given because nobody else (except another writer, which
+ // would just block on it) will do anything while there are pending
+ // writer(s).
+ if ((number_of_readers_ > 0) || (number_of_write_locks_ > 0)) {
+ ++number_of_writers_pending_;
+ // Wait for it to be our turn.
+ rwlock_assert_success(semTake(write_ready_, WAIT_FOREVER));
+ --number_of_writers_pending_;
+ } else {
+ rwlock_assert(number_of_writers_pending_ == 0);
+ }
+ rwlock_assert((number_of_write_locks_ == 0) && (number_of_readers_ == 0));
+ number_of_write_locks_ = 1;
+ return 0;
+ } else { // read
+ // While there are one or more writers active or waiting.
+ // Has to be a loop in case a writer gets scheduled between the time
+ // read_ready_ gets flushed and we run.
+ while ((number_of_write_locks_ > 0) || (number_of_writers_pending_ > 0)) {
+ // Wait for the writer(s) to finish.
+ rwlock_assert_success(semTake(read_ready_, WAIT_FOREVER));
+ }
+
+ int num = number_of_readers_;
+ number_of_readers_ = num + 1;
+ assert(num < kMaxReaders);
+ rwlock_assert(reader_tasks_[num] == 0);
+ reader_tasks_[num] = current_task;
+ rwlock_assert((number_of_write_locks_ == 0) && (number_of_readers_ > 0));
+ return num;
+ }
+}
+
+void RWLock::Unlock(int num) {
+ assert(!intContext());
+ TaskSchedulerLocker scheduler_locker;
+
+ // We have to be reading or writing right now, but not both.
+ rwlock_assert((number_of_write_locks_ > 0) != (number_of_readers_ > 0));
+
+ if (number_of_write_locks_ > 0) { // we're currently writing
+ rwlock_assert(num == 0);
+ --number_of_write_locks_;
+ rwlock_assert((number_of_write_locks_ >= 0) &&
+ (number_of_writers_pending_ >= 0));
+ // If we were the last one.
+ if (number_of_write_locks_ == 0) {
+ // If there are no other tasks waiting to write (because otherwise they
+ // need to get priority over any readers).
+ if (number_of_writers_pending_ == 0) {
+ // Wake up any waiting readers.
+ rwlock_assert_success(semFlush(read_ready_));
+ } else {
+ // Wake up a waiting writer.
+ // Not a problem if somebody else already did this before the waiting
+ // writer got a chance to take it because it'll do nothing and return
+ // success.
+ rwlock_assert_success(semGive(write_ready_));
+ }
+ }
+ } else { // we're curently reading
+ rwlock_assert(reader_tasks_[num] == taskIdSelf());
+ reader_tasks_[num] = 0;
+ --number_of_readers_;
+ rwlock_assert(number_of_readers_ >= 0 &&
+ (number_of_writers_pending_ >= 0));
+ // If we were the last one.
+ if (number_of_readers_ == 0) {
+ // If there are any writers waiting for a chance to go.
+ if (number_of_writers_pending_ > 0) {
+ // Wake a waiting writer.
+ // Not a problem if somebody else already did this before the waiting
+ // writer got a chance to take it because it'll still return success.
+ rwlock_assert_success(semGive(write_ready_));
+ }
+ }
+ }
+}
+
+int RWLock::AddLock() {
+ assert(!intContext());
+ // TODO: Replace this with just atomically incrementing the right number once
+ // we start using a GCC new enough to have the nice atomic builtins.
+ // That will be safe because whether we're currently reading or writing can't
+ // change in the middle of this.
+ TaskSchedulerLocker scheduler_locker;
+
+ // We have to be reading or writing right now, but not both.
+ rwlock_assert((number_of_write_locks_ > 0) != (number_of_readers_ > 0));
+
+ if (number_of_write_locks_ > 0) { // we're currently writing
+ ++number_of_write_locks_;
+ return 0;
+ } else { // we're currently reading
+ return number_of_readers_++;
+ }
+}
+
+bool RWLock::TaskOwns(int task_id) {
+ for (size_t i = 0;
+ i < sizeof(reader_tasks_) / sizeof(reader_tasks_[0]);
+ ++i) {
+ if (reader_tasks_[i] == task_id) return true;
+ }
+ return false;
+}
diff --git a/aos/externals/WPILib/WPILib/RWLock.h b/aos/externals/WPILib/WPILib/RWLock.h
new file mode 100644
index 0000000..13d7b50
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/RWLock.h
@@ -0,0 +1,125 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
+/*----------------------------------------------------------------------------*/
+
+#ifndef WPILIB_DATA_LOCK_H_
+#define WPILIB_DATA_LOCK_H_
+
+#include <semLib.h>
+
+#include "Base.h"
+
+/**
+ * Represents a read/write lock on using some shared data so that it will not
+ * be modified by any other tasks while code is using it.
+ *
+ * See <http://en.wikipedia.org/wiki/Readers-writer_lock> for an overview of how
+ * this can be used.
+ *
+ * In this implementation, if there are any writers pending, then any new
+ * attempts to acquire read locks will wait until all writers are done unless a
+ * read lock is already held by that same task.
+ */
+class RWLock {
+ public:
+ /**
+ * Represents an actual lock on the shared data. The destructor will free it.
+ *
+ * Intended to be used as an automatic (or local) variable so that the
+ * compiler will ensure that the destructor gets called no matter how the
+ * scope is exited.
+ *
+ * While it is possible to use new/delete to dynamically allocate an instance,
+ * the constructor and destructor still MUST still be called from the same
+ * task.
+ *
+ * Has a copy constructor which allows "copying" the lock that is held. Does
+ * not have an assignment operator because assigning a lock doesn't make much
+ * sense.
+ */
+ class Locker {
+ public:
+ /**
+ * @param write Whether to create a writer lock (creates a reader lock
+ * otherwise).
+ */
+ Locker(RWLock *lock, bool write);
+
+ /**
+ * Creates another lock of the same type. They can both be released
+ * (destructed) independently.
+ * NOTE: This does allow creating multiple write locks that are held at the
+ * same time.
+ */
+ Locker(const Locker &other);
+
+ /**
+ * Unlocks the lock.
+ */
+ ~Locker();
+
+ private:
+ RWLock *const lock_;
+ const int num_;
+
+ void operator=(const Locker &);
+ };
+
+ /**
+ * The maximum number of read locks that can be held at the same time.
+ */
+ static const int kMaxReaders = 64;
+
+ RWLock();
+ /**
+ * Waits until there are no more read or write locks held.
+ */
+ ~RWLock();
+
+ private:
+ // The number of write locks that are currently held.
+ int number_of_write_locks_;
+ // How many tasks are currently waiting to get a write lock.
+ // Each count in here corresponds to a task that is blocked on write_ready_.
+ int number_of_writers_pending_;
+
+ // How many read locks are currently held.
+ int number_of_readers_;
+
+ // The task ID of the task holding each read lock.
+ int reader_tasks_[kMaxReaders];
+
+ // Always locked. Gets semFlushed when readers are allowed to take the lock
+ // (after all writers are done).
+ SEM_ID read_ready_;
+ // Locked almost all of the time. Pending writers (who have to update
+ // number_of_writers_pending_) block locking this and it gets unlocked when it
+ // is time for one of them to go.
+ SEM_ID write_ready_;
+
+ // Acquires the appropriate kind of lock.
+ // Returns a value that must be passed to the corresponding Unlock call.
+ int Lock(bool write);
+ // Unlocks 1 lock.
+ // Does not need to know whether it is read or write because only one type of
+ // lock can be held at a time.
+ // num must be the return value from the corresponding Lock/AddLock call.
+ void Unlock(int num);
+ // Increments the lock count by 1.
+ // There must be at least 1 lock held during the execution of this method.
+ // Use the regular Unlock() to unlock a lock acquired this way.
+ // This is not the same as Lock(current_type) because this is the only way to
+ // acquire multiple write locks at the same time.
+ // Returns a value that must be passed to the corresponding Unlock call.
+ int AddLock();
+ // Checks whether task_id is in reader_tasks_.
+ bool TaskOwns(int task_id);
+
+ friend class Locker;
+
+ DISALLOW_COPY_AND_ASSIGN(RWLock);
+};
+
+#endif // WPILIB_DATA_LOCK_H_
diff --git a/aos/externals/WPILib/WPILib/Relay.cpp b/aos/externals/WPILib/WPILib/Relay.cpp
index dd59d52..9fb3a2d 100644
--- a/aos/externals/WPILib/WPILib/Relay.cpp
+++ b/aos/externals/WPILib/WPILib/Relay.cpp
@@ -43,9 +43,9 @@
if (m_direction == kBothDirections || m_direction == kForwardOnly)
{
snprintf(buf, 64, "Forward Relay %d (Module: %d)", m_channel, moduleNumber);
- if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels + m_channel - 1) * 2, buf) == ~0ul)
+ if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels +
+ m_channel - 1) * 2, buf, this) == ~0ul)
{
- CloneError(relayChannels);
return;
}
@@ -54,9 +54,9 @@
if (m_direction == kBothDirections || m_direction == kReverseOnly)
{
snprintf(buf, 64, "Reverse Relay %d (Module: %d)", m_channel, moduleNumber);
- if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels + m_channel - 1) * 2 + 1, buf) == ~0ul)
+ if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels
+ + m_channel - 1) * 2 + 1, buf, this) == ~0ul)
{
- CloneError(relayChannels);
return;
}
@@ -105,11 +105,13 @@
if (m_direction == kBothDirections || m_direction == kForwardOnly)
{
- relayChannels->Free(((m_module->GetNumber() - 1) * kRelayChannels + m_channel - 1) * 2);
+ relayChannels->Free(((m_module->GetNumber() - 1) *
+ kRelayChannels + m_channel - 1) * 2, this);
}
if (m_direction == kBothDirections || m_direction == kReverseOnly)
{
- relayChannels->Free(((m_module->GetNumber() - 1) * kRelayChannels + m_channel - 1) * 2 + 1);
+ relayChannels->Free(((m_module->GetNumber() - 1) *
+ kRelayChannels + m_channel - 1) * 2 + 1, this);
}
}
diff --git a/aos/externals/WPILib/WPILib/Resource.cpp b/aos/externals/WPILib/WPILib/Resource.cpp
index ec35eb3..ef7d15d 100644
--- a/aos/externals/WPILib/WPILib/Resource.cpp
+++ b/aos/externals/WPILib/WPILib/Resource.cpp
@@ -6,7 +6,6 @@
#include "Resource.h"
#include "WPIErrors.h"
-#include "ErrorBase.h"
ReentrantSemaphore Resource::m_createLock;
@@ -28,6 +27,7 @@
/**
* Factory method to create a Resource allocation-tracker *if* needed.
+ * Handles the necessary synchronization internally.
*
* @param r -- address of the caller's Resource pointer. If *r == NULL, this
* will construct a Resource and make *r point to it. If *r != NULL, i.e.
@@ -59,7 +59,7 @@
* When a resource is requested, mark it allocated. In this case, a free resource value
* within the range is located and returned after it is marked allocated.
*/
-UINT32 Resource::Allocate(const char *resourceDesc)
+UINT32 Resource::Allocate(const char *resourceDesc, const ErrorBase *error)
{
Synchronized sync(m_allocateLock);
for (UINT32 i=0; i < m_size; i++)
@@ -70,7 +70,7 @@
return i;
}
}
- wpi_setWPIErrorWithContext(NoAvailableResources, resourceDesc);
+ wpi_setStaticWPIErrorWithContext(error, NoAvailableResources, resourceDesc);
return ~0ul;
}
@@ -79,17 +79,18 @@
* The user requests a specific resource value, i.e. channel number and it is verified
* unallocated, then returned.
*/
-UINT32 Resource::Allocate(UINT32 index, const char *resourceDesc)
+UINT32 Resource::Allocate(UINT32 index, const char *resourceDesc,
+ const ErrorBase *error)
{
Synchronized sync(m_allocateLock);
if (index >= m_size)
{
- wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, resourceDesc);
+ wpi_setStaticWPIErrorWithContext(error, ChannelIndexOutOfRange, resourceDesc);
return ~0ul;
}
if ( m_isAllocated[index] )
{
- wpi_setWPIErrorWithContext(ResourceAlreadyAllocated, resourceDesc);
+ wpi_setStaticWPIErrorWithContext(error, ResourceAlreadyAllocated, resourceDesc);
return ~0ul;
}
m_isAllocated[index] = true;
@@ -102,18 +103,18 @@
* After a resource is no longer needed, for example a destructor is called for a channel assignment
* class, Free will release the resource value so it can be reused somewhere else in the program.
*/
-void Resource::Free(UINT32 index)
+void Resource::Free(UINT32 index, const ErrorBase *error)
{
Synchronized sync(m_allocateLock);
if (index == ~0ul) return;
if (index >= m_size)
{
- wpi_setWPIError(NotAllocated);
+ wpi_setStaticWPIError(error, NotAllocated);
return;
}
if (!m_isAllocated[index])
{
- wpi_setWPIError(NotAllocated);
+ wpi_setStaticWPIError(error, NotAllocated);
return;
}
m_isAllocated[index] = false;
diff --git a/aos/externals/WPILib/WPILib/Resource.h b/aos/externals/WPILib/WPILib/Resource.h
index dbe1936..11faaec 100644
--- a/aos/externals/WPILib/WPILib/Resource.h
+++ b/aos/externals/WPILib/WPILib/Resource.h
@@ -19,15 +19,18 @@
* The Resource class does not allocate the hardware channels or other
* resources; it just tracks which indices were marked in use by
* Allocate and not yet freed by Free.
+ *
+ * Several methods require an ErrorBase* so that they can report any errors.
*/
-class Resource : public ErrorBase
+class Resource
{
public:
virtual ~Resource();
static void CreateResourceObject(Resource **r, UINT32 elements);
- UINT32 Allocate(const char *resourceDesc);
- UINT32 Allocate(UINT32 index, const char *resourceDesc);
- void Free(UINT32 index);
+ UINT32 Allocate(const char *resourceDesc, const ErrorBase *error);
+ UINT32 Allocate(UINT32 index, const char *resourceDesc,
+ const ErrorBase *error);
+ void Free(UINT32 index, const ErrorBase *error);
private:
explicit Resource(UINT32 size);
diff --git a/aos/externals/WPILib/WPILib/RobotBase.cpp b/aos/externals/WPILib/WPILib/RobotBase.cpp
index 0bebf09..17c7682 100644
--- a/aos/externals/WPILib/WPILib/RobotBase.cpp
+++ b/aos/externals/WPILib/WPILib/RobotBase.cpp
@@ -19,6 +19,8 @@
void RobotBase::setInstance(RobotBase* robot)
{
+ // No point in synchronization here because it's private and it only gets
+ // called from robotTask.
wpi_assert(m_instance == NULL);
m_instance = robot;
}
@@ -30,8 +32,9 @@
/**
* Constructor for a generic robot program.
- * User code should be placed in the constuctor that runs before the Autonomous or Operator
- * Control period starts. The constructor will run to completion before Autonomous is entered.
+ * User code that should run before the Autonomous or Operator Control period
+ * starts should be placed in the subclass constructor.
+ * The constructor must finish before Autonomous can be entered.
*
* This must be used to ensure that the communications code starts. In the future it would be
* nice to put this code into it's own task that loads on boot so ensure that it runs.
@@ -45,7 +48,7 @@
/**
* Free the resources for a RobotBase class.
- * This includes deleting all classes that might have been allocated as Singletons to they
+ * This includes deleting all classes that might have been allocated as Singletons so they
* would never be deleted except here.
*/
RobotBase::~RobotBase()
@@ -95,7 +98,7 @@
}
/**
- * Determine if the robot is currently in Autnomous mode.
+ * Determine if the robot is currently in Autonomous mode.
* @return True if the robot is currently operating Autonomously as determined by the field controls.
*/
bool RobotBase::IsAutonomous()
@@ -135,18 +138,21 @@
*/
void RobotBase::robotTask(FUNCPTR factory, Task *task)
{
- RobotBase::setInstance((RobotBase*)factory());
- RobotBase::getInstance().m_task = task;
- RobotBase::getInstance().StartCompetition();
+ RobotBase *instance = (RobotBase*)factory();
+ instance->m_task = task;
+ RobotBase::setInstance(instance);
+ instance->StartCompetition();
}
/**
*
* Start the robot code.
- * This function starts the robot code running by spawning a task. Currently tasks seemed to be
- * started by LVRT without setting the VX_FP_TASK flag so floating point context is not saved on
- * interrupts. Therefore the program experiences hard to debug and unpredictable results. So the
- * LVRT code starts this function, and it, in turn, starts the actual user program.
+ * This function starts the robot code running by spawning a task. Currently
+ * tasks seem to be started by LVRT without setting the VX_FP_TASK flag which
+ * means that the floating point registers are not saved on interrupts and task
+ * switches. That causes the program to experience hard to debug and
+ * unpredictable results, so the LVRT code starts this function, so that it, in
+ * turn, can start the actual user program.
*/
void RobotBase::startRobotTask(FUNCPTR factory)
{
@@ -199,8 +205,7 @@
/**
* This class exists for the sole purpose of getting its destructor called when the module unloads.
* Before the module is done unloading, we need to delete the RobotBase derived singleton. This should delete
- * the other remaining singletons that were registered. This should also stop all tasks that are using
- * the Task class.
+ * the other remaining singletons that were registered.
*/
class RobotDeleter
{
diff --git a/aos/externals/WPILib/WPILib/RobotBase.h b/aos/externals/WPILib/WPILib/RobotBase.h
index ac15250..45c24dd 100644
--- a/aos/externals/WPILib/WPILib/RobotBase.h
+++ b/aos/externals/WPILib/WPILib/RobotBase.h
@@ -13,6 +13,11 @@
class DriverStation;
+/**
+ * This macro will set up the given class (which must be a (direct or indirect)
+ * RobotBase subclass) so that when the user code is loaded, it will be
+ * instantiated and StartCompetition() will be called on the instance.
+ */
#define START_ROBOT_CLASS(_ClassName_) \
RobotBase *FRC_userClassFactory() \
{ \
@@ -28,17 +33,14 @@
/**
* Implement a Robot Program framework.
- * The RobotBase class is intended to be subclassed by a user creating a robot program.
- * Overridden Autonomous() and OperatorControl() methods are called at the appropriate time
- * as the match proceeds. In the current implementation, the Autonomous code will run to
- * completion before the OperatorControl code could start. In the future the Autonomous code
- * might be spawned as a task, then killed at the end of the Autonomous period.
+ * The RobotBase class is intended to be subclassed by a user creating a robot
+ * program, possibly indirectly through one of the subclasses included in this
+ * library.
*/
class RobotBase {
friend class RobotDeleter;
public:
static RobotBase &getInstance();
- static void setInstance(RobotBase* robot);
bool IsEnabled();
bool IsDisabled();
@@ -49,13 +51,15 @@
bool IsNewDataAvailable();
Watchdog &GetWatchdog();
static void startRobotTask(FUNCPTR factory);
- static void robotTask(FUNCPTR factory, Task *task);
protected:
virtual ~RobotBase();
virtual void StartCompetition() = 0;
RobotBase();
+ static void setInstance(RobotBase* robot);
+ static void robotTask(FUNCPTR factory, Task *task);
+
Task *m_task;
Watchdog m_watchdog;
DriverStation *m_ds;
diff --git a/aos/externals/WPILib/WPILib/RobotDrive.cpp b/aos/externals/WPILib/WPILib/RobotDrive.cpp
index 14e28f5..1d538c5 100644
--- a/aos/externals/WPILib/WPILib/RobotDrive.cpp
+++ b/aos/externals/WPILib/WPILib/RobotDrive.cpp
@@ -725,7 +725,7 @@
void RobotDrive::GetDescription(char *desc)
{
- sprintf(desc, "RobotDrive");
+ snprintf(desc, 64, "RobotDrive");
}
void RobotDrive::StopMotor()
diff --git a/aos/externals/WPILib/WPILib/SafePWM.cpp b/aos/externals/WPILib/WPILib/SafePWM.cpp
index 484fbe6..989d782 100644
--- a/aos/externals/WPILib/WPILib/SafePWM.cpp
+++ b/aos/externals/WPILib/WPILib/SafePWM.cpp
@@ -100,7 +100,7 @@
void SafePWM::GetDescription(char *desc)
{
- sprintf(desc, "PWM %d on module %d", GetChannel(), GetModuleNumber());
+ snprintf(desc, 64, "PWM %d on module %d", GetChannel(), GetModuleNumber());
}
/**
diff --git a/aos/externals/WPILib/WPILib/SensorBase.cpp b/aos/externals/WPILib/WPILib/SensorBase.cpp
index a68d260..5574a35 100644
--- a/aos/externals/WPILib/WPILib/SensorBase.cpp
+++ b/aos/externals/WPILib/WPILib/SensorBase.cpp
@@ -7,7 +7,6 @@
#include "SensorBase.h"
#include "NetworkCommunication/LoadOut.h"
-#include "WPIErrors.h"
const UINT32 SensorBase::kSystemClockTicksPerMicrosecond;
const UINT32 SensorBase::kDigitalChannels;
@@ -18,11 +17,11 @@
const UINT32 SensorBase::kSolenoidModules;
const UINT32 SensorBase::kPwmChannels;
const UINT32 SensorBase::kRelayChannels;
-const UINT32 SensorBase::kChassisSlots;
SensorBase *SensorBase::m_singletonList = NULL;
+ReentrantSemaphore SensorBase::m_singletonListSemaphore;
/**
- * Creates an instance of the sensor base and gets an FPGA handle
+ * Creates an instance of SensorBase.
*/
SensorBase::SensorBase()
{
@@ -36,27 +35,31 @@
}
/**
- * Add sensor to the singleton list.
- * Add this sensor to the list of singletons that need to be deleted when
- * the robot program exits. Each of the sensors on this list are singletons,
- * that is they aren't allocated directly with new, but instead are allocated
- * by the static GetInstance method. As a result, they are never deleted when
- * the program exits. Consequently these sensors may still be holding onto
- * resources and need to have their destructors called at the end of the program.
+ * @brief Add sensor to the singleton list.
+ * Add this object to the list of singletons that need to be deleted when
+ * the robot program exits. Each of the objects on this list are singletons,
+ * that is they aren't allocated directly by user code, but instead are
+ * allocated by (for example) a static GetInstance method. Because of this, they
+ * need some way to be freed when the module is unloaded so that they can free
+ * any resources that they are holding on to.
+ * @see #DeleteSingletons()
*/
void SensorBase::AddToSingletonList()
{
+ Synchronized sync(m_singletonListSemaphore);
m_nextSingleton = m_singletonList;
m_singletonList = this;
}
/**
- * Delete all the singleton classes on the list.
- * All the classes that were allocated as singletons need to be deleted so
+ * @brief Delete all the singleton objects on the list.
+ * All the objects that were allocated as singletons need to be deleted so
* their resources can be freed.
+ * @see #AddToSingletonList()
*/
void SensorBase::DeleteSingletons()
{
+ Synchronized sync(m_singletonListSemaphore);
for (SensorBase *next = m_singletonList; next != NULL;)
{
SensorBase *tmp = next;
diff --git a/aos/externals/WPILib/WPILib/SensorBase.h b/aos/externals/WPILib/WPILib/SensorBase.h
index 1b149b1..3f12360 100644
--- a/aos/externals/WPILib/WPILib/SensorBase.h
+++ b/aos/externals/WPILib/WPILib/SensorBase.h
@@ -7,20 +7,19 @@
#ifndef SENSORBASE_H_
#define SENSORBASE_H_
-#include "ChipObject/NiRio.h"
#include "ErrorBase.h"
#include <stdio.h>
#include "Base.h"
+#include "Synchronized.h"
/**
* Base class for all sensors.
* Stores most recent status information as well as containing utility functions for checking
- * channels and error processing.
+ * channels.
*/
class SensorBase: public ErrorBase {
public:
SensorBase();
- virtual ~SensorBase();
static void DeleteSingletons();
static UINT32 GetDefaultAnalogModule() { return 1; }
static UINT32 GetDefaultDigitalModule() { return 1; }
@@ -36,6 +35,9 @@
static bool CheckAnalogChannel(UINT32 channel);
static bool CheckSolenoidChannel(UINT32 channel);
+ // NOT vxworks system clock ticks (returned by sysClkRateGet() from sysLib).
+ // TODO: Document what this actually is (has something to do with FPGA times).
+ // 40kHz clock?
static const UINT32 kSystemClockTicksPerMicrosecond = 40;
static const UINT32 kDigitalChannels = 14;
static const UINT32 kAnalogChannels = 8;
@@ -45,14 +47,19 @@
static const UINT32 kSolenoidModules = 2;
static const UINT32 kPwmChannels = 10;
static const UINT32 kRelayChannels = 8;
- static const UINT32 kChassisSlots = 8;
+
protected:
void AddToSingletonList();
+ // Subclasses that don't use the singleton list mechanism should make this
+ // public, but ones that do should keep it protected so that users can not
+ // delete the singleton instance(s).
+ virtual ~SensorBase();
private:
DISALLOW_COPY_AND_ASSIGN(SensorBase);
static SensorBase *m_singletonList;
SensorBase *m_nextSingleton;
+ static ReentrantSemaphore m_singletonListSemaphore;
};
diff --git a/aos/externals/WPILib/WPILib/SimpleRobot.h b/aos/externals/WPILib/WPILib/SimpleRobot.h
index d963805..88143a6 100644
--- a/aos/externals/WPILib/WPILib/SimpleRobot.h
+++ b/aos/externals/WPILib/WPILib/SimpleRobot.h
@@ -11,6 +11,10 @@
/**
* @todo If this is going to last until release, it needs a better name.
+ * Overridden Autonomous() and OperatorControl() methods are called at the appropriate time
+ * as the match proceeds. In the current implementation, the Autonomous code will run to
+ * completion before the OperatorControl code could start. In the future the Autonomous code
+ * might be spawned as a task, then killed at the end of the Autonomous period.
*/
class SimpleRobot: public RobotBase
{
diff --git a/aos/externals/WPILib/WPILib/Solenoid.cpp b/aos/externals/WPILib/WPILib/Solenoid.cpp
index 35813af..7a56a09 100644
--- a/aos/externals/WPILib/WPILib/Solenoid.cpp
+++ b/aos/externals/WPILib/WPILib/Solenoid.cpp
@@ -30,9 +30,10 @@
Resource::CreateResourceObject(&m_allocated, tSolenoid::kNumDO7_0Elements * kSolenoidChannels);
snprintf(buf, 64, "Solenoid %d (Module: %d)", m_channel, m_moduleNumber);
- if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels + m_channel - 1, buf) == ~0ul)
+ if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels
+ + m_channel - 1,
+ buf, this) == ~0ul)
{
- CloneError(m_allocated);
return;
}
@@ -72,7 +73,8 @@
{
if (CheckSolenoidModule(m_moduleNumber))
{
- m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_channel - 1);
+ m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_channel - 1,
+ this);
}
}
diff --git a/aos/externals/WPILib/WPILib/SolenoidBase.cpp b/aos/externals/WPILib/WPILib/SolenoidBase.cpp
index cc1a2c4..1f38869 100644
--- a/aos/externals/WPILib/WPILib/SolenoidBase.cpp
+++ b/aos/externals/WPILib/WPILib/SolenoidBase.cpp
@@ -50,7 +50,7 @@
}
/**
- * Set the value of a solenoid.
+ * Set the value of 1 or more solenoids at the same time.
*
* @param value The value you want to set on the module.
* @param mask The channels you want to be affected.
@@ -63,8 +63,9 @@
Synchronized sync(m_semaphore);
UINT8 currentValue = m_fpgaSolenoidModule->readDO7_0(m_moduleNumber - 1, &localStatus);
// Zero out the values to change
- currentValue = currentValue & ~mask;
- currentValue = currentValue | (value & mask);
+ currentValue &= ~mask;
+ // Actually set the values.
+ currentValue |= value & mask;
m_fpgaSolenoidModule->writeDO7_0(m_moduleNumber - 1, currentValue, &localStatus);
}
wpi_setError(localStatus);
diff --git a/aos/externals/WPILib/WPILib/SolenoidBase.h b/aos/externals/WPILib/WPILib/SolenoidBase.h
index a7a67a6..a9fdf61 100644
--- a/aos/externals/WPILib/WPILib/SolenoidBase.h
+++ b/aos/externals/WPILib/WPILib/SolenoidBase.h
@@ -15,17 +15,25 @@
/**
* SolenoidBase class is the common base class for the Solenoid and
* DoubleSolenoid classes.
+ * It also supports getting and setting the values of all solenoids on a given
+ * module at the same time directly.
*/
class SolenoidBase : public SensorBase {
public:
+ explicit SolenoidBase(UINT8 moduleNumber);
virtual ~SolenoidBase();
+
+ void Set(UINT8 value, UINT8 mask);
+
UINT8 GetAll();
+ /**
+ * Set the value of all of the solenoids at the same time.
+ *
+ * @param value The values you want to set all of the solenoids to.
+ */
+ void SetAll(UINT8 value) { Set(value, 0xFF); }
protected:
- explicit SolenoidBase(UINT8 moduleNumber);
- void Set(UINT8 value, UINT8 mask);
- virtual void InitSolenoid() = 0;
-
UINT32 m_moduleNumber; ///< Slot number where the module is plugged into the chassis.
static Resource *m_allocated;
diff --git a/aos/externals/WPILib/WPILib/Synchronized.cpp b/aos/externals/WPILib/WPILib/Synchronized.cpp
index eb17318..fd133b5 100644
--- a/aos/externals/WPILib/WPILib/Synchronized.cpp
+++ b/aos/externals/WPILib/WPILib/Synchronized.cpp
@@ -20,7 +20,7 @@
semTake(m_semaphore, WAIT_FOREVER);
}
-Synchronized::Synchronized(ReentrantSemaphore& semaphore)
+Synchronized::Synchronized(const ReentrantSemaphore& semaphore)
{
m_semaphore = semaphore.m_semaphore;
semTake(m_semaphore, WAIT_FOREVER);
diff --git a/aos/externals/WPILib/WPILib/Synchronized.h b/aos/externals/WPILib/WPILib/Synchronized.h
index 28cfbc3..4e44b0e 100644
--- a/aos/externals/WPILib/WPILib/Synchronized.h
+++ b/aos/externals/WPILib/WPILib/Synchronized.h
@@ -27,11 +27,15 @@
*
* This class is safe to use in static variables because it does not depend on
* any other C++ static constructors or destructors.
+ *
+ * The instance methods are marked const because using this class from multiple
+ * threads at once is safe and they don't actually modify the value of any of
+ * the instance variables.
*/
class ReentrantSemaphore
{
public:
- explicit ReentrantSemaphore() {
+ ReentrantSemaphore() {
m_semaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE);
}
~ReentrantSemaphore() {
@@ -42,7 +46,7 @@
* Lock the semaphore, blocking until it's available.
* @return 0 for success, -1 for error. If -1, the error will be in errno.
*/
- int take() {
+ int take() const {
return semTake(m_semaphore, WAIT_FOREVER);
}
@@ -50,7 +54,7 @@
* Unlock the semaphore.
* @return 0 for success, -1 for error. If -1, the error will be in errno.
*/
- int give() {
+ int give() const {
return semGive(m_semaphore);
}
@@ -83,8 +87,8 @@
{
public:
explicit Synchronized(SEM_ID);
- explicit Synchronized(ReentrantSemaphore&);
- virtual ~Synchronized();
+ explicit Synchronized(const ReentrantSemaphore&);
+ ~Synchronized();
private:
SEM_ID m_semaphore;
diff --git a/aos/externals/WPILib/WPILib/Task.cpp b/aos/externals/WPILib/WPILib/Task.cpp
index b81b16e..37f7d2f 100644
--- a/aos/externals/WPILib/WPILib/Task.cpp
+++ b/aos/externals/WPILib/WPILib/Task.cpp
@@ -18,6 +18,7 @@
/**
* Create but don't launch a task.
+ * Does not use any floating point registers.
* @param name The name of the task. "FRC_" will be prepended to the task name.
* @param function The address of the function to run as the new task.
* @param priority The VxWorks priority for the task.
@@ -38,6 +39,9 @@
nUsageReporting::report(nUsageReporting::kResourceType_Task, instances, 0, m_taskName);
}
+/**
+ * Does not use any floating point registers.
+ */
Task::~Task()
{
if (m_taskID != kInvalidTaskID) Stop();
@@ -47,18 +51,20 @@
/**
* Starts this task.
- * If it is already running or unable to start, it fails and returns false.
+ * Does not use any floating point registers.
+ * @return true on success
*/
bool Task::Start(UINT32 arg0, UINT32 arg1, UINT32 arg2, UINT32 arg3, UINT32 arg4,
UINT32 arg5, UINT32 arg6, UINT32 arg7, UINT32 arg8, UINT32 arg9)
{
+ // Don't have to lock m_prioritySemaphore because this code isn't changing it.
m_taskID = taskSpawn(m_taskName,
m_priority,
VX_FP_TASK, // options
m_stackSize, // stack size
m_function, // function to start
- arg0, arg1, arg2, arg3, arg4, // parameter 1 - pointer to this class
- arg5, arg6, arg7, arg8, arg9);// additional unused parameters
+ arg0, arg1, arg2, arg3, arg4, // parameters to pass to m_function
+ arg5, arg6, arg7, arg8, arg9);// additional parameters
bool ok = HandleError(m_taskID);
if (!ok) m_taskID = kInvalidTaskID;
return ok;
@@ -66,8 +72,7 @@
/**
* Restarts a running task.
- * If the task isn't started, it starts it.
- * @return false if the task is running and we are unable to kill the previous instance
+ * @return true on success
*/
bool Task::Restart()
{
@@ -75,8 +80,8 @@
}
/**
- * Kills the running task.
- * @returns true on success false if the task doesn't exist or we are unable to kill it.
+ * Makes sure that the task is not running.
+ * @returns true on success
*/
bool Task::Stop()
{
@@ -114,7 +119,7 @@
/**
* Pauses a running task.
- * Returns true on success, false if unable to pause or the task isn't running.
+ * Returns true on success
*/
bool Task::Suspend()
{
@@ -123,7 +128,7 @@
/**
* Resumes a paused task.
- * Returns true on success, false if unable to resume or if the task isn't running/paused.
+ * Returns true on success
*/
bool Task::Resume()
{
@@ -146,6 +151,7 @@
*/
INT32 Task::GetPriority()
{
+ Synchronized sync(m_prioritySemaphore);
if (HandleError(taskPriorityGet(m_taskID, &m_priority)))
return m_priority;
else
@@ -161,6 +167,7 @@
*/
bool Task::SetPriority(INT32 priority)
{
+ Synchronized sync(m_prioritySemaphore);
m_priority = priority;
return HandleError(taskPrioritySet(m_taskID, m_priority));
}
diff --git a/aos/externals/WPILib/WPILib/Task.h b/aos/externals/WPILib/WPILib/Task.h
index 03c9555..d1ad259 100644
--- a/aos/externals/WPILib/WPILib/Task.h
+++ b/aos/externals/WPILib/WPILib/Task.h
@@ -9,10 +9,14 @@
#include "ErrorBase.h"
#include <vxWorks.h>
+#include "Synchronized.h"
/**
- * WPI task is a wrapper for the native Task object.
- * All WPILib tasks are managed by a static task manager for simplified cleanup.
+ * WPI task is a wrapper for a native VxWorks task.
+ *
+ * Some functions (documented) are guaranteed not to use any floating point so
+ * that it is safe to use them from tasks that do not have the VX_FP_TASK flag
+ * set (like during startup).
**/
class Task : public ErrorBase
{
@@ -47,6 +51,7 @@
INT32 m_taskID;
UINT32 m_stackSize;
INT32 m_priority;
+ ReentrantSemaphore m_prioritySemaphore;
bool HandleError(STATUS results);
DISALLOW_COPY_AND_ASSIGN(Task);
};
diff --git a/aos/externals/WPILib/WPILib/Timer.cpp b/aos/externals/WPILib/WPILib/Timer.cpp
index e9fcde7..7638b59 100644
--- a/aos/externals/WPILib/WPILib/Timer.cpp
+++ b/aos/externals/WPILib/WPILib/Timer.cpp
@@ -12,6 +12,7 @@
#include "Synchronized.h"
#include "Utility.h"
+#include "Global.h"
/**
* Pause the task for a specified time.
@@ -180,7 +181,7 @@
{
// FPGA returns the timestamp in microseconds
// Call the helper GetFPGATime() in Utility.cpp
- return GetFPGATime() * 1.0e-6;
+ return Global::GetInstance()->GetFPGATime() * 1.0e-6;
}
// Internal function that reads the PPC timestamp counter.
diff --git a/aos/externals/WPILib/WPILib/Utility.cpp b/aos/externals/WPILib/WPILib/Utility.cpp
index 5ce84f9..680da97 100644
--- a/aos/externals/WPILib/WPILib/Utility.cpp
+++ b/aos/externals/WPILib/WPILib/Utility.cpp
@@ -7,34 +7,34 @@
#include "Utility.h"
#include "NetworkCommunication/FRCComm.h"
-#include "ChipObject.h"
#include "Task.h"
#include <dbgLib.h>
#include <stdio.h>
+#include <taskLib.h>
#include <sysSymTbl.h>
#include "nivision.h"
-#define DBG_DEMANGLE_PRINT_LEN 256 /* Num chars of demangled names to print */
+#define DBG_DEMANGLE_PRINT_LEN MAX_SYS_SYM_LEN /* Num chars of demangled names to print */
extern "C"
{
extern char * cplusDemangle (char *source, char *dest, INT32 n);
}
-char *wpi_getLabel(UINT addr, INT32 *found)
+void wpi_getLabel(UINT addr, char *label, INT32 *found)
{
INT32 pVal;
SYM_TYPE pType;
char name[MAX_SYS_SYM_LEN + 1];
- static char label[DBG_DEMANGLE_PRINT_LEN + 1 + 11];
- bzero(label, DBG_DEMANGLE_PRINT_LEN + 1 + 11);
+ static const size_t kLabelSize = DBG_DEMANGLE_PRINT_LEN + 1 + 11;
+ bzero(label, kLabelSize);
if (symFindByValue(sysSymTbl, addr, name, &pVal, &pType) == OK)
{
- cplusDemangle(name, label, sizeof(label) - 11);
+ cplusDemangle(name, label, kLabelSize - 11);
if ((UINT)pVal != addr)
{
- sprintf(&label[strlen(label)], "+0x%04x", addr-pVal);
+ snprintf(label + strlen(label), kLabelSize - strlen(label), "+0x%04x", addr-pVal);
if (found) *found = 2;
}
else
@@ -44,11 +44,9 @@
}
else
{
- sprintf(label, "0x%04x", addr);
+ snprintf(label, kLabelSize, "0x%04x", addr);
if (found) *found = 0;
}
-
- return label;
}
/*
static void wpiTracePrint(INSTR *caller, INT32 func, INT32 nargs, INT32 *args, INT32 taskId, BOOL isKernelAdrs)
@@ -56,15 +54,16 @@
char buf [MAX_SYS_SYM_LEN * 2];
INT32 ix;
INT32 len = 0;
- len += sprintf (&buf [len], "%s <%#010x>: ", wpi_getLabel((UINT)caller), (INT32)caller);
- len += sprintf (&buf [len], "%s <%#010x> (", wpi_getLabel((UINT)func), func);
+ len += snprintf (&buf [len], sizeof(buf) - len, "%s <%#010x>: ", wpi_getLabel((UINT)caller), (INT32)caller);
+ len += snprintf (&buf [len], sizeof(buf) - len, "%s <%#010x> (", wpi_getLabel((UINT)func), func);
for (ix = 0; ix < nargs; ix++)
{
- if (ix != 0)
- len += sprintf (&buf [len], ", ");
- len += sprintf (&buf [len], "%#x", args [ix]);
+ if (ix != 0) {
+ len += snprintf (&buf [len], sizeof(buf) - len, ", ");
+ }
+ len += snprintf (&buf [len], sizeof(buf) - len, "%#x", args [ix]);
}
- len += sprintf (&buf [len], ")\n");
+ len += snprintf (&buf [len], sizeof(buf) - len, ")\n");
printf(buf);
}
@@ -77,7 +76,8 @@
INT32 nameFound = 0;
INT32 params = 0;
INT32 totalnargs = nargs;
- char *funcName = wpi_getLabel((UINT)func, &nameFound);
+ char funcName[DBG_DEMANGLE_PRINT_LEN + 1 + 11];
+ wpi_getLabel((UINT)func, funcName, &nameFound);
// Ignore names that are not exact symbol address matches.
if (nameFound != 1) return;
@@ -107,23 +107,24 @@
}
char *funcNameEnd = strchr(funcName, '(');
*funcNameEnd = 0;
- len += sprintf (&buf [len], funcName);
+ len += snprintf (buf + len, sizeof(buf) - len, funcName);
// If this is a member function, print out the this pointer value.
if (totalnargs - params == 1)
{
- len += sprintf (&buf [len], "<this=%#x>", args [0]);
+ len += snprintf (buf + len, sizeof(buf) - len, "<this=%#x>", args [0]);
}
// Print out the argument values.
- len += sprintf (&buf [len], "(");
+ len += snprintf (buf + len, sizeof(buf) - len, "(");
for (ix = totalnargs - params; ix < nargs; ix++)
{
- if (ix != totalnargs - params)
- len += sprintf (&buf [len], ", ");
- len += sprintf (&buf [len], "%#x", args [ix]);
+ if (ix != totalnargs - params) {
+ len += snprintf (buf + len, sizeof(buf) - len, ", ");
+ }
+ len += snprintf (buf + len, sizeof(buf) - len, "%#x", args [ix]);
}
- len += sprintf (&buf [len], ")\n");
+ len += snprintf (buf + len, sizeof(buf) - len, ")\n");
printf(buf);
}
@@ -135,7 +136,11 @@
static INT32 wpiStackTask(INT32 taskId)
{
- taskDelay(1);
+ // Make sure it's suspended in spite of any scheduler weirdness or whatever.
+ while (!taskIsSuspended(taskId)) {
+ taskDelay(1);
+ }
+
//tt(taskId);
REG_SET regs;
@@ -212,10 +217,10 @@
// If an error message was specified, include it
// Build error string
if(message != NULL) {
- sprintf(error, "Assertion failed: \"%s\", \"%s\" failed in %s() in %s at line %d\n",
+ snprintf(error, sizeof(error), "Assertion failed: \"%s\", \"%s\" failed in %s() in %s at line %d\n",
message, conditionText, funcName, fileName, lineNumber);
} else {
- sprintf(error, "Assertion failed: \"%s\" in %s() in %s at line %d\n",
+ snprintf(error, sizeof(error), "Assertion failed: \"%s\" in %s() in %s at line %d\n",
conditionText, funcName, fileName, lineNumber);
}
@@ -248,10 +253,10 @@
// If an error message was specified, include it
// Build error string
if(message != NULL) {
- sprintf(error, "Assertion failed: \"%s\", \"%d\" %s \"%d\" in %s() in %s at line %d\n",
+ snprintf(error, sizeof(error), "Assertion failed: \"%s\", \"%d\" %s \"%d\" in %s() in %s at line %d\n",
message, valueA, equalityType, valueB, funcName, fileName, lineNumber);
} else {
- sprintf(error, "Assertion failed: \"%d\" %s \"%d\" in %s() in %s at line %d\n",
+ snprintf(error, sizeof(error), "Assertion failed: \"%d\" %s \"%d\" in %s() in %s at line %d\n",
valueA, equalityType, valueB, funcName, fileName, lineNumber);
}
@@ -302,137 +307,3 @@
}
return valueA != valueB;
}
-
-
-/**
- * Return the FPGA Version number.
- * For now, expect this to be competition year.
- * @return FPGA Version number.
- */
-UINT16 GetFPGAVersion()
-{
- tRioStatusCode status = 0;
- tGlobal *global = tGlobal::create(&status);
- UINT16 version = global->readVersion(&status);
- delete global;
- wpi_setGlobalError(status);
- return version;
-}
-
-/**
- * Return the FPGA Revision number.
- * The format of the revision is 3 numbers.
- * The 12 most significant bits are the Major Revision.
- * the next 8 bits are the Minor Revision.
- * The 12 least significant bits are the Build Number.
- * @return FPGA Revision number.
- */
-UINT32 GetFPGARevision()
-{
- tRioStatusCode status = 0;
- tGlobal *global = tGlobal::create(&status);
- UINT32 revision = global->readRevision(&status);
- delete global;
- wpi_setGlobalError(status);
- return revision;
-}
-
-/**
- * Read the microsecond-resolution timer on the FPGA.
- *
- * @return The current time in microseconds according to the FPGA (since FPGA reset).
- */
-UINT32 GetFPGATime()
-{
- tRioStatusCode status = 0;
- tGlobal *global = tGlobal::create(&status);
- UINT32 time = global->readLocalTime(&status);
- delete global;
- wpi_setGlobalError(status);
- return time;
-}
-
-// RT hardware access functions exported from ni_emb.out
-extern "C"
-{
- INT32 UserSwitchInput(INT32 nSwitch);
- INT32 LedInput(INT32 led);
- INT32 LedOutput(INT32 led, INT32 value);
-}
-
-/**
- * Read the value of the USER1 DIP switch on the cRIO.
- */
-INT32 GetRIOUserSwitch()
-{
- INT32 switchValue = UserSwitchInput(0);
- wpi_assert(switchValue >= 0);
- return switchValue > 0;
-}
-
-/**
- * Set the state of the USER1 status LED on the cRIO.
- */
-void SetRIOUserLED(UINT32 state)
-{
- LedOutput(0, state > 0);
-}
-
-/**
- * Get the current state of the USER1 status LED on the cRIO.
- * @return The curent state of the USER1 LED.
- */
-INT32 GetRIOUserLED()
-{
- return LedInput(0);
-}
-
-/**
- * Toggle the state of the USER1 status LED on the cRIO.
- * @return The new state of the USER1 LED.
- */
-INT32 ToggleRIOUserLED()
-{
- INT32 ledState = !GetRIOUserLED();
- SetRIOUserLED(ledState);
- return ledState;
-}
-
-/**
- * Set the state of the FPGA status LED on the cRIO.
- */
-void SetRIO_FPGA_LED(UINT32 state)
-{
- tRioStatusCode status = 0;
- tGlobal *global = tGlobal::create(&status);
- global->writeFPGA_LED(state, &status);
- wpi_setGlobalError(status);
- delete global;
-}
-
-/**
- * Get the current state of the FPGA status LED on the cRIO.
- * @return The curent state of the FPGA LED.
- */
-INT32 GetRIO_FPGA_LED()
-{
- tRioStatusCode status = 0;
- tGlobal *global = tGlobal::create(&status);
- bool ledValue = global->readFPGA_LED(&status);
- wpi_setGlobalError(status);
- delete global;
- return ledValue;
-}
-
-/**
- * Toggle the state of the FPGA status LED on the cRIO.
- * @return The new state of the FPGA LED.
- */
-INT32 ToggleRIO_FPGA_LED()
-{
- INT32 ledState = !GetRIO_FPGA_LED();
- SetRIO_FPGA_LED(ledState);
- return ledState;
-}
-
-
diff --git a/aos/externals/WPILib/WPILib/Utility.h b/aos/externals/WPILib/WPILib/Utility.h
index 01bcbff..4462b7c 100644
--- a/aos/externals/WPILib/WPILib/Utility.h
+++ b/aos/externals/WPILib/WPILib/Utility.h
@@ -22,20 +22,10 @@
bool wpi_assertEqual_impl(int valueA, int valueB, const char *message, const char *fileName,UINT32 lineNumber, const char *funcName);
bool wpi_assertNotEqual_impl(int valueA, int valueB, const char *message, const char *fileName,UINT32 lineNumber, const char *funcName);
-char *wpi_getLabel(UINT addr, INT32 *found = NULL);
+// Will use up to (DBG_DEMANGLE_PRINT_LEN + 1 + 11) of label.
+void wpi_getLabel(UINT addr, char *label, INT32 *found = NULL);
void wpi_selfTrace();
void wpi_suspendOnAssertEnabled(bool enabled);
void wpi_stackTraceOnAssertEnable(bool enabled);
-UINT16 GetFPGAVersion();
-UINT32 GetFPGARevision();
-UINT32 GetFPGATime();
-INT32 GetRIOUserSwitch();
-void SetRIOUserLED(UINT32 state);
-INT32 GetRIOUserLED();
-INT32 ToggleRIOUserLED();
-void SetRIO_FPGA_LED(UINT32 state);
-INT32 GetRIO_FPGA_LED();
-INT32 ToggleRIO_FPGA_LED();
-
#endif // UTILITY_H_
diff --git a/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp b/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp
index 35d3c41..5f9fa3e 100644
--- a/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp
+++ b/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp
@@ -12,7 +12,7 @@
#include <errno.h>
#include <stdlib.h>
#ifdef _WRS_KERNEL
-#include <iolib.h>
+#include <ioLib.h>
#else
#include <unistd.h>
#endif
diff --git a/doc/wpilib-check-notes.txt b/doc/wpilib-check-notes.txt
new file mode 100644
index 0000000..e8c1a0f
--- /dev/null
+++ b/doc/wpilib-check-notes.txt
@@ -0,0 +1,50 @@
+This file has the results of going through all of the WPILib code that we use.
+If you use any more parts of WPILib, then check them carefully (preferrably get
+multiple people to do it) and then add the results here.
+The notes are so that it is clear what has been checked and how things interact
+in nonintiutive ways to potentially create subtle bugs.
+
+DriverStationEnhancedIO
+ only checked what DriverStation does to it
+ the rest of it is implemented horribly
+DriverStation
+ GetMatchTime() is garbage (DriverStation shouldn't keep track of that
+ information and it does a bad job of it)
+ don't call Set*PriorityDashboardPackerToUse
+ IsNewControlData() and WaitForData() are OK
+ make sure to GetDataReadLock() correctly when you want to read data
+ GetStickAxis uses brain-dead math
+ most of the "helper" methods to retrieve parts of the control data have no
+ benefit and do other weird things besides just get the value
+Dashboard
+ GetStatusBuffer and Flush do get called from a separate task by DriverStation
+ only checked what DriverStation does to it
+MotorSafetyHelper
+ CheckMotors() does get called from a separate task by DriverStation
+RobotBase
+ the Is* methods are garbage (call them directly on the instances of the
+ objects that they forward too)
+Task
+ the constructor and Start get called in RobotBase in a task without the
+ floating point save flag set
+ReentrantSemaphore
+Synchronized
+Error
+ it synchronizes all of the non-const methods internally, and does it right
+ Do not use EnableStackTrace.
+ErrorBase
+ the mutable Error instance varible is weird, but safe
+Utility
+ it is a bad idea to use wpi_selfTrace()
+ That gets called if you use Error::EnableStackTrace or
+ wpi_stackOnAssertEnable, so don't call those.
+ The assertions NEVER stop on failure unless you wpi_SuspendOnAssertEnabled.
+Global
+Watchdog
+ the return value of Feed() is garbage
+Module
+DigitalModule
+ didn't look at I2C
+AnalogChannel
+AnalogModule
+NetworkRobot
diff --git a/doc/wpilib-issues.txt b/doc/wpilib-issues.txt
new file mode 100644
index 0000000..1b46940
--- /dev/null
+++ b/doc/wpilib-issues.txt
@@ -0,0 +1,6 @@
+sprintf
+ErrorBase/Error not thread safe (aka CANJaguar)
+all of the sync issues last year
+Module not thread safe
+
+compressor turned on as soon as power was turned on (2 times in a row)
diff --git a/frc971/crio/crio.gyp b/frc971/crio/crio.gyp
index 9542e0b..9000f68 100644
--- a/frc971/crio/crio.gyp
+++ b/frc971/crio/crio.gyp
@@ -28,17 +28,35 @@
],
},
{
+ 'target_name': 'dumb_main',
+ 'type': 'static_library',
+ 'sources': [
+ 'dumb_main.cc',
+ ],
+ 'dependencies': [
+ '<(EXTERNALS):WPILib',
+ '<(AOS)/common/common.gyp:common',
+ ],
+ },
+ {
'target_name': 'FRC_UserProgram',
'type': 'shared_library',
'dependencies': [
- 'user_program'
+ '<(EXTERNALS):libgcc-4.5.2',
+ 'dumb_main',
+ ],
+ },
+ {
+ 'target_name': 'FRC_UserProgram_old',
+ 'type': 'shared_library',
+ 'dependencies': [
+ 'user_program',
],
},
{
'target_name': 'FRC_UserProgram_WithTests',
'type': 'shared_library',
'dependencies': [
- # For testing.
'<(AOS)/build/aos_all.gyp:Crio',
],
},
diff --git a/frc971/crio/dumb_main.cc b/frc971/crio/dumb_main.cc
new file mode 100644
index 0000000..4accd9c
--- /dev/null
+++ b/frc971/crio/dumb_main.cc
@@ -0,0 +1,19 @@
+#include "WPILib/NetworkRobot/NetworkRobot.h"
+#include "WPILib/RobotBase.h"
+#include "aos/common/Configuration.h"
+
+namespace frc971 {
+
+class MyRobot : public NetworkRobot {
+ public:
+ MyRobot() : NetworkRobot(static_cast<uint16_t>(::aos::NetworkPort::kMotors),
+ ::aos::configuration::GetIPAddress(
+ ::aos::configuration::NetworkDevice::kAtom),
+ static_cast<uint16_t>(::aos::NetworkPort::kDS),
+ ::aos::configuration::GetIPAddress(
+ ::aos::configuration::NetworkDevice::kAtom)) {}
+};
+
+} // namespace frc971
+
+START_ROBOT_CLASS(::frc971::MyRobot);
diff --git a/frc971/output/AtomMotorWriter.cc b/frc971/output/AtomMotorWriter.cc
index 8431fa7..3ba3593 100644
--- a/frc971/output/AtomMotorWriter.cc
+++ b/frc971/output/AtomMotorWriter.cc
@@ -2,14 +2,12 @@
#include <string.h>
#include <unistd.h>
-#include "aos/aos_core.h"
-#include "aos/common/control_loop/Timing.h"
-#include "aos/common/messages/RobotState.q.h"
-#include "aos/atom_code/output/MotorOutput.h"
+#include "aos/atom_code/output/motor_output.h"
+#include "aos/common/logging/logging.h"
+#include "aos/atom_code/init.h"
#include "frc971/queues/Piston.q.h"
#include "frc971/control_loops/drivetrain/drivetrain.q.h"
-#include "frc971/constants.h"
#include "frc971/control_loops/wrist/wrist_motor.q.h"
#include "frc971/control_loops/shooter/shooter_motor.q.h"
#include "frc971/control_loops/index/index_motor.q.h"
@@ -26,77 +24,84 @@
namespace frc971 {
namespace output {
-class MotorWriter : public aos::MotorOutput {
+class MotorWriter : public ::aos::MotorOutput {
// Maximum age of an output packet before the motors get zeroed instead.
static const int kOutputMaxAgeMS = 20;
static const int kEnableDrivetrain = true;
- void RunIteration() {
+ virtual void RunIteration() {
+ values_.digital_module = 0;
+ values_.pressure_switch_channel = 14;
+ values_.compressor_channel = 1;
+ values_.solenoid_module = 0;
+
drivetrain.output.FetchLatest();
if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS) && kEnableDrivetrain) {
- AddMotor(TALON, 2, drivetrain.output->right_voltage / 12.0);
- AddMotor(TALON, 3, drivetrain.output->right_voltage / 12.0);
- AddMotor(TALON, 5, -drivetrain.output->left_voltage / 12.0);
- AddMotor(TALON, 6, -drivetrain.output->left_voltage / 12.0);
+ SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
} else {
- AddMotor(TALON, 2, 0);
- AddMotor(TALON, 3, 0);
- AddMotor(TALON, 5, 0);
- AddMotor(TALON, 6, 0);
+ DisablePWMOutput(2);
+ DisablePWMOutput(3);
+ DisablePWMOutput(5);
+ DisablePWMOutput(6);
if (kEnableDrivetrain) {
LOG(WARNING, "drivetrain not new enough\n");
}
}
shifters.FetchLatest();
- if (shifters.IsNewerThanMS(kOutputMaxAgeMS)) {
- AddSolenoid(1, shifters->set);
- AddSolenoid(2, !shifters->set);
+ if (shifters.get()) {
+ SetSolenoid(1, shifters->set);
+ SetSolenoid(2, !shifters->set);
}
wrist.output.FetchLatest();
if (wrist.output.IsNewerThanMS(kOutputMaxAgeMS)) {
- AddMotor(TALON, 10, wrist.output->voltage / 12.0);
+ SetPWMOutput(10, wrist.output->voltage / 12.0, kTalonBounds);
} else {
- AddMotor(TALON, 10, 0);
+ DisablePWMOutput(10);
LOG(WARNING, "wrist not new enough\n");
}
shooter.output.FetchLatest();
if (shooter.output.IsNewerThanMS(kOutputMaxAgeMS)) {
- AddMotor(TALON, 4, shooter.output->voltage / 12.0);
+ SetPWMOutput(4, shooter.output->voltage / 12.0, kTalonBounds);
} else {
- AddMotor(TALON, 4, 0);
+ DisablePWMOutput(4);
LOG(WARNING, "shooter not new enough\n");
}
angle_adjust.output.FetchLatest();
if (angle_adjust.output.IsNewerThanMS(kOutputMaxAgeMS)) {
- AddMotor(TALON, 1, -angle_adjust.output->voltage / 12.0);
+ SetPWMOutput(1, -angle_adjust.output->voltage / 12.0, kTalonBounds);
} else {
- AddMotor(TALON, 1, 0);
+ DisablePWMOutput(1);
LOG(WARNING, "angle adjust is not new enough\n");
}
index_loop.output.FetchLatest();
+ if (index_loop.output.get()) {
+ SetSolenoid(7, index_loop.output->loader_up);
+ SetSolenoid(8, !index_loop.output->loader_up);
+ SetSolenoid(6, index_loop.output->disc_clamped);
+ SetSolenoid(3, index_loop.output->disc_ejected);
+ }
if (index_loop.output.IsNewerThanMS(kOutputMaxAgeMS)) {
- AddMotor(TALON, 8, index_loop.output->intake_voltage / 12.0);
- AddMotor(TALON, 9, index_loop.output->transfer_voltage / 12.0);
- AddMotor(TALON, 7, -index_loop.output->index_voltage / 12.0);
- AddSolenoid(7, index_loop.output->loader_up);
- AddSolenoid(8, !index_loop.output->loader_up);
- AddSolenoid(6, index_loop.output->disc_clamped);
- AddSolenoid(3, index_loop.output->disc_ejected);
+ SetPWMOutput(8, index_loop.output->intake_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(9, index_loop.output->transfer_voltage / 12.0, kTalonBounds);
+ SetPWMOutput(7, -index_loop.output->index_voltage / 12.0, kTalonBounds);
} else {
- AddMotor(TALON, 8, 0);
- AddMotor(TALON, 9, 0);
- AddMotor(TALON, 7, 0);
+ DisablePWMOutput(8);
+ DisablePWMOutput(9);
+ DisablePWMOutput(7);
LOG(WARNING, "index not new enough\n");
}
hangers.FetchLatest();
- if (hangers.IsNewerThanMS(kOutputMaxAgeMS)) {
- AddSolenoid(4, hangers->set);
- AddSolenoid(5, hangers->set);
+ if (hangers.get()) {
+ SetSolenoid(4, hangers->set);
+ SetSolenoid(5, hangers->set);
}
}
};
@@ -104,4 +109,9 @@
} // namespace output
} // namespace frc971
-AOS_RUN(frc971::output::MotorWriter)
+int main() {
+ ::aos::Init();
+ ::frc971::output::MotorWriter writer;
+ writer.Run();
+ ::aos::Cleanup();
+}
diff --git a/frc971/output/output.gyp b/frc971/output/output.gyp
index 4459984..b872713 100644
--- a/frc971/output/output.gyp
+++ b/frc971/output/output.gyp
@@ -29,8 +29,8 @@
'sources': ['AtomMotorWriter.cc'],
'dependencies': [
'<(AOS)/atom_code/output/output.gyp:motor_output',
- '<(AOS)/atom_code/messages/messages.gyp:messages',
'<(AOS)/atom_code/atom_code.gyp:init',
+ '<(AOS)/build/aos.gyp:logging',
'<(DEPTH)/frc971/control_loops/angle_adjust/angle_adjust.gyp:angle_adjust_loop',
'<(DEPTH)/frc971/control_loops/wrist/wrist.gyp:wrist_loop',
'<(DEPTH)/frc971/control_loops/index/index.gyp:index_loop',