blob: 9d429e4a2f37d3953ce980c383da40d5a6e12550 [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* 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 "Vision/AxisCameraParams.h"
#include "Vision/AxisCamera.h"
#include <inetLib.h>
#include "pcre.h"
#include <sockLib.h>
#include <string.h>
#include "Synchronized.h"
#include "Timer.h"
#include "Utility.h"
#include "WPIErrors.h"
#if JAVA_CAMERA_LIB != 1
#include "DriverStation.h"
#endif
static const char *const kRotationChoices[] = {"0", "180"};
static const char *const kResolutionChoices[] = {"640x480", "640x360", "320x240", "160x120"};
static const char *const kExposureControlChoices[] = { "automatic", "hold", "flickerfree50", "flickerfree60" };
static const char *const kWhiteBalanceChoices[] = { "auto", "holdwb", "fixed_outdoor1",
"fixed_outdoor2", "fixed_indoor", "fixed_fluor1", "fixed_fluor2" };
/**
* AxisCamera constructor
*/
AxisCameraParams::AxisCameraParams(const char* ipAddress)
: m_paramTask("paramTask", (FUNCPTR) s_ParamTaskFunction)
, m_paramChangedSem (NULL)
, m_socketPossessionSem (NULL)
, m_brightnessParam (NULL)
, m_compressionParam (NULL)
, m_exposurePriorityParam (NULL)
, m_colorLevelParam (NULL)
, m_maxFPSParam (NULL)
, m_rotationParam (NULL)
, m_resolutionParam (NULL)
, m_exposureControlParam (NULL)
, m_whiteBalanceParam (NULL)
{
if (ipAddress == NULL || strlen(ipAddress) == 0)
{
#if JAVA_CAMERA_LIB == 1
wpi_setWPIErrorWithContext(ParameterOutOfRange, "IP Address must be specified");
return;
#else
DriverStation *ds = DriverStation::GetInstance();
ds->WaitForData();
UINT16 teamNumber = ds->GetTeamNumber();
char cameraIP[16];
snprintf(cameraIP, 16, "10.%d.%d.11", teamNumber / 100, teamNumber % 100);
m_ipAddress = inet_addr(cameraIP);
#endif
}
else
{
m_ipAddress = inet_addr((char*)ipAddress);
}
if (m_ipAddress == (u_long)ERROR)
{
wpi_setErrnoError();
return;
}
m_brightnessParam = new IntCameraParameter("ImageSource.I0.Sensor.Brightness=%i",
"root.ImageSource.I0.Sensor.Brightness=(.*)", false);
m_parameters.push_back(m_brightnessParam);
m_colorLevelParam = new IntCameraParameter("ImageSource.I0.Sensor.ColorLevel=%i",
"root.ImageSource.I0.Sensor.ColorLevel=(.*)", false);
m_parameters.push_back(m_colorLevelParam);
m_exposurePriorityParam = new IntCameraParameter("ImageSource.I0.Sensor.exposurePriority=%i",
"root.ImageSource.I0.Sensor.ExposurePriority=(.*)", false);
m_parameters.push_back(m_exposurePriorityParam);
m_compressionParam = new IntCameraParameter("Image.I0.Appearance.Compression=%i",
"root.Image.I0.Appearance.Compression=(.*)", true);
m_parameters.push_back(m_compressionParam);
m_maxFPSParam = new IntCameraParameter("Image.I0.Stream.FPS=%i",
"root.Image.I0.Stream.FPS=(.*)", false);
m_parameters.push_back(m_maxFPSParam);
m_rotationParam = new EnumCameraParameter("Image.I0.Appearance.Rotation=%s",
"root.Image.I0.Appearance.Rotation=(.*)", true, kRotationChoices, sizeof(kRotationChoices)/sizeof(kRotationChoices[0]));
m_parameters.push_back(m_rotationParam);
m_resolutionParam = new EnumCameraParameter("Image.I0.Appearance.Resolution=%s",
"root.Image.I0.Appearance.Resolution=(.*)", true, kResolutionChoices, sizeof(kResolutionChoices)/sizeof(kResolutionChoices[0]));
m_parameters.push_back(m_resolutionParam);
m_exposureControlParam = new EnumCameraParameter("ImageSource.I0.Sensor.Exposure=%s",
"root.ImageSource.I0.Sensor.Exposure=(.*)", false, kExposureControlChoices, sizeof(kExposureControlChoices)/sizeof(kExposureControlChoices[0]));
m_parameters.push_back(m_exposureControlParam);
m_whiteBalanceParam = new EnumCameraParameter("ImageSource.IO.Sensor.WhiteBalance=%s",
"root.ImageSource.I0.Sensor.WhiteBalance=(.*)", false, kWhiteBalanceChoices, sizeof(kWhiteBalanceChoices)/sizeof(kWhiteBalanceChoices[0]));
m_parameters.push_back(m_whiteBalanceParam);
m_paramChangedSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
m_socketPossessionSem = semBCreate (SEM_Q_PRIORITY, SEM_FULL);
m_paramTask.Start((int)this);
}
/**
* Destructor
*/
AxisCameraParams::~AxisCameraParams()
{
m_paramTask.Stop();
semDelete(m_socketPossessionSem);
semDelete(m_paramChangedSem);
delete m_whiteBalanceParam;
delete m_exposureControlParam;
delete m_resolutionParam;
delete m_rotationParam;
delete m_maxFPSParam;
delete m_compressionParam;
delete m_exposurePriorityParam;
delete m_colorLevelParam;
delete m_brightnessParam;
}
/**
* Static function to start the parameter updating task
*/
int AxisCameraParams::s_ParamTaskFunction(AxisCameraParams* thisPtr)
{
return thisPtr->ParamTaskFunction();
}
/**
* Main loop of the parameter task.
* This loop runs continuously checking parameters from the camera for
* posted changes and updating them if necessary.
*/
// TODO: need to synchronize the actual setting of parameters (the assignment statement)
int AxisCameraParams::ParamTaskFunction()
{
static bool firstTime = true;
while (true)
{
semTake(m_socketPossessionSem, WAIT_FOREVER);
if (firstTime)
{
while (ReadCamParams() == 0);
firstTime = false;
}
bool restartRequired = false;
ParameterVector_t::iterator it = m_parameters.begin();
ParameterVector_t::iterator end = m_parameters.end();
for(; it != end; it++)
{
bool changed = false;
char param[150];
restartRequired |= (*it)->CheckChanged(changed, param);
if (changed)
{
UpdateCamParam(param);
}
}
if (restartRequired)
{
RestartCameraTask();
}
semGive(m_socketPossessionSem);
}
return 0;
}
/**
* Write the brightness value to the camera.
* @param brightness valid values 0 .. 100
*/
void AxisCameraParams::WriteBrightness(int brightness)
{
m_brightnessParam->SetValue(brightness);
semGive(m_paramChangedSem);
}
/**
* Get the brightness value.
* @return Brightness value from the camera.
*/
int AxisCameraParams::GetBrightness()
{
return m_brightnessParam->GetValue();
}
/**
* Set the white balance value.
* @param whiteBalance Valid values from the WhiteBalance_t enum.
*/
void AxisCameraParams::WriteWhiteBalance(WhiteBalance_t whiteBalance)
{
m_whiteBalanceParam->SetValue(whiteBalance);
semGive(m_paramChangedSem);
}
/**
* Retrieve the current white balance parameter.
* @return The white balance value.
*/
AxisCameraParams::WhiteBalance_t AxisCameraParams::GetWhiteBalance()
{
return (WhiteBalance_t) m_whiteBalanceParam->GetValue();
}
/**
* Write the color level to the camera.
* @param colorLevel valid values are 0 .. 100
*/
void AxisCameraParams::WriteColorLevel(int colorLevel)
{
m_colorLevelParam->SetValue(colorLevel);
semGive(m_paramChangedSem);
}
/**
* Retrieve the color level from the camera.
* @Returns the camera color level.
*/
int AxisCameraParams::GetColorLevel()
{
return m_colorLevelParam->GetValue();
}
/**
* Write the exposure control value to the camera.
* @param exposureControl A mode to write in the Exposure_t enum.
*/
void AxisCameraParams::WriteExposureControl(Exposure_t exposureControl)
{
m_exposureControlParam->SetValue(exposureControl);
semGive(m_paramChangedSem);
}
/**
* Get the exposure value from the camera.
* @returns the exposure value from the camera.
*/
AxisCameraParams::Exposure_t AxisCameraParams::GetExposureControl()
{
return (Exposure_t) m_exposureControlParam->GetValue();
}
/**
* Write resolution value to camera.
* @param resolution The camera resolution value to write to the camera. Use the Resolution_t enum.
*/
void AxisCameraParams::WriteResolution(Resolution_t resolution)
{
m_resolutionParam->SetValue(resolution);
semGive(m_paramChangedSem);
}
/**
* Get the resolution value from the camera.
* @returns resultion value for the camera.
*/
AxisCameraParams::Resolution_t AxisCameraParams::GetResolution()
{
return (Resolution_t) m_resolutionParam->GetValue();
}
/**
* Write the exposre priority value to the camera.
* @param exposurePriority Valid values are 0, 50, 100.
* 0 = Prioritize image quality
* 50 = None
* 100 = Prioritize frame rate
*/
void AxisCameraParams::WriteExposurePriority(int exposurePriority)
{
m_exposurePriorityParam->SetValue(exposurePriority);
semGive(m_paramChangedSem);
}
int AxisCameraParams::GetExposurePriority()
{
return m_exposurePriorityParam->GetValue();
}
/**
* Write the rotation value to the camera.
* If you mount your camera upside down, use this to adjust the image for you.
* @param rotation The image from the Rotation_t enum in AxisCameraParams (kRotation_0 or kRotation_180)
*/
void AxisCameraParams::WriteRotation(Rotation_t rotation)
{
m_rotationParam->SetValue(rotation);
semGive(m_paramChangedSem);
}
/**
* Get the rotation value from the camera.
* @return The rotation value from the camera (Rotation_t).
*/
AxisCameraParams::Rotation_t AxisCameraParams::GetRotation()
{
return (Rotation_t) m_rotationParam->GetValue();
}
/**
* Write the compression value to the camera.
* @param compression Values between 0 and 100.
*/
void AxisCameraParams::WriteCompression(int compression)
{
m_compressionParam->SetValue(compression);
semGive(m_paramChangedSem);
}
/**
* Get the compression value from the camera.
* @return The cached compression value from the camera.
*/
int AxisCameraParams::GetCompression()
{
return m_compressionParam->GetValue();
}
/**
* Write the maximum frames per second that the camera should send
* Write 0 to send as many as possible.
* @param maxFPS The number of frames the camera should send in a second, exposure permitting.
*/
void AxisCameraParams::WriteMaxFPS(int maxFPS)
{
m_maxFPSParam->SetValue(maxFPS);
semGive(m_paramChangedSem);
}
/**
* Get the max number of frames per second that the camera will send
* @return Maximum frames per second.
*/
int AxisCameraParams::GetMaxFPS()
{
return m_maxFPSParam->GetValue();
}
/**
* Update a camera parameter.
* Write a camera parameter to the camera when it has bene changed.
* @param param the string to insert into the http request.
* @returns 0 if it failed, otherwise nonzero.
*/
int AxisCameraParams::UpdateCamParam(const char* param)
{
char *requestString =
"GET /axis-cgi/admin/param.cgi?action=update&%s HTTP/1.1\n\
User-Agent: HTTPStreamClient\n\
Connection: Keep-Alive\n\
Cache-Control: no-cache\n\
Authorization: Basic RlJDOkZSQw==\n\n";
char completedRequest[1024];
sprintf(completedRequest, requestString, param);
// Send request
int camSocket = CreateCameraSocket(completedRequest);
if (camSocket == ERROR)
{
printf("UpdateCamParam failed: %s\n", param);
return 0;
}
close(camSocket);
return 1;
}
/**
* Read the full param list from camera, use regular expressions to find the bits we care about
* assign values to member variables.
*/
int AxisCameraParams::ReadCamParams()
{
char * requestString =
"GET /axis-cgi/admin/param.cgi?action=list HTTP/1.1\n\
User-Agent: HTTPStreamClient\n\
Connection: Keep-Alive\n\
Cache-Control: no-cache\n\
Authorization: Basic RlJDOkZSQw==\n\n";
int camSocket = CreateCameraSocket(requestString);
if (camSocket == ERROR)
{
return 0;
}
// Allocate on the heap since it is very large and only needed once
char *readBuffer = new char[27000];
int totalRead = 0;
while (1)
{
wpi_assert(totalRead < 26000);
int bytesRead = recv(camSocket, &readBuffer[totalRead], 1000, 0);
if (bytesRead == ERROR)
{
wpi_setErrnoErrorWithContext("Failed to read image header");
close(camSocket);
return 0;
}
else if (bytesRead <= 0)
{
break;
}
totalRead += bytesRead;
}
readBuffer[totalRead] = '\0';
ParameterVector_t::iterator it = m_parameters.begin();
ParameterVector_t::iterator end = m_parameters.end();
for(; it != end; it++)
{
(*it)->GetParamFromString(readBuffer, totalRead);
}
close(camSocket);
delete [] readBuffer;
return 1;
}
/*
* Create a socket connected to camera
* Used to create a connection to the camera by both AxisCameraParams and AxisCamera.
* @param requestString The initial request string to send upon successful connection.
* @return ERROR if failed, socket handle if successful.
*/
int AxisCameraParams::CreateCameraSocket(const char *requestString)
{
int sockAddrSize;
struct sockaddr_in serverAddr;
int camSocket;
/* create socket */
if ((camSocket = socket(AF_INET, SOCK_STREAM, 0)) == ERROR)
{
wpi_setErrnoErrorWithContext("Failed to create the camera socket");
return ERROR;
}
sockAddrSize = sizeof(struct sockaddr_in);
bzero((char *) &serverAddr, sockAddrSize);
serverAddr.sin_family = AF_INET;
serverAddr.sin_len = (u_char) sockAddrSize;
serverAddr.sin_port = htons(80);
serverAddr.sin_addr.s_addr = m_ipAddress;
/* connect to server */
struct timeval connectTimeout;
connectTimeout.tv_sec = 5;
connectTimeout.tv_usec = 0;
if (connectWithTimeout(camSocket, (struct sockaddr *) &serverAddr, sockAddrSize, &connectTimeout) == ERROR)
{
wpi_setErrnoErrorWithContext("Failed to connect to the camera");
close(camSocket);
return ERROR;
}
int sent = send(camSocket, requestString, strlen(requestString), 0);
if (sent == ERROR)
{
wpi_setErrnoErrorWithContext("Failed to send a request to the camera");
close(camSocket);
return ERROR;
}
return camSocket;
}