3 // (C) Copyright Seb Wills 2005
5 #include "../Common/Common.h"
7 #include "SocketInputBase.h"
9 #include "DasherInterfaceBase.h"
16 #define DASHER_SOCKET_CLOSE_FUNCTION closesocket
18 #include <sys/socket.h>
19 #include <netinet/in.h>
21 #define DASHER_SOCKET_CLOSE_FUNCTION close
24 using namespace Dasher
;
26 static SModuleSettings sSettings
[] = {
27 {LP_SOCKET_PORT
, T_LONGSPIN
, 0, 65535, 1, 10, _("Port:")},
28 {SP_SOCKET_INPUT_X_LABEL
, T_STRING
, -1, -1, -1, -1, _("X label:")},
29 {LP_SOCKET_INPUT_X_MIN
, T_LONGSPIN
, -2147480000, 2147480000, 1000, 10000, _("X minimum:")},
30 {LP_SOCKET_INPUT_X_MAX
, T_LONGSPIN
, -2147480000, 2147480000, 1000, 10000, _("X maximum:")},
31 {SP_SOCKET_INPUT_Y_LABEL
, T_STRING
, -1, -1, -1, -1, _("Y label:")},
32 {LP_SOCKET_INPUT_Y_MIN
, T_LONGSPIN
, -2147480000, 2147480000, 1000, 10000, _("Y minimum:")},
33 {LP_SOCKET_INPUT_Y_MAX
, T_LONGSPIN
, -2147480000, 2147480000, 1000, 10000, _("Y maximum:")},
34 {BP_SOCKET_DEBUG
, T_BOOL
, -1, -1, -1, -1, _("Print socket-related debugging information to console:")}
37 Dasher::CSocketInputBase::CSocketInputBase(CSettingsUser
*pCreator
, CMessageDisplay
*pMsgs
)
38 : CScreenCoordInput(1, _("Socket Input")), CSettingsUserObserver(pCreator
), m_pMsgs(pMsgs
) {
40 debug_socket_input
= false;
41 readerRunning
= false;
43 SetCoordinateCount(2);
44 for(int i
= 0; i
< DASHER_SOCKET_INPUT_MAX_COORDINATE_COUNT
; i
++) {
45 dasherMaxCoordinateValues
[i
] = 4096; // the real value will come later when SetMaxCoordinates is invoked
46 rawMinValues
[i
] = 0.0; // suitable defaults for BCI2000
47 rawMaxValues
[i
] = 512.0;
48 memset(coordinateNames
[i
], '\0', DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH
+ 1);
49 dasherCoordinates
[i
] = 2048; // initialise to mid-range value
52 // initialise using parameter settings:
53 SetDebug(GetBoolParameter(BP_SOCKET_DEBUG
));
54 SetReaderPort(GetLongParameter(LP_SOCKET_PORT
));
55 SetRawRange(0, ((double)GetLongParameter(LP_SOCKET_INPUT_X_MIN
)) / 1000.0, ((double)GetLongParameter(LP_SOCKET_INPUT_X_MAX
)) / 1000.0);
56 SetRawRange(1, ((double)GetLongParameter(LP_SOCKET_INPUT_Y_MIN
)) / 1000.0, ((double)GetLongParameter(LP_SOCKET_INPUT_Y_MAX
)) / 1000.0);
57 SetCoordinateLabel(0, GetStringParameter(SP_SOCKET_INPUT_X_LABEL
).c_str());
58 SetCoordinateLabel(1, GetStringParameter(SP_SOCKET_INPUT_Y_LABEL
).c_str());
59 SocketDebugMsg("Socket input is initialised but not yet enabled");
62 Dasher::CSocketInputBase::~CSocketInputBase() {
63 // Would like to call StopListening(); Can't do this here because by the time this (base class) destructor is called,
64 // the derived class instance has been deleted, so we can no longer call it's StopListening.
65 // Instead, you should call StopListening in the derived class's destructor.
68 void Dasher::CSocketInputBase::HandleEvent(int iParameter
) {
71 SetReaderPort(GetLongParameter(LP_SOCKET_PORT
));
73 case SP_SOCKET_INPUT_X_LABEL
:
74 SetCoordinateLabel(0, GetStringParameter(SP_SOCKET_INPUT_X_LABEL
).c_str());
76 case SP_SOCKET_INPUT_Y_LABEL
:
77 SetCoordinateLabel(1, GetStringParameter(SP_SOCKET_INPUT_Y_LABEL
).c_str());
79 case LP_SOCKET_INPUT_X_MIN
:
80 case LP_SOCKET_INPUT_X_MAX
:
81 SetRawRange(0, ((double)GetLongParameter(LP_SOCKET_INPUT_X_MIN
)) / 1000.0, ((double)GetLongParameter(LP_SOCKET_INPUT_X_MAX
)) / 1000.0);
83 case LP_SOCKET_INPUT_Y_MIN
:
84 case LP_SOCKET_INPUT_Y_MAX
:
85 SetRawRange(1, ((double)GetLongParameter(LP_SOCKET_INPUT_Y_MIN
)) / 1000.0, ((double)GetLongParameter(LP_SOCKET_INPUT_Y_MAX
)) / 1000.0);
88 SetDebug(GetBoolParameter(BP_SOCKET_DEBUG
));
95 bool Dasher::CSocketInputBase::StartListening() {
96 struct sockaddr_in name
;
98 // this shouldn't be called if we are already listening, but if it is, let's failsafe
99 // rather than attempt to bind to the same port twice.
104 SocketDebugMsg("Socket input: binding to socket and starting to listen.");
106 if((sock
= socket(PF_INET
, SOCK_DGRAM
, 0)) == -1) {
107 //TODO This is not a very good error message even in English...???
108 m_pMsgs
->Message(_("Error creating socket"),true);
112 name
.sin_family
= AF_INET
;
113 name
.sin_port
= htons(port
);
114 name
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
115 if(bind(sock
, (struct sockaddr
*)&name
, sizeof(name
)) < 0) {
116 ReportErrnoError(_("Error binding to socket - already in use?"));
117 DASHER_SOCKET_CLOSE_FUNCTION(sock
);
122 if(!LaunchReaderThread()) {
123 // LaunchReaderThread will already have displayed an error message
124 DASHER_SOCKET_CLOSE_FUNCTION(sock
);
129 readerRunning
= true;
134 void Dasher::CSocketInputBase::StopListening() {
140 CancelReaderThread();
143 DASHER_SOCKET_CLOSE_FUNCTION(sock
);
145 readerRunning
= false;
146 SocketDebugMsg("Socket input: stopped listening to socket.");
149 void CSocketInputBase::SetReaderPort(int _port
) {
151 SocketDebugMsg("SetReaderPort called with same value (%d), so ignoring.",port
);
155 SocketDebugMsg("Setting socket input port to %d.", _port
);
166 void CSocketInputBase::SetCoordinateLabel( int iWhichCoordinate
, const char *Label
) {
167 DASHER_ASSERT(iWhichCoordinate
< DASHER_SOCKET_INPUT_MAX_COORDINATE_COUNT
);
168 if(strlen(Label
) > DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH
) {
169 const char *msg
=_("Warning truncating socket input label '%s' to %i characters.");
170 char *buf(new char[strlen(msg
)+strlen(Label
)+DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH
]);
171 sprintf(buf
,msg
,Label
,DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH
);
172 m_pMsgs
->Message(buf
, true);
175 strncpy(coordinateNames
[iWhichCoordinate
], Label
, DASHER_SOCKET_INPUT_MAX_COORDINATE_LABEL_LENGTH
);
176 SocketDebugMsg("Socket input: set coordinate %d label to '%s'.", iWhichCoordinate
, coordinateNames
[iWhichCoordinate
]);
179 void CSocketInputBase::SetRawRange(int iWhich
, double dMin
, double dMax
) {
180 rawMinValues
[iWhich
] = dMin
;
181 rawMaxValues
[iWhich
] = dMax
;
182 SocketDebugMsg("Socket input: set coordinate %d input range to: min: %lf, max: %lf.", iWhich
, dMin
, dMax
);
188 void CSocketInputBase::ReadForever() {
189 // this gets called in its own thread. It reads datagrams and updates the coordinate variables
193 SocketDebugMsg("Reading from socket...");
194 if((numbytes
= recv(sock
, buffer
, sizeof(buffer
) - 1, 0)) == -1) {
195 m_pMsgs
->Message(_("Socket input: Error reading from socket"),false);
198 buffer
[numbytes
] = '\0';
200 SocketDebugMsg(" received string: '%s'.", buffer
);
202 ParseMessage(buffer
);
207 // Parse and act on a message received from the socket
208 // Allowed to modify contents of memory pointed to by message, up to its final '\0'.
209 void CSocketInputBase::ParseMessage(char *message
) {
213 // myint dasherCoordinateTemp;
214 // parse line by line
215 while((p
= strchr(message
, '\n')) != NULL
) {
217 // Each line is expected to be of the form "Label <value>"
218 // We run through each coordinate label, checking if this line matches it
219 for(int i
= 0; i
< coordinateCount
; i
++) {
220 int len
= strlen(coordinateNames
[i
]);
221 if(strncmp(coordinateNames
[i
], message
, len
) == 0) {
222 SocketDebugMsg("Matched label '%s'...", coordinateNames
[i
]);
223 // First len chars match the label of this coordinate. Value should be at the next non-space char.
224 if(sscanf(message
+ len
, "%lf", &rawdouble
) == 1) {
225 SocketDebugMsg("...parsed value as %lf.", rawdouble
);
227 #ifdef DASHER_SOCKET_INPUT_BCI2000_OVERFLOW_WORKAROUND
228 // a temporary workaround to undo an integer overflow that occurs in messages sent from BCI2000
229 if(rawdouble
> 32000) {
232 if(rawdouble
> 768 && rawdouble
< 32000) {
238 // for clipping purposes, we want to ignore whether Max < Min (which indicates that
239 // we need to flip the sense of the input)
240 double actualMax
= (rawMaxValues
[i
] > rawMinValues
[i
]) ? rawMaxValues
[i
] : rawMinValues
[i
];
241 double actualMin
= (rawMaxValues
[i
] > rawMinValues
[i
]) ? rawMinValues
[i
] : rawMaxValues
[i
];
242 if(rawdouble
< actualMin
) {
243 //TODO: Should these be converted to calls to Message() ? On first occurrence only???
244 cerr
<< "Socket input: clipped " << coordinateNames
[i
] << " value of " << rawdouble
<< "to configured minimum of " << actualMin
<< endl
;
245 rawdouble
= actualMin
;
247 if(rawdouble
> actualMax
) {
248 //TODO: Should these be converted to calls to Message() ? On first occurrence only???
249 cerr
<< "Socket input: clipped " << message
<< " value of " << rawdouble
<< "to configured maximum of " << actualMax
<< endl
;
250 rawdouble
= actualMax
;
253 // convert to dasher coordinates:
255 const bool do_lowpass
= false;
257 // initial attempt at putting a low-pass filter in. Not well tested; disabled for now.
258 double timeconst
= 100.0; // no of updates
259 double newcoord
= ((rawdouble
- rawMinValues
[i
]) / (rawMaxValues
[i
] - rawMinValues
[i
]) * dasherMaxCoordinateValues
[i
]);
260 dasherCoordinates
[i
] = (myint
) ((1 - 1 / timeconst
) * (double)dasherCoordinates
[i
] + (1 / timeconst
) * newcoord
);
263 // straightforward linear mapping to dasher coordinates:
264 // Treat X coordinate specially: reverse sense so it has the more intuitive left-to-right direction
265 double min
= (i
==0) ? rawMaxValues
[i
] : rawMinValues
[i
];
266 double max
= (i
==0) ? rawMinValues
[i
] : rawMaxValues
[i
];
267 if(max
!= min
) { // prevent nasty explosion
268 dasherCoordinates
[i
] = (myint
) ((rawdouble
- min
) / (max
- min
) * (double)dasherMaxCoordinateValues
[i
]);
272 SocketDebugMsg("Socket input: new value for coordinate %d rescales to %u in Dasher's internal coordinates (range 0-%d).", i
, (unsigned int) dasherCoordinates
[i
], (int) dasherMaxCoordinateValues
[i
]);
274 // don't break out of the for loop in case we get asked to drive two coordinates from same label
276 SocketDebugMsg("... but couldn't parse the text following that label as a number.");
281 message
= p
+ 1; // move on to next line (if there isn't one, we'll point at the terminating '\0')
285 void CSocketInputBase::SetDebug(bool _debug
) {
287 SocketDebugMsg("Disabling socket debug messages.");
289 debug_socket_input
= _debug
;
291 SocketDebugMsg("Enabled socket debug messages.");
295 void CSocketInputBase::ReportErrnoError(const std::string
&prefix
) {
296 int err
= errno
; errno
=0;
297 const char *msg
= _("Dasher Socket Input error: %s: %s");
298 char *e
= strerror(err
);
299 char *buf(new char[strlen(msg
) + prefix
.length() + strlen(e
)]);
300 sprintf(buf
,msg
,prefix
.c_str(),e
);
301 m_pMsgs
->Message(buf
,true);
305 void CSocketInputBase::SocketDebugMsg(const char *pszFormat
, ...) {
306 if(debug_socket_input
) {
308 va_start(v
, pszFormat
);
309 vfprintf(stderr
, pszFormat
, v
);
310 fprintf(stderr
, "\n");
315 bool CSocketInputBase::GetSettings(SModuleSettings
**pSettings
, int *iCount
) {
316 *pSettings
= sSettings
;
317 *iCount
= sizeof(sSettings
) / sizeof(SModuleSettings
);