Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added C++ Example with ported CheesyVisionServer #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions examples/CppCheesyVisionSample/CheesyVisionRobot/.cproject
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?>

<cproject>
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="org.eclipse.cdt.core.default.config.283967106">
<storageModule buildSystemId="org.eclipse.cdt.core.defaultConfigDataProvider" id="org.eclipse.cdt.core.default.config.283967106" moduleId="org.eclipse.cdt.core.settings" name="Configuration">
<externalSettings/>
<extensions>
<extension id="com.windriver.ide.core.WRScannerInfoProvider" point="org.eclipse.cdt.core.ScannerInfoProvider"/>
<extension id="com.windriver.ide.core.WRElfParserVxWorks" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
</extensions>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="org.eclipse.cdt.core.pathentry">
<pathentry kind="src" path=""/>
<pathentry kind="out" path=""/>
<pathentry kind="con" path="com.windriver.ide.core.WR_CONTAINER"/>
<pathentry kind="con" path="com.windriver.ide.core.build.model.WR_USERDEFINED_CONTAINER"/>
</storageModule>
<storageModule moduleId="scannerConfiguration"/>
<storageModule moduleId="userdefinedContainer">
<indexAllFiles value="false"/>
<initialized value="true"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
</cconfiguration>
</storageModule>
</cproject>
20 changes: 20 additions & 0 deletions examples/CppCheesyVisionSample/CheesyVisionRobot/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>CheesyVisionRobot</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.windriver.ide.core.wrbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.windriver.ide.core.wrnature</nature>
<nature>com.windriver.ide.core.wrcorenature</nature>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
</natures>
</projectDescription>
48 changes: 48 additions & 0 deletions examples/CppCheesyVisionSample/CheesyVisionRobot/.wrmakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# The file ".wrmakefile" is the template used by the Wind River Workbench to
# generate the makefiles of this project. Add user-specific build targets and
# make rules only(!) in this project's ".wrmakefile" file. These will then be
# automatically dumped into the makefiles.

WIND_HOME := $(subst \,/,$(WIND_HOME))
WIND_BASE := $(subst \,/,$(WIND_BASE))
WIND_USR := $(subst \,/,$(WIND_USR))

all : pre_build main_all post_build

_clean ::
@echo "make: removing targets and objects of `pwd`"

%IDE_GENERATED%

-include $(PRJ_ROOT_DIR)/*.makefile

-include *.makefile

main_all : external_build $(PROJECT_TARGETS)
@echo "make: built targets of `pwd`"

# entry point for extending the build
external_build ::
@echo ""

# main entry point for pre processing prior to the build
pre_build :: $(PRE_BUILD_STEP) generate_sources
@echo ""

# entry point for generating sources prior to the build
generate_sources ::
@echo ""

# main entry point for post processing after the build
post_build :: $(POST_BUILD_STEP) deploy_output
@echo ""

# entry point for deploying output after the build
deploy_output ::
@echo ""

clean :: external_clean $(CLEAN_STEP) _clean

# entry point for extending the build clean
external_clean ::
@echo ""
258 changes: 258 additions & 0 deletions examples/CppCheesyVisionSample/CheesyVisionRobot/.wrproject

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "WPILib.h"
#include "CheesyVisionServer.h"

class CheesyVisionRobot : public IterativeRobot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class level comment saying what the goal of this is.

{
CheesyVisionServer *server;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your indentation is inconsistent...

Please explicitly make private members private.

class {
public:
...

private:
...
};

public:
static const int listenPort = 1180;
CheesyVisionRobot()
{
server = CheesyVisionServer::GetInstance();
}

void CheesyVisionRobot::RobotInit() {
server->SetPort(listenPort);
server->StartListening();
}

void CheesyVisionRobot::DisabledInit() {
server->StopSamplingCounts();
}

void CheesyVisionRobot::DisabledPeriodic() {

}

void CheesyVisionRobot::AutonomousInit() {
server->Reset();
server->StartSamplingCounts();
}

void CheesyVisionRobot::AutonomousPeriodic() {
printf("Left Status: %d\tRight Status: %d\n", server->GetLeftStatus(), server->GetRightStatus());
printf("Left Counts: %d\tRight Counts: %d\tTotal Count: %d", server->GetLeftCount(), server->GetRightCount(), server->GetTotalCount());
}

void CheesyVisionRobot::TeleopInit() {
}

void CheesyVisionRobot::TeleopPeriodic() {
}

void CheesyVisionRobot::TestInit() {
}

void CheesyVisionRobot::TestPeriodic() {
}

};

START_ROBOT_CLASS(CheesyVisionRobot);

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#include "CheesyVisionServer.h"
#include "WPILib.h"
#include "networktables2/util/EOFException.h"

CheesyVisionServer *CheesyVisionServer::_instance = (CheesyVisionServer *) 0;

#define HEARTBEAT_TIMEOUT 3.0

CheesyVisionServer::CheesyVisionServer(int port)
{
_listenPort = port;
_counting = false;
_curLeftStatus = false;
_curRightStatus = false;
_lastHeartbeatTime = Timer::GetFPGATimestamp();
_leftCount = 0;
_rightCount = 0;
_totalCount = 0;
_listening = false;


}

void CheesyVisionServer::Run()
{
if (_listening == false) return; //Make sure we are listening

SocketServerStreamProvider *sock;
sock = new SocketServerStreamProvider(_listenPort);
while (_listening)
{
IOStream *stream = sock->accept();
_lastHeartbeatTime = Timer::GetFPGATimestamp();
try
{
while (Timer::GetFPGATimestamp() < _lastHeartbeatTime + HEARTBEAT_TIMEOUT)
{
try
{
uint8_t byte;
stream->read(&byte, 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document this command better. Under exactly what conditions does it return a EOFException? Looks like it returns one after 1 second of not receiving any data.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EOFException is returned by the SocketServerStreamProvider when the end of the stream has been reached and another read is performed

_curLeftStatus = (byte & (1 << 1)) > 0;
_curRightStatus = (byte & (1 << 0)) > 0;
UpdateCounts(_curLeftStatus,_curRightStatus);
_lastHeartbeatTime = Timer::GetFPGATimestamp();
}
catch (EOFException e)
{
//End of file, wait for a bit and read some more

Wait(0.05);
}

}
}
catch (IOException e)
{
printf("Socket IO error: %s\n", e.what());
//Catching this exception will dro
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will ... ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop the current connection and create a new one

}
delete stream;//close, delete and recreate the stream

Wait(0.05);
}
delete sock;
}

void CheesyVisionServer::Reset()
{
_leftCount = 0;
_rightCount = 0;
_totalCount = 0;

_curLeftStatus = false;
_curRightStatus = false;
}

void CheesyVisionServer::UpdateCounts(bool left, bool right)
{
if (true == _counting)
{
_leftCount += left ? 1 : 0;
_rightCount += right ? 1 : 0;
_totalCount++;
}
}

CheesyVisionServer *CheesyVisionServer::GetInstance()
{
if (CheesyVisionServer::_instance == (CheesyVisionServer *) 0)
{
CheesyVisionServer::_instance = new CheesyVisionServer();
}
return CheesyVisionServer::_instance;
}

bool CheesyVisionServer::HasClientConnection()
{
return (_lastHeartbeatTime > 0) && (Timer::GetFPGATimestamp() - _lastHeartbeatTime);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh? lastHeartbeatTime is initialized to the FPGA timestamp

}



Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "jankyTask.h"
#include "networktables2/stream/SocketServerStreamProvider.h"

#ifndef CHEESYVISIONSERVER_H_
#define CHEESYVISIONSERVER_H_



class CheesyVisionServer: private JankyTask
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private inheritance! Noooooo....

{

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to see a comment for each line in this file at a minimum. What does everything do?

static CheesyVisionServer *_instance;


CheesyVisionServer(int port = 1180);
int _listenPort;

int _leftCount;
int _rightCount;
int _totalCount;
bool _curLeftStatus;
bool _curRightStatus;
bool _counting;

double _lastHeartbeatTime;
bool _listening;
public:
static CheesyVisionServer *GetInstance();
bool HasClientConnection();
void SetPort(int port){_listenPort = port;}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should the user be allowed to change this?

virtual void Run();
void StartListening(){_listening = true; Start();}
void StopListening(){_listening = false; Pause();}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation for these should be in the .cpp file.

The more I look at this, the more I'm going to suggest dropping JankyTask and implementing the logic here. The logic really isn't hard, and the extra layer of abstraction doesn't really fit very well in this application. You want to start a thread on StartListening (locking it out so you can't call it more than once) and signal the thread to stop in StopListening (and lock it out there as well so it can't be stopped multiple times. Pretty simple.


void Reset();
void UpdateCounts(bool left, bool right);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpdateCounts should be private

void StartSamplingCounts(){_counting = true;}
void StopSamplingCounts(){_counting = false;}

bool GetLeftStatus(){return _curLeftStatus;}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have some data corruption bugs here. The reader and writer for this data are in separate threads. It is not a good idea to rely on reads being atomic.

Make a structure with all the data in it, and protect that with a lock. Grab the lock, make a tmp structure, release the lock, and return the structure. Much safer.

bool GetRightStatus(){return _curRightStatus;}
int GetLeftCount(){return _leftCount;}
int GetRightCount(){return _rightCount;}
int GetTotalCount(){return _totalCount;}


};


#endif //CHEESYVISIONSERVER_H_
60 changes: 60 additions & 0 deletions examples/CppCheesyVisionSample/CheesyVisionRobot/jankyTask.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "WPILib.h"
#include "jankyTask.h"
#include <string>

JankyTask::JankyTask(const char* taskName, UINT32 priority) {
std::string name = taskName;
char tmp[30];

if (!taskName)
{
sprintf(tmp, "%d", GetFPGATime());
name = "jankyTask-";
name += tmp;
}

enabled_ = false;
running_ = true;
isDead_ = false;

task_ = new Task(name.c_str(), (FUNCPTR)JankyTask::JankyPrivateStarterTask, priority);
task_->Start((UINT32)this);
}

JankyTask::~JankyTask(){
task_->Stop();

delete task_; // Now kill the WPI class for the task.
}

void JankyTask::JankyPrivateStarterTask(JankyTask* task) {
while (task->running_) {
if (task->enabled_) {
task->Run();
Wait(0.002); // Only wait 2ms when task is active.
}
else
Wait(0.05); // 50 ms wait period while task is 'paused'
}

task->isDead_ = true; // Falling off the edge of the earth...
}

void JankyTask::Start() {
enabled_ = true;
}

void JankyTask::Pause() {
enabled_ = false;
}

void JankyTask::Terminate() {
running_ = false;

// Above told the task to exit on the next loop around.
// That could take 2ms or 50ms based on whether it's in pause or run and how long
// the actual Run() routine takes too. So we have to wait until we're really terminated here.
while (!isDead_) {
Wait(0.02); // Wait until we're really dead on that task.
}
}
Loading