blob: 222128d4363e8711a70b2dc7902469269fe3c3c9 [file] [log] [blame]
jerrymf1579332013-02-07 01:56:28 +00001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
5/*----------------------------------------------------------------------------*/
6
7#include <vxWorks.h>
8
9#include "PCVideoServer.h"
10
11#include <errnoLib.h>
12#include <hostLib.h>
13#include <inetLib.h>
14#include <sockLib.h>
15
16#include "NetworkCommunication/UsageReporting.h"
17#include "Task.h"
18#include "Timer.h"
19#include "Vision/AxisCamera.h"
20#include "WPIErrors.h"
21
22/**
23 * @brief Implements an object that automatically does a close on a
24 * camera socket on destruction.
25 */
26class ScopedSocket {
27public:
28 ScopedSocket(int camSock)
29 : m_camSock(camSock)
30 {
31 }
32
33 ~ScopedSocket() {
34 if (m_camSock != ERROR) {
35 close(m_camSock);
36 }
37 }
38 // Cast to int allows you to pass this to any function that
39 // takes the socket as an int.
40 operator int() const {
41 return m_camSock;
42 }
43
44private:
45 int m_camSock;
46};
47
48//============================================================================
49// PCVideoServer
50//============================================================================
51
52/**
53 * @brief Constructor.
54 */
55PCVideoServer::PCVideoServer()
56 : m_serverTask("PCVideoServer", (FUNCPTR)s_ServerTask)
57 , m_newImageSem (NULL)
58 , m_stopServer (false)
59{
60 AxisCamera &cam = AxisCamera::GetInstance();
61 m_newImageSem = cam.GetNewImageSem();
62 if (!cam.StatusIsFatal())
63 {
64 StartServerTask();
65 }
66 else
67 {
68 CloneError(&cam);
69 }
70}
71
72/**
73 * @brief Destructor.
74 * Stop serving images and destroy this class.
75 */
76PCVideoServer::~PCVideoServer()
77{
78 // Stop the images to PC server.
79 Stop();
80 // Clear the error so that you can use this object to make a connection to
81 // the VIDEO_TO_PC_PORT to stop the ImageToPCServer if it is waiting to
82 // accept connections from a PC.
83 ClearError();
84 // Open a socket.
85 int camSock = socket(AF_INET, SOCK_STREAM, 0);
86 if (camSock == ERROR)
87 {
88 wpi_setErrnoError();
89 return;
90 }
91 ScopedSocket scopedCamSock(camSock);
92 // If successful
93 if (!StatusIsFatal())
94 {
95 // Create a connection to the localhost.
96 struct sockaddr_in selfAddr;
97 int sockAddrSize = sizeof(selfAddr);
98 bzero ((char *) &selfAddr, sockAddrSize);
99 selfAddr.sin_family = AF_INET;
100 selfAddr.sin_len = (u_char) sockAddrSize;
101 selfAddr.sin_port = htons (VIDEO_TO_PC_PORT);
102
103 if (( (int)(selfAddr.sin_addr.s_addr = inet_addr (const_cast<char*>("localhost")) ) != ERROR) ||
104 ( (int)(selfAddr.sin_addr.s_addr = hostGetByName (const_cast<char*>("localhost")) ) != ERROR))
105 {
106 struct timeval connectTimeout;
107 connectTimeout.tv_sec = 1;
108 connectTimeout.tv_usec = 0;
109 connectWithTimeout(camSock, (struct sockaddr *) &selfAddr, sockAddrSize, &connectTimeout);
110 }
111 }
112}
113
114/**
115 * Start the task that is responsible for sending images to the PC.
116 */
117int PCVideoServer::StartServerTask()
118{
119 if (StatusIsFatal())
120 {
121 return -1;
122 }
123 int id = 0;
124 m_stopServer = false;
125 // Check for prior copy of running task
126 int oldId = taskNameToId((char*)m_serverTask.GetName());
127 if(oldId != ERROR)
128 {
129 // TODO: Report error. You are in a bad state.
130 taskDelete(oldId);
131 }
132
133 // spawn video server task
134 // this is done to ensure that the task is spawned with the
135 // floating point context save parameter.
136 bool started = m_serverTask.Start((int)this);
137
138 id = m_serverTask.GetID();
139
140 if (!started)
141 {
142 wpi_setWPIError(TaskError);
143 return id;
144 }
145 taskDelay(1);
146 return id;
147}
148
149/**
150 * @brief Start sending images to the PC.
151 */
152void PCVideoServer::Start()
153{
154 StartServerTask();
155}
156
157/**
158 * @brief Stop sending images to the PC.
159 */
160void PCVideoServer::Stop()
161{
162 m_stopServer = true;
163}
164
165/**
166 * Static stub for kicking off the server task
167 */
168int PCVideoServer::s_ServerTask(PCVideoServer *thisPtr)
169{
170 return thisPtr->ServerTask();
171}
172
173/**
174 * @brief Initialize the socket and serve images to the PC.
175 * This is the task that serves images to the PC in a loop. This runs
176 * as a separate task.
177 */
178int PCVideoServer::ServerTask()
179{
180 /* Setup to PC sockets */
181 struct sockaddr_in serverAddr;
182 int sockAddrSize = sizeof(serverAddr);
183 int pcSock = ERROR;
184 bzero ((char *) &serverAddr, sockAddrSize);
185 serverAddr.sin_len = (u_char) sockAddrSize;
186 serverAddr.sin_family = AF_INET;
187 serverAddr.sin_port = htons (VIDEO_TO_PC_PORT);
188 serverAddr.sin_addr.s_addr = htonl (INADDR_ANY);
189
190 int success;
191 while (true)
192 {
193 taskSafe();
194 // Create the socket.
195 if ((pcSock = socket (AF_INET, SOCK_STREAM, 0)) == ERROR)
196 {
197 wpi_setErrnoErrorWithContext("Failed to create the PCVideoServer socket");
198 continue;
199 }
200 // Set the TCP socket so that it can be reused if it is in the wait state.
201 int reuseAddr = 1;
202 setsockopt(pcSock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&reuseAddr), sizeof(reuseAddr));
203 // Bind socket to local address.
204 if (bind (pcSock, (struct sockaddr *) &serverAddr, sockAddrSize) == ERROR)
205 {
206 wpi_setErrnoErrorWithContext("Failed to bind the PCVideoServer port");
207 close (pcSock);
208 continue;
209 }
210 // Create queue for client connection requests.
211 if (listen (pcSock, 1) == ERROR)
212 {
213 wpi_setErrnoErrorWithContext("Failed to listen on the PCVideoServer port");
214 close (pcSock);
215 continue;
216 }
217
218 struct sockaddr_in clientAddr;
219 int clientAddrSize;
220 int newPCSock = accept (pcSock, reinterpret_cast<sockaddr*>(&clientAddr), &clientAddrSize);
221 if (newPCSock == ERROR)
222 {
223 close(pcSock);
224 continue;
225 }
226 //TODO: check camera error
227
228 // Report usage when there is actually a connection.
229 nUsageReporting::report(nUsageReporting::kResourceType_PCVideoServer, 0);
230
231 int numBytes = 0;
232 int imageDataSize = 0;
233 char* imageData = NULL;
234
235 while(!m_stopServer)
236 {
237 success = semTake(m_newImageSem, 1000);
238 if (success == ERROR)
239 {
240 // If the semTake timed out, there are no new images from the camera.
241 continue;
242 }
243 success = AxisCamera::GetInstance().CopyJPEG(&imageData, numBytes, imageDataSize);
244 if (!success)
245 {
246 // No point in running too fast -
247 Wait(1.0);
248 // If camera is not initialzed you will get failure and
249 // the timestamp is invalid. Reset this value and try again.
250 continue;
251 }
252
253 // Write header to PC
254 static const char header[4]={1,0,0,0};
255 int headerSend = write(newPCSock, const_cast<char*>(header), 4);
256
257 // Write image length to PC
258 int lengthSend = write(newPCSock, reinterpret_cast<char*>(&numBytes), 4);
259
260 // Write image to PC
261 int sent = write (newPCSock, imageData, numBytes);
262
263 // The PC probably closed connection. Get out of here
264 // and try listening again.
265 if (headerSend == ERROR || lengthSend == ERROR || sent == ERROR)
266 {
267 break;
268 }
269 }
270 // Clean up
271 delete [] imageData;
272 close (newPCSock);
273 newPCSock = ERROR;
274 close (pcSock);
275 pcSock = ERROR;
276 taskUnsafe();
277 Wait(0.1);
278 }
279 return (OK);
280}
281