=============================================================================== WHERE VERSION NUMBERS ARE USED IN THE CODE =============================================================================== DETAILS OF VERSION NUMBERING WITHIN THE SERVER ARE IN version.h =============================================================================== KNOWN BUGS AND ISSUES =============================================================================== - KNOWN PROBLEM FEB 2014, no plan to fix any time soon ... zero-length (single-frame) videos on loop: they swallow all the GUI time, since CMainFrame::OnBridgeEndOfSegment is the handler for these messages. Upshot is that the log chugs on but the GUI freezes. Not sure what a more sensible solution would be; all solutions would involve specifying a >0 time for the video to take, but then it's not a single-frame video; in other words, single-frame video are a bit crazy conceptually, even if they seem appealing as a quick alternative to a bitmap in a video-based application. Use a short-length video or a still image (processed as such). - KNOWN PROBLEM FEB 2014, no plan to fix any time soon ... if you drag a video from one monitor to another, it will fail (stop) ... should not be a problem in operant chamber environments! ... likely due to not handling the loss of DirectX surfaces - KNOWN ISSUE FEB 2004 - RELATES TO A BUG IN WINDOWS XP Occasional lock-up when WhiskerServer is minimized under Windows XP with the XP interface. Doesn't appear to happen with the Windows Classic interface. Reason currently unknown. Ah, sombody else has had the problem: http://dvb2000recorder.gmxhome.de/Download/readme.txt Known bug in XP: Q317159 (see http://support.microsoft.com/default.aspx?scid=kb;en-us;317159) ... resolved by updating the Windows XP Service Pack (or by using the Windows Classic interface!). - KNOWN ISSUE Versions 2.10 - 2.11.4 - LAPTOP SERIAL CARDS MAY STOP RESPONDING Some serial device drivers are reported reset the RTS / DTR (output) lines after a period of c. 30s on at least one laptop. The Windows API states that the line states used in the DCB for CreateFile for DTR and RTS should be maintained (Comm timeouts set to infinite, but these should just affect serial Read/Write anyway). Does not happen with desktop machines - suggests that the PCMCIA drivers drop the lines (power saving?) if nothing happens... Possible that the driver was doing some form of 'best guess' handshaking, and therefore dropping the lines (?). Tried asserting the DTR/RTS values on every poll in an attempt to prevent this.... doesn't help... (the drivers are just ignoring the assertion of the DTR / RTS? Or Windows driver doesn't pass them on because it "knows" they are not needed??). Tried toggling the DTR & RTS values every poll... which works. Obviously, we should only do this if we are not using the lines as outputs! - KNOWN ISSUE Release Build Versions 2.12.0 - 2.12.2 - BNC controller Occasional errors in BNC IO due to lack of missing critical section lock in Polling of BNC cards (lines missing from checked-in source). - KNOWN ISSUE Inappropriate mouse behaviour from intasolve touchscreens reported under UPDD v3. Believed to be a UPDD error. (May be related to the problem with UPDD-touchscreen communication problem described above... let's see if the TBApiReinit call also solves this problem.) =============================================================================== VERSION TRACKER =============================================================================== Fixes to v2.2 (MRFA) ------------------------------------------------- Bugs in v2.2 MRFA ### Touchscreens - failure with mulitple touchscreens. Server decides that there's lots of touchscreen 1 (only one of which is enabled), other numbers can be enabled for next time but become inactive devideID 1 again. Got it: CHardwareConfig was the culprit. Read iDeviceID!=0 instead of iDeviceID, thus setting all previously ID'd touchscreens to 1 (true). Fixed: version 2.2.1 MRFA ### Failure to load (system "hang";mouse stationary etc) v2.2 on SCRATCHY when administrator priviledges applied. Loads under user prvs, but is a terrible PITA: using c. 100% of CPU resources. v1 is fine. Problem seems to manifest on creation of high resolution timer / mm timer, looking at where the initialisation slows down. Will investigate using remote debug. Investigating revealed apparant starvation of other Threads. Completely allieviated artificially speeding timerproc (Poll()). However, noproblems isolated within Poll() and (prob appears to be due to deadlock with MFC Threads setting up the document/view whilst Polling is going on. Overall: the possibility of thread starvation is REAL (clearly) and cannot be ignored. Double Fix; Introduced a synchronisation object which is set by Housekeeping() to show that the windows messages are getting through. If not (after twice the minimum number of polls, then Poll() yields for 20ms on each call until Housekeeping() sets event. (Made Poll() go slow from start until first Housekeeping() to allow smoother startup). Found a potential problem (see 'MMTimer' in Knowledge Base). This problem is an issue with MFC DLLs (probably not an issue with the multithreading versions, although not specified). Cals to InitInstance must return before any call to ::timeSetEvent(). Changed Initialisation code for WhiskerServer to reflect this (difficult to know if this is important with static MFC linkage). v2.2a built & working With hindsight - that didn't fix so much as change the problem (see below) ### Changes in v 2.2b minor change to CClient/ CView(s) & command set REMOTE CONTROL ID command : "ReportRemoteControlID" now removed. All RC will be conducted via the client-client msg protocol. Member functions etc. removed from relevant threads. PERMISSIONS TO NON-LOCAL CLIENTS 'PermitClientMessages' 'TimeStamps' 'ResetClock' 'SendToCLient' are now permitted by non-local client on non-promiscuous server. ANTISTARVATION Change to the staravation code: Force Poll()ing thread to yield for 1ms every cycle (rather than allowing bursts of 100% CPU use). Force the UI thread to yield after message pumping (be polite). Note: previous strategy forced it to yield just before it went into the wait state on it's message pump. Not ideal,esp. as it would only do it if it had no messages, and a loop which posted messages to the UI FrameWnd could starve everything anyway. Sigh. Change to the destructor (bad thread sync on after a yielded Poll() could & occasionally did result in the Poll()ing thread access violationing it's way through a plethora of items deleted in the destructor...). v2.3 ### OK - maybe I've gone "rod for my own back", but... The control language has evolved in a slightly odd way. So: redesigned it so that messages are conceptually in two classes "general" and "device". Device messages are prefixed by the device type (Display, Line, Audio or Timer), and are OF A SIMILAR FORM for each device type, where appropriate [ie. we don't "request" and "kill" timers, then "setEvent" and "clearEvent" for displays...] Hopefully, this will also allow the language to be expandable in a principled way (e.g. AudioSetEvent for the voicekey code (?). Note there are other digital voice keys out there - notably in DMDX - perhaps should check out the literature for algorithms on best way to calculate triggers, etc. In the meantime, I've put the voicekey on the backburner: might be useful if we ever went for audio recording. Client lib & SDK rebuilt with new message structure & within-lab clients also rebuilt. BUGS: ### ClearEventByLine - sent > 1 messages after an error (including misleading 'I killed all'). fixed v 2.3a Bugs fixed on sound code. ### Sound buffers not deleted when a client quits with a claimed audio device. fixed. (Weeeelll. Actually not fixed completely til 2.3.01. Bad loop through vector not spotted) ### sound looping on non-accelerated hardware & poor stereo separation. Fixed (specificying primary buffer removes 1st bug; 2nd due to a spurious request for 3d control of buffer) Changes to the way that display views are setup & updated. New behaviour : drawing is clipped appropriately for diplay docs (bye bye annoying flash) one view of a document is drawn smoothly (invalidated rectangle only is redrawn). This view will be that of the last display device which was asked to show the document. That's been tested & I can't find any problems. Feel it was worth doing before Newcastle job as this will be the first end user program to thrash the display code around. Fingers crossed. ### outstanding bug ?? Memory leaking a single string (19bytes) which contains "PC27" at the RH end. Presumably allocated on the heap during setup (looks suspiciously like something that an Amplicon board might say) but never deleted. Can't be bothered to track it today. ======================================= Version 2.3a distributed to Copenhagen (& Newcastle, though not yet commissioned & they may get a later version) ======================================= v2.3.01 15-01-02 changes now logged in version.h. New version numbering New sound functions (AudioLoadTone, AudioSetSoundVolume & looped playing) 2.3a ------------------------------------------------- * As shipped to Copenhagen & Newcastle. (CD from MRFA -> RNC at this point.) * Support added for PCI-272 cards (trivial) 2.3.01 (MRFA) ------------------------------------------------- Version 2.3.01 (15 Jan 2002, MRFA) * Internal version update strategy. Changed code to Zip file for incremental updates. Code changes logged in (1) code (//); (2) version.h; (3) CODE\WhiskerServer\todo.txt * Sound api extended with AudioLoadTone (this makes a sawtooth, sinewave or squarewave of a given frequency); a -loop option added to AudioPlaySound; AudioSetSoundVolume * version numbering changed to major/minor (this is mainly to suit me: I get very bored with setting up DDFs etc.; only major number is used for Reg key). * Changes to NewWhiskerCommandSet.txt (now matches the WhiskerMessages.h - all commands bar Link:) * Changes to WhiskerTestClient resource script (likewise) ##16-01-02 Version numbering changed: added minor_version_number_string New Amplicon libraries to allow PCI272 usage. (Relink) Cosmetic changes to show changes, in CAmpliconDIOboard.cpp & resource (configure Hardward dialog). Added new commands: AudioLoadTone, a "-loop" option to AudioPlaySound, AudioSetSoundVolume Corresponding changes to CClient, CSoundDevice, WhiskerServer classes. Bugfix from previous versions (failure to release DirectSound buffers) 2.3.02 (MRFA) ------------------------------------------------- ## 22-01-02 MRFA - added a new tone type to AudioLoadTone: "tone". This plays a tone similar to the one made by a PC's onboard speaker - similar to sinewave, but more energy (more audible at low freqs). Thanks to Chris at CCL for the form of this! 22 Jan 2002 * added an extra option to the audioloadtone command, to make tones more similar to DOS ones for Animal CANTAB clients. * Updated commands.txt & whiskertestclient 27 Jan 2002 * I tidied up all the installshield projects to point only at existing files, etc. & updated batch files for making distributables. They now all build installable versions as of 2.3.02; [they would install 'whisker\whisker_test_media\test sounds - orchestral', although this folder is excluded from my incremental backups for time/space reasons]. * Testing tomorrow on new Win2K boxes (Pat Di C). * Outstanding point is the orgamisation of the SDK: this can't go into the 'basic installation', the way Clientlib currently does, because we need to have it installable on 'non-server' machines (so they can develop & run clients using the Control). Logically, the clientlibrary & demo clients should go here as well. Have therefore * Moved ClientLib & association library to be a 'part' of the SDK installation. * Moved demo & simple client to part of SDK. 29 Jan 2002 * Installed 2.3.02 on Patti using new Amplicon PCI cards. So far, so good. * All installers work. (changed the SDK to install from CD, not floppy - DOH!]) * While testing noted that some of the clients used the 'direct communication' interface in talking to the server (Send(); SendAfterDelay(); ImmediateQuery(); etc). Instances of these were changed to use the current messages declared in WhiskerMessages.h * Server & Cambridge Lab clients now essentially coherent again. I'll not be touching them for a while, I don't think Difference between ClientLib & SDK? ClientLib is for anything that uses MS C++ style linkage, which can pass a pointer to a CALLBACK function & use a CString& reference. Other than that - nothing, really except for personal preferences. 2.4.00 (beta: untested) ------------------------------------------------- [Different development to 2.3.03 (which is on Snake) and given that it's not completely compatible with previous clients & supports new hardware, decided to call it 2.4.00] 2.4.00 ------------------------------------------------- A multitude of updates... ## 06-02-02 MRFA - changed the port numbering to include the IANA port number. & Allow the MFC to pick a port number for the second port. ## 09-03-02 MRFA - Added class to support Advantech cards - including MINOR changes to DIOCard classes for hardware abstraction (now allow 8 boards of *either* type. Registration details apply for each board separately, so one of each kind would result in config details for both being registered in Board 0 key). ## 12-03-02 MRFA - Configuration & UI changes for communications (option to manually configure) - bugfix: disabled touchscreen options on standard version. - some changes attempted to tidy up ~WhiskerServer() a bit. ## 26-03-02 MRFA - configuration dialog for advantech cards. - updating of config to allow registration of advantech settings. - overhaul of (horrible) registry i/o code ## 29-05-02 MRFA First return to the code for a while, - Added 'attenuation' code (for each logical soundline) ## 14-07-02 MRFA - Added a 'programmers edition' build target. Disables touchscreens & 'real' io. - Disabled testing on 'claimed' sound devices. Not done for video devices yet (too complicated) - Minor bugfix on calls to CClient::ReportFailedClaim. Now doesn't report a nonsense line number if the claimed *name* is not in the DDF. - Initial testing on a combined Advantech/Amplicon system successful. - Change to the clientmessage protocol. A number of -1 broadcasts to all *other* clients ## 22-07-02 bugfix - Stupid behaviour of CClient::Line_ReadState (which I think I introduced) stopped - (it happily returned 'off' when couldnt read a line) 2.4.01 ------------------------------------------------- ## BUG: Installer for 2.4.00 does not install ADSAPI32.DLL; this is run-time linked from ADSAPI32.LIB. added 2 new commands "DisplayDeleteAllDocuments" and DisplayDeleteAllDevices. Minor changes to single delete commands for added safety... conditional compilation changes throughout. Tried to keep code for specific versions (e.g. touch processing) unique to those builds. New compiler directive (WHISKER_FULL) for tscreen target. ## 17-08-02 Disabled testing on video devices. Not complicated at all if done in the right place (sheepish grin). Added time to touchscreen WM messages. Should allow better timing of touches. Added facility to display touches on server's view of a display. Made mouse & touchevents distinct. Option for debugging mouse input to produce touch, rather than mouse, messages. 2.4.02 ------------------------------------------------- ## 05-09-02 Pissed around with help all day. Now does basic html help, so can ship with latest help. Just noticed that a memory leak claims to have arrived (strcore.cpp, in the allocation). Suspicious of these: allocation is different during DEBUG builds (release builds dont complain, but does the debugger check??). Ah yes - relinking gets rid of it. 06-09-02 Cleared up behaviour following F1 on display screen. 2.4.03 ------------------------------------------------- ## 20-09-02 MRFA: RNC reported bugs in display: 1] failure to delete client displays (until client exits) 2] some objects failing to invalidate themselves correctly on primary (optimised) display. [2] fixed by changing CObject->ObjectExtent. TODO: update help to show the changed 'rules' on which shapes can receive touches. ## 23-09-02 MRFA: [1] fixed: CClient::Housekeeping() was never called. Now called by GUI on housekeeping cycles. subtle change to media search behaviour: now searches for filename: 1) in media dir (as absolute) 2) **NEW** in media dir (relative to default) 3) in default [there's a case for leaving this out when media dir is non-empty, but could be useful] 4) filename (as absolute) Help updated for both changes ------------------ Belatedly realised that I built this without updating the version info: Build2_4_03 230902 mistakenly believes it is 2.4.02 throughout. As we never distributed a 2.4.02 have let this lie... ------------------ 2.4.1 (MRFA) ------------------------------------------------- 2 new display commands Disabled testing of claimed a/v devices server's view of touches 2.5 RNC (Oct 2002) ------------------------------------------------- - ICS card support - split overly generic MAX_BOARDS into MAX_AMPLICON_BOARDS, MAX_... etc. - new registry version (so now v2.5) - logo :-) 2.5.01 MRFA (Oct 2002) ------------------------------------------------- - Cards now have selectable 'reverse inputs' and 'reverse outputs' - Note: as it stands WhiskerReset will not respect this... 2.5.02 MRFA (Nov 2002) ------------------------------------------------- - New build targets for UPDD version 2.x & 3.x 2.5.03 RNC (Dec 2002) ------------------------------------------------- - minor bugfix re fake I/O dialogue box - changed HTML help to separate process - given to Lawrence at the Innes, therefore given a version number... ... though this had an annoying "help" debugging dialogue box... 2.5.04 RNC (Jan 2003) ------------------------------------------------- - changed anti-starvation code. Was waiting for one millisecond... ::WaitForSingleObject(m_hStarvationEvent,STARVATION_PAUSE) ... but this was variable (it's under the operating system's control) So now uses a high-performance timer and yields if a poll takes more than a defined fraction of a millisecond. 2.6.0 MRFA (Jan 2003) ------------------------------------------------- - minor bugfix on fake IO line reversal - changed behaviour of CommLog view to 'snapshot' - changed timer behaviour to force values of > 1msec - added 'Cacheing' of display document changes 2.6.01 MRFA (Mar 2003) ------------------------------------------------- - bugfix on (v 2.6.00 DisplaySetEvent: returned false after succeeding) 2.6.02 RNC (27-Mar-2003) ------------------------------------------------- - bugfix: fake input lines always appeared on if there were no fake output lines 2.6.03 RNC (4-Apr-2003) ------------------------------------------------- - changed timer behaviour to allow 0ms requests again! As I understand it, the reason not to allow them is in case you do it accidentally. The reason to allow it is that it can simplify program coding in non-time-critical sections, i.e. you can call TimerSetEvent(time,event...) and event will always happen (even if time=0 and it happens near-immediately). 2.6.04 MRFA (2-May-2003) ------------------------------------------------- - Change to Programmers Edition (now does not use MM Timers) to make it more well behaved under slow 9x shells. - Bugfix: If USEMMTIMERS was #undefined, then code since 2.3 did not start the WMTIMER codes (as WhiskerServer::m_bUseMMTimer was not set to false until *after* the initial update of CMainFrame). My fault - but never cropped up before as always defined. Fixed (although a whole slew of redundancy remains - all the bMMTimer params / members just reflect the value of the #define...) 2.6.05 MRFA/RNC (9-May-2003) ------------------------------------------------- - Slight server BUG while demonstrating to Mark Baxter... Timers took it up the ARSE. QueryPerformanceCounter returns 64-bit (unsigned) number... into signed structure... so WhiskerServer::Poll() probably yielded much too much... :-) Some changes made. Uses low 32 bits only. 2.6.06 RNC (16-May-2003) ------------------------------------------------- - Cosmetic changes to poll status display to make it easier to notice yields. Not absolutely convinced that the above was the cause of the problem... This sort of code doesn't seem to fail: LARGE_INTEGER one; LARGE_INTEGER two; one.QuadPart = 0; two.QuadPart = 3; do { LONGLONG liOne = one.QuadPart; LONGLONG liTwo = two.QuadPart; if (liTwo - liOne != 3 || liTwo <= liOne) cerr << "FAILURE "; --one.QuadPart; --two.QuadPart; } while (one.QuadPart != 0); 2.6.07 RNC (22 May 2003) ------------------------------------------------- - AudioSilenceAllDevices (new command) - DisplayDeleteAllDocuments - oh, this was already here (MRFA, v2.4.01)! - AudioGetSoundLength - clients can ask the server how long a WAV file is. (See DSBUFFERDESC, WAVEFORMATEX. In CSoundBuffer::m_dwLengthInMs.) - DisplaySetBackgroundEvent, DisplayClearBackgroundEvent - touch-sensitive background - Not much easy prospect of fixing the distorted "show touches" crosshair since that would require scaling it independently of the document scaling; too complex to bother. - Since "show detected touches" and "Mouse input mimics a touchscreen" are not per-display settings, have moved them in the menu. - Unclaimed devices didn't show detected touches; fixed. - If you're looking at a server view with detected touches on, and you switch off detected touches, it didn't update. Fixed. (CDisplayView::ShowTouches; CDisplayView::OnUpdate, HINT_DISPLAY_REMOVEBLACKDOCTOUCHCROSSHAIR...) - comm log refresh button (N.B. MRFA's previous don't-update-on-the-fly code is done in CWSClientCommLogView::GetLogSize().) - Find memory leaks... using _CrtSetBreakAlloc()... see CWhiskerServerApp::InitInstance()... 4 are from Touchbase library, and we can't do much about that. Specifically, these are they: Dumping objects -> {570} normal block at 0x00F04760, 16384 bytes long. Data: 68 46 F0 00 00 00 00 00 00 00 00 00 00 00 00 00 {566} normal block at 0x00F046B0, 39 bytes long. Data: <{78693642-3BED-1> 7B 37 38 36 39 33 36 34 32 2D 33 42 45 44 2D 31 {565} normal block at 0x00F04668, 12 bytes long. Data: <& ' F > 26 00 27 00 B0 46 F0 00 00 00 01 CD {562} normal block at 0x00F04550, 24 bytes long. Data: 58 25 14 00 FF FF FF FF 00 00 00 00 00 00 00 00 The other three all emanate from WhiskerServer::InitAddDioBoards()... I've re-jigged the code here to new/delete with no intervening code. That did no good. We're still left with the above, plus {407} normal block at 0x00E2D158, 64 bytes long. Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 // _CrtSetBreakAlloc(407); // m_vbOutputStates.push_back(false); in CFakeDIOboard::CFakeDIOboard(); from WhiskerServer::InitAddDioBoards() strcore.cpp(118) : {354} normal block at 0x00E2C3A8, 19 bytes long. Data: < PCI2> 01 00 00 00 06 00 00 00 06 00 00 00 50 43 49 32 // _CrtSetBreakAlloc(354); // m_strBoardType = "PCI224"; in CAmpliconDIOboard::InitBoard(int xMode, int yMode, int zMode); ultimately from WhiskerServer::InitAddDioBoards() strcore.cpp(118) : {340} normal block at 0x00E2AB78, 19 bytes long. Data: < PCI2> 01 00 00 00 06 00 00 00 06 00 00 00 50 43 49 32 // _CrtSetBreakAlloc(340); // m_strBoardType = "PCI230"; in CAmpliconDIOboard::InitBoard(int xMode, int yMode, int zMode); ultimately from WhiskerServer::InitAddDioBoards() Aha. Now I understand. This was my bug; CDIOboard::~CDIOboard() wasn't virtual - so derived destructors weren't being called. And it should have been (see C++ FAQ 98). Yes; that got rid of all memory leaks apart from the UPDD/Touchbase ones. - Intermittent BUG: touch -> crash (appears that GUI unresponsive and timers not firing; deadlock?) Seen in v2.6.03, v2.6.06 Detected touches problem too (failed with JG here...) RNC 26-May-2003 Analysis of a crash caught in the debugger. Was running MonkeyCantab/SWM task; Whisker v2.6.07 (in progress). Plenty of finger-wiggling in touchscreen. In retrospect, all the crashes I've seen have been with touchscreen activity, I think... Threads: ID suspend priority location 1 00000364 0 9 2 000004c0 0 10 3 0000079c 0 15 4 000009f8 0 11 CMclAutoLock::CMclAutoLock 5 00000a30 0 15 6 00000a40 0 9 7 00000a58 0 15 8 00000a78 0 9 CMclAutoLock::CMclAutoLock 9 00000a80 0 9 CMclAutoLock::CMclAutoLock So threads 4,8,9 are potentially deadlocked. THREAD 4 CALL STACK (in forward order) -------------------------------------- void _stdcall TouchscreenCallbackFunc(unsigned long context, _PointerData* data) void CUPDDTouchscreen::TouchscreenEvent(int message, const CPoint& point) void WhiskerServer::Thread_TouchEvent(int iServerDisplayDevice, int message, const CPoint& point) --> CMclAutoLock autoLock(m_TouchQueueCritSec); CAN'T HAVE IT BECAUSE THREAD 9 HAS IT. CMclAutoLock::CMclAutoLock( CMclCritSec & rCMclCritSec) THREAD 8 CALL STACK (in forward order) -------------------------------------- void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam) void CImmediateSocket::OnReceive(int nErrorCode) --> CMclAutoLock autoLock(m_CritSec); void CClient::OnReceive(CString strMsg, bool bImmediate) void CClient::Parse(CString strMsg, bool bImmediate) void CClient::Display_CacheChanges(CString &strParameters, bool bImmediate) --> CMclAutoLock autoLock(m_DisplayDocVectorGuard); void CDisplayDocument::CacheChanges() --> CMclAutoLock autoLock(m_ObjectVectorGuard); CAN'T HAVE IT BECAUSE THREAD 9 HAS IT. --> CMclAutoLock autoLockCache(m_ObjectCacheVectorGuard); THREAD 9 CALL STACK (in forward order) -------------------------------------- LPARAM CMainFrame::OnTouchesInQueue(WPARAM wParam, LPARAM lParam) void WhiskerServer::Thread_ProcessTouches() --> CThreadAutoLock autoLock1(g_DisplayGuard); // read --> CMclAutoLock autoLock2(m_TouchQueueCritSec); // ~~~ WhiskerServer thread-safing void CDisplayDevice::TouchscreenEvent(int message, const CPoint& point, long lTime) void CDisplayView::TouchscreenEvent(int message, const CPoint& point, long lTime) void CDisplayView::IncomingMouseInput(UINT message, UINT nFlags, const CPoint &point, long lTime) void CDisplayDocument::ProcessMouseEvent(UINT message, CPoint cursorPos, long lTime) --> CMclAutoLock autoLock(m_ObjectVectorGuard); void CDisplayDocument::Thread_UpdateWhiskerViews(int iHint) void CClient::Display_Thread_UpdateAllViews_DisplayDoc(int iHint, CDisplayDocument* pDisplayDoc) int CClient::Display_GetDocNum(CDisplayDocument* pDocument) --> CMclAutoLock autoLock(m_DisplayDocVectorGuard); CAN'T HAVE IT BECAUSE THREAD 8 HAS IT. CMclAutoLock::CMclAutoLock( CMclCritSec & rCMclCritSec) ~~~~~~~~~~~~~~~~~~~~~~~~~ So threads 8 and 9 are deadlocked, and thread 4 is waiting for that deadlock to be broken. Thread 8: client locks its document vector, asks document to cache changes; documents locks its object vector Thread 9: document receives touch event; passes update request via client, which locks its document vector. The conceptual error is that Thread 9 doesn't need to retain the lock on its object vector at the point it requests Thread_UpdateWhiskerViews. So the place to fix this is in CDisplayDocument::ProcessMouseEvent. Though they're not obviously necessary, I've removed a few other chances for this sort of thing to occur too. Fix 1 - altered CDisplayDocument::ProcessMouseEvent so as not to retain m_ObjectVectorGuard Fix 2 - similar change to CDisplayDocument::DeleteObject, including stashing refresh rectangles in a temporary vector Fix 3 - tidied up CClient slightly by adding CClient::SendTouchscreenEvent and making a few more things const Fix 4 - CDisplayDocument::BringToFront (as per CDisplayDocument::DeleteObject) Fix 5 - CDisplayDocument::SendToBack - Extended MRFA's "don't refresh the logs while I'm reading them" technique to client event log - Version 2.6.07 frozen and distributed to Innes. 2.6.08 RNC (6 June 2003) ------------------------------------------------- - BUG. Dan experienced server lock-up (or lock-up for long enough that a couple of clients dropped off through ?timeouts) when choosing to view what was probably a v. long client log on the server. - Replicated with LogThrashClient - which revealed an additional bug - that a client doing enough communicating with the server can freeze out the GUI thread, and the MMtimer thread, so probably all threads. [Second part not true - see below.] - Thought about modify ResetLogSize() etc. so the log copies the entire vector from the client at once upon a full refresh (then it doesn't need to disturb the client at other times). However, this lengthens the refresh time of a 100,000-size log from 65 to ~110 sec (server at normal process priority) (presumably because only visible entries are refreshed in the one-by-one system). - Limit log size to WHISKERCLIENT_MAXLOGENTRIES entries (currently 2000). This necessitated changing the log from a vector to a deque, to allow use of pop_front. This took the refresh time down to ~0.5 sec. - However, this means that the display can get jumbled (since all the items on the view can change simultaneously)... So perhaps we are better off doing a whole-vector copy now we've limited the log size. Yup. That works well. - Implemented for client event logs, too. - Add thread-yielding code to all thread entry points (e.g. at CClientSocket::OnReceive)? Actually, this probably isn't the problem... Although LogThrashClient, which communicates with the server at its maximum possible rate, was capable of locking up the server when the server was at normal process priority, it doesn't do this when the server's at real-time priority. So the problem isn't that the server's client socket threads are screwing up the MMtimer thread, but that the client process itself was doing this. - CONSIDER ALSO ensuring client TCP stack timeout is fairly long by default in WhiskerClientLib... if there is one? - Test display altered to ensure it fits within a 640x480 display easily. Note that virtual displays do not allow test patterns these days (CDisplayDevice::ShowTestPattern refuses, as all virtual displays are claimed displays, and we don't show test patterns on claimed displays). - Events added to client event logs! - BORDERS. Use central region of monitor, not whole thing... ... "X% border" configured per display device in Server -> Configure hardware -> Display devices ... best way is to modify client rectangle somehow so GetClientRect() returns valid bit? ... implemented CDisplayView::GetUsableClientRect... ... but actually is very hard - to do scrolling properly, all sorts of things need to be rewritten, because so many of the window classes rely on GetClientRect (and on a zero origin for it)... ... better option is to create a second window owned by CDisplayDevice? ... no... better still is to call CFrameWnd::NegotiateBorderSpace... CDisplayFrameWnd::NegotiateBorders... The only trick then is to ensure the window outside the border gets updated... ... by overriding CDisplayFrameWnd::OnPaint... this is starting to look workable! ... then the last thing is to change (reduce) the reported size of the window... this happens when CDisplayDevice::InitializeWindow sets m_iSizeX, m_iSizeY ... success! - Encrypted edition of server // _CrtSetBreakAlloc(3254); // memory leak in CryptoPP library (just the one; it uses managed pointers...) - File logging - Rejigging of thread priorities: MMtimer thread boosted to TIMECRITICAL (CMMTimerWhisker::timerProc) and signing thread drops process and thread priorities temporarily (CFileCache::ThrottleDown). Not perfect; see discussion in CFileCache::ThrottleDown comments. - IF NECESSARY, spawn new (normal-priority) process to sign digital logs (MRFA's suggestion, rather better than my approach!). ... only way is CreateProcess(), inheriting the relevant file handle ... but CreateProcess() has an absolute requirement of passing a filename to execute... ... there are nasty security implications if the digital signing code lives in a separate executable; anybody could sign anything... unless we have some special "entitlement" encryption security between the two... even then, it gets a little dubious; keys are easier to find, too... and what happens if that program gets deleted? ... there are the _spawn() functions... which create a new process and then call _exec() functions... ... ideally, you'd just fork()... ... there's no way to fork() in Windows... http://www.geeksalad.org/odds/fork/all.shtml ... the POSIX module of WinNT supports fork(), but supports pretty much nothing else from WinNT so you can't mix it with native NT code ... if you ran CreateProcess() and established interprocess communication with the new process for authentication, the high-priority thread might block waiting for the low-priority thread to respond... unless the communication was one-way... (high -> low) ... http://flinflon.brandonu.ca/Dueck/2002/62306/Processes/ProcessCreation.htm ... http://www.byte.com/art/9410/sec14/art3.htm ... http://jan.netcomp.monash.edu.au/ssw/processes/win32.html (on inheriting file handles a different way) ... so sequence is (1) establish filename of the running copy of whiskerserver.exe (this can't have been moved, as it's executing) (2) CreateProcess(); a low-priority process; whiskerserver.exe -sign [RANDOMAUTHCODE] [SIGNEDRANDOMAUTHCODE] where signing is done with "private" key ... bInheritHandles true ... STARTUP_INFO contains open file handle as standard input handle, or command line passes value of open handle (http://jan.netcomp.monash.edu.au/ssw/processes/win32.html) (3) recipient process checks authentication, signs file, exits (4) original process never waits for child processes to complete - Problem discovered on v2.6.07 (Olivia's touchscreen computers, walnut/cashew), ~10 July 2003 Server crashes upon exit if user is not an administrator? Failure comes *after* server has exited, so Dr Watson won't attach, or so it claims... Can't easily replicate this with 2.6.08 debug build... or 2.6.08 release... Ah. Replicated. Access violation; extern "C" void CALLBACK internalTimerProc(UINT id, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2) { ... timer->timerProc(); } I presume that when WhiskerServer::~WhiskerServer() calls "delete m_pMMTimer", and that calls CMMTimer::stopTimer(), then a poll is still outstanding and in progress in another thread. When that eventually executes, the object has gone. Not an easy one to fix, given that internalTimerProc can't have access to anything except global objects without causing exactly this problem... Options: (1) stop the MM timer a short while before m_pMMTimer is deleted; (2) have internalTimerProc bin out if... if what? If one of the many potential co-existing MMTimers has been deleted? That doesn't make much sense. (3) have CMMTimer::stopTimer() wait until it's sure that no more calls are outstanding? How? timeKillEvent() doesn't seem to cooperate... Option 1 chosen - timer stopped at beginning of WhiskerServer::~WhiskerServer() but deleted later. Nasty hack really, but seems to work. - ANALOGUE DEVELOPMENT. - CAmpliconDIOBoard rewritten and optimized (lookup tables; junked superfluous code). Now handles digital I/O boards with <3 chips (e.g. PCI230). - Successfully says hi to analogue boards and counts DACs/ADCs. - CAnalogueLine, CWSServerAnalogueView, CWSClientAnalogueView. - Possible problem (not yet replicated): mouse emulating touch facility didn't work on walnut, ~10 July 2003 - Default to having "show touches" and "mouse emulates touchscreen if mouse input enabled" on. - hostname/IP address on test displays - Bug discovered in 2.6.07: failure to update multimedia display (or server's copy) at times. Also seen in 2.6.08. Seems to trigger mostly when the window is exposed by minimizing something else, i.e. when it doesn't have the focus. ... when it occurs (e.g. after running a 10-trial RatBat PAL session -> link -> N-pair - happened at start of NPair) neither View/Refresh or View/Refresh All works. So this combination... is it that the view's lost its document? Well, no... because if you drag another window over a view, it refreshes... ... so has the view been lost from the list of views the document thinks it has? (i.e. has the document lost the view?) Yet when this document is removed, things happen (but it stays screwed when a new document comes on)... N.B. this is a *reliable* way to generate the bug. displayclaim 0 displaycreatedocument bob displayaddobject bob sqaure rectangle 100 100 200 200 displaycreatedocument bill displayaddobject bill square rectangle 400 400 500 500 displayshowdocument 0 bill displaydeletealldocuments - found one bug: this didn't update the display... displaycreatedocument fish displayshowdocument 0 fish - this did blank the display displaysetbackgroundcolour fish 0 0 255 - but now things are screwed. So certainly a problem with DisplayDeleteAllDocuments. Well, more than that; the same thing happens with DisplayDeleteDocument. All these functions do is: Display_Thread_UpdateAllViews_DisplayDoc(HINT_DOCUMENT_DOCDELETED, pDoc); ... remarkably, HINT_DOCUMENT_DOCDELETED is only processed by CWSClientDocumentView and CWSLeftView, which don't do a lot... pDoc->FlagForDeletion(); ... which just sets a flag that can be queried with FlaggedForDeletion... And yet I'm sure that DisplayDeleteDocument removed documents when I first wrote it! I remember checking! DisplayBlank works... So tried adding Display_MakeDocumentsSafeForDeletion(); to CClient::WorkerThread_Housekeeping(). ... which promptly asserted, 'cos it's meant to be called from the GUI thread... ... so tried it in CClient::Housekeeping() instead... [+++CHANGE - BUT REVERTED BELOW+++] Seems to work... in that things vanish from the screen when they're deleted... ... but still screws up subsequent updates... displayclaim 0;displaycreatedocument bob;displayshowdocument 0 bob;displaydeletedocument bob;displaycreatedocument bob;displayshowdocument 0 bob;displaysetbackgroundcolour bob 0 0 255;displaysetbackgroundcolour bob 0 255 0 ... This goes green if sent as one command, but if you delay the last setbackgroundcolour it stays blue... displayclaim 0;displaycreatedocument bob;displayshowdocument 0 bob;displayblank 0;displayshowdocument 0 bob;displaysetbackgroundcolour bob 0 0 255;displaysetbackgroundcolour bob 0 255 0 ... No problems. It clearly is the deletion that screws up. Added m_pWhisker->Display_DocumentDying(this, pDoc); // added 6 Aug 2003 RNC to CClient::Display_DeleteDocument and CClient::Display_DeleteAllDocuments [+++CHANGE+++] Still doesn't bloody work! But the document still knows which views are looking at it (GetFirstViewPosition and so forth)... +++ Ah. Even this screws it up: create show ... play around (works fine) blank delete create show ... is now screwed. +++ Even a deletion of a document that has *never* been shown screws things up. Commenting out the actual document deletion bit from CClient::WorkerThread_Housekeeping() makes it work again! (Though it leaves a document with FlaggedForDeletion(), which rather upsets some of the mundane WSLeftView views, no doubt thanks to CClient::Display_MakeDocumentsSafeForDeletion() being called by CClient::Housekeeping() repetitively, since that then calls WhiskerServer::Display_DocumentDying() which calls m_pLeftView->RefreshAndReturn(). But why the screw-up? The offending code in CClient::WorkerThread_Housekeeping() is { CMclAutoLock autoLock(m_DisplayDocVectorGuard); vector::iterator docit = m_vpDisplayDocs.end(); while (docit != m_vpDisplayDocs.begin()) { --docit; if ((*docit)->FlaggedForDeletion() && !(*docit)->GetFirstViewPosition()) { delete (*docit); // this has the potential to crash, which is why the document // creation process ensures that bAutoDelete is false for these documents. m_vpDisplayDocs.erase(docit); } } } [that reference being to pDoc->m_bAutoDelete = FALSE; // see CClient::~CClient() in CClient::Display_CreateDocument] ... so the likely culprit is delete (*docit); CDisplayDocument::~CDisplayDocument() looks harmless. So the problem's probably here: CDocument::~CDocument() { // do not call DeleteContents here ! #ifdef _DEBUG if (IsModified()) TRACE0("Warning: destroying an unsaved document.\n"); #endif // there should be no views left! DisconnectViews(); ASSERT(m_viewList.IsEmpty()); if (m_pDocTemplate != NULL) m_pDocTemplate->RemoveDocument(this); ASSERT(m_pDocTemplate == NULL); // must be detached } void CDocument::DisconnectViews() { while (!m_viewList.IsEmpty()) { CView* pView = (CView*)m_viewList.RemoveHead(); ASSERT_VALID(pView); ASSERT_KINDOF(CView, pView); pView->m_pDocument = NULL; } } Still can't find it... FOUND IT. Following a deletion, this fails (in CMainFrame::OnUpdateAllViews()): case HINT_DOCUMENT_CHANGED_PARTIAL: case HINT_DOCUMENT_CHANGED_ALL: case HINT_DOCUMENT_TOUCHED: case HINT_DOCUMENT_REMOVETOUCHCROSSHAIR: { CClient* pClient = pDoc->View_GetClient(hint.m_iClientNum); if (pClient != NULL) { pClient->Sync_LockDocPtrs(); pDisplayDoc = pClient->Display_View_GetDocPtr(hint.m_iDocNum); ### this returns NULL. In other words, the m_iDocNum thing has screwed up. if (pDisplayDoc != NULL) pDisplayDoc->UpdateAllViews(NULL, hint.m_iHint, &hint); // This function calls the CView::OnUpdate member function for each of the document’s views except the sending view, passing pHint and lHint. pClient->Sync_UnlockDocPtrs(); } break; } It's because... CDisplayDocument* CClient::Display_View_GetDocPtr(int iDocument) { ... if ((*it)->View_GetDocNum() == iDocument && !(*it)->FlaggedForDeletion()) return (*it); } int CDisplayDocument::View_GetDocNum() { return m_iDocNumber; } ... which got set by pDoc->RegisterOwner(this, strDocName, m_iNextDocNumber); from CClient::Display_CreateDocument ... but... void CDisplayDocument::Thread_UpdateAllViews(bool bUpdateAll, CRect* pRectInvalid) { ... m_pClient->Display_Thread_UpdateAllViews_DisplayDoc((bUpdateAll ? HINT_DOCUMENT_CHANGED_ALL : HINT_DOCUMENT_CHANGED_PARTIAL), this); } void CClient::Display_Thread_UpdateAllViews_DisplayDoc(int iHint, CDisplayDocument* pDisplayDoc) { ... int iDisplayDocNum = Display_GetDocNum(pDisplayDoc); ... m_pWhisker->Thread_UpdateAllViews_DisplayDoc(iHint, m_iClientNumber, iDisplayDocNum); } int CClient::Display_GetDocNum(CDisplayDocument* pDocument) { CMclAutoLock autoLock(m_DisplayDocVectorGuard); vector::iterator it = m_vpDisplayDocs.begin(); int iDocument = 0; for (; it != m_vpDisplayDocs.end(); ++it, ++iDocument) { if ((*it) == pDocument) return iDocument; } return -1; } So which should change? Fairly obvious! CClient::Display_GetDocNum should return pDocument->m_iDocNumber. That's simplest; otherwise we'd have to tell all the documents to renumber when we fiddled with the vector. It's not called by anything else. OK - altered CClient::Display_GetDocNum. [+++CHANGE+++] Fixed! Removed Display_MakeDocumentsSafeForDeletion(); from CClient::Housekeeping() again. Superfluous and just led to too many RefreshAndReturn() calls from WhiskerServer::Display_DocumentDying(), which makes the console display flicker. [+++REVERTED+++] - Problem: cache no longer works properly (e.g. MonkeyCantab SWM task); flickers... Ah, no, silly me; version upgrade had switched off DirectDraw. Fine with it on! - extra "state" column in line views on the far right-hand side - changed "on" indicator to "###" in line view; clearer from a distance - PLAN: Spawn new (normal-priority) process for context-sensitive help, perhaps? Help is still not optimal. I hate the way it stays on top. But not modifiable; only option is to create a new process, and then we'd have to implement interprocess communication (1) to prevent thousands of copies running; (2) to pass "now go to the TOC" messages. Oh, it's a 'mare. Anyway, you can minimize the help. CALLED v2.8 at this point (7 Aug 2003) ------------------------------------------------------- 2.8.01 RNC (26 Aug 2003) ------------------------------------------------- - #ifdef error in WSLeftView.cpp such that standard edition didn't show client summary view 2.8.02 RNC (9 Sep 2003) ------------------------------------------------- - Investigating report by Simon Gow (of Cambridge Cognition) that MonkeyCantab boxes "pulse" their output lines on when the server starts. These devices have a single ICS card. The pellet dispensers pick this up, as do EEG recording equipment. - NOTE THAT THESE DEVICES HAVE REVERSED INPUTS AND OUTPUTS. - Found minor bug in ICSDIOboard.cpp: should have had "m_bInitialized = true" before "WriteControlRegister()", since the latter requires it to be true in order to do anything. (Not that it would have made a huge difference since the server subsequently calls ResetOutputs() from WhiskerServer::InitAddDioBoardSpecifics(), and ResetOutputs() in turn calls WriteControlRegister(). Anyway, fixed. - Sure enough, when WriteControlRegister() runs, it turns on the devices - so it's ignored the "reverse polarity" bit here... but as soon as TalkToChip() runs, it's sorted again. - Tried TalkToChip() with appropriate values before WriteControlRegister()... but it doesn't help (WriteControlRegister resets things again.) - Tried writing everything at once (with a 4-byte simultaneous write). Still doesn't work. - I quote from the ICS card manual: "regardless of the mode... the default at power up is mode 0 for all boards, with all lines set for input. Output ports are initialized to zeros when their control words are written." So not fixable. Their fault (choice of card + reversed output). They later reported that the pulse is of the order of microseconds long, which is consistent. 2.8.03 RNC (2 Mar 2004) ------------------------------------------------- - Sound bug. Tiny fraction of a second of previous sound merges with newly-played sound. Consistent bug. Noticed with MonkeyCantab, which uses AudioLoadWav and AudioLoadTone, and then a series of PlaySoundForDuration calls. In turn, CMonkeyCantabController::PlaySoundForDuration makes AudioPlaySound and (timed) AudioStopSound calls. They end up as CLogicalSoundDevice::PlayBuffer and CLogicalSoundDevice::StopBuffer calls. Reproducible in test client like this: AudioClaim 0 -alias speaker AudioLoadSound speaker one "c:\utopiaasterisk.wav"; AudioLoadSound speaker two "c:\utopiaexclam.wav" AudioPlaySound speaker two; testnetworklatency; AudioStopSound speaker two AudioPlaySound speaker one Doesn't appear to happen with AudioPlayFile. So likely problem is that when a buffer is stopped, it doesn't fully clear. DirectSound calls are m_pDSBuffer->Play(0,0,bLoop ? DSBPLAY_LOOPING : 0) and m_pDSBuffer->Stop(). Problem also occurs when the *same* buffer is stopped and re-played. Is the problem that the secondary buffer is being copied into the primary buffer, or into some temporary storage (e.g. hardware memory), and this isn't being cleared? Certainly, since the problem occurs when secondary buffer A is started, stopped, and secondary buffer B is played, the problem must be with the primary buffer. - Simplest attempt at fix: add m_pDSBuffer->SetCurrentPosition(0) after Stop() call. Had no effect. - Fiddling with DSBPLAY_LOOPING flag for primary buffer had no effect. - Upgraded machine to DirectX 9.0b runtime - no change. - Removing DSBCAPS_STATIC from secondary buffer - no change. - Adding DSBCAPS_LOCSOFTWARE to secondary buffer and primary buffer - no change. - Even happens if you play sound A, stop sound A, delete soundbuffer A, play soundbuffer B - definitely primary buffer problem. - Also happens with sample applications as part of DirectX 9.0 SDK! So it's not just us. - Downgraded SDK from DirectX 9 to that supplied with VC98. - Workaround: play a blank buffer? - WAV format: see http://www.borg.com/~jglatt/tech/wave.htm http://www.technology.niagarac.on.ca/courses/comp630/WavFileFormat.html "Since this is a 8-bit WAV, the sample rate and the bytes/second are the same at 0x00005622 or 22,050 in decimal... In the example above 0x80 would represent "0" or silence on the output since the DAC used to playback samples is a bipolar device (i.e. a value of 0x00 would output a negative voltage and a value of 0xFF would output a positive voltage at the output of the DAC on the sound card)." - Silence in WAV files: http://www.sonicspot.com/guide/wavefiles.html One "point about sample data that may cause some confusion is that when samples are represented with 8-bits, they are specified as unsigned values. All other sample bit-sizes are specified as signed values. For example a 16-bit sample can range from -32,768 to +32,767 with a mid-point (silence) at 0." http://sharkysoft.com/software/java/lava/docs/javadocs/lava/riff/wave/doc-files/riffwave-content.htm - SORTED. Clean sound, perfect mixing. Whew. That was needlessly hard. - But now discovered that it's not just when you stop a sound by hand - happens when the sound stops on its own, too. For example, AudioLoadTone speaker ping 1000 sine AudioLoadTone speaker bong 500 sine AudioPlaySound speaker ping (wait) AudioPlaySound speaker bong -- hear residual of 1kHz tone. (wait) AudioPlaySound speaker bong -- hear residual of 500 Hz tone... etc. (Also get residuals across Windows applications...) So is the correct thing to (a) play the blank sound whenever a sound stops, or (b) have the blank sound played on loop? The former uses less total CPU, so is preferable, but requires notification back from DirectSound when a sound finishes playing... Note that this problem is occurring even though the primary buffer has DSBPLAY_LOOPING set, and so (in theory) should be playing all the time... I can find no method of getting callback information for method (a), so we'll have to go for method (b). That works perfectly. [See CLogicalSoundDevice::InitializeBlankSoundBuffer().] - Also, CLogicalSoundDevice::LoadTone would never have written to the second available chunk of buffer. Fixed. - dialogue box for ICS cards: tick boxes for "reverse inputs"/"reverse outputs" the wrong way round. Turns out not to have been a dialogue box error but the fact that WhiskerServer::InitAddDioBoards() calls pBoard = new CAdvantechDIOboard(this, iBoardDriver,x,ip,op); while that constructor was defined as CAdvantechDIOboard(WhiskerServer* pOwner, int boardnum, long portmodes, bool bReverseOutputs = 0, bool bReverseInputs = 0); Was also confusion between AdvantechDIOboard.h and .cpp. Similar mis-calls of CAmpliconDIOboard and CICSDIOboard constructors. Standardized on "input, output" order everywhere. 2.8.04 RNC (25 Mar 2004) ------------------------------------------------- - bugfix 25 Mar 2003: CDisplayObject_Bitmap::CDisplayObject_Bitmap had error - used x size for y size! - downgraded "warning: no timers to kill called ..." to an Info: message (it's common to kill nonexistent timers and the warnings worry users) 2.9.00 MRFA(25 Mar 2004) ------------------------------------------------- - Added duration option to AudioLoadTone. NB: It could take a LONG time to fill up a very long buffer, and there's really no point as info redundant. People can use looping & timers or edited Wav files if they want control of longer tones. So buffer length constrained to 1-30000 msec. - RNC (1 Apr 2004) Camcog experienced dead inputs (and 0 polls/sec, 1000 yields/sec). That problem again. After much faffing looking at Advantech driver code, found problem and fixed yield code in WhiskerServer::Poll() (I think) by using LARGE_INTEGER arithmetic. The reason: my Pentium 4 @ 2.5 GHz (family 15, model 2, stepping ID 4) has a high-performance counter of 3.5 MHz (period 286 ns). Their Pentium 4 @ 2.0 GHz (family 15, model 2, stepping ID 7) has a high- performance counter of 2 GHz (period 0.5 ns = 500 ps). That'd be it... 2.9.01 RNC (27 Apr 2004) ------------------------------------------------- - 28 April 2004 Problem calibrating touchscreens for displays with "borders" configured. Touchscreen is smaller than physical monitor. Therefore, calibrate UPDD (just need to move calibration marks in a bit first) and touchscreen is calibrated to physical world. But suppose the border is 10% (so the display is 80% of the full width and 80% of the full height); the touches are then scaled inappropriately by a factor of 0.8. Solution: 1. modify CUPDDTouchscreen::SetScaling() to scale to physical, not virtual, screen size. - touchscreen coordinates are now at same scale as target, but may be offset 2. modify CDisplayDevice::TouchscreenEvent() - if there's a border, shift the coordinates up/left (since the display is shifted down/right relative to the physical screen) 2.9.02 RNC (8 May 2004) ------------------------------------------------- - further summary information about documents on summary view - Fixed known issue of 7 Aug 2003, which was this... * Turn on a test display * Have a client connect/claim a display * Have the client show a document (e.g. 1000x750) and scale it to the device * Turn off the test display -> The document won't be scaled; it'll be shown with scrollbars. ... together with another problem (show test pattern - client claims display - can't then remove test pattern) ... by making it impossible for a test pattern to be shown on a claimed display. ... by modifying CDisplayDevice::RegisterClient() to turn off any test pattern - m_bScaleToFitDocument was inappropriately persistent across clients - if you created and then scaled a document v. fast, a blank was shown; this was because CDisplayDevice::ScaleToFitDocument used ShowDocument(m_pCurrentDoc...) which overrode a pending "switch to queued document" - CDisplayDevice::GetQueuedDocPtr() added to ensure that other things check that neither the current nor the queued document is in use by a display device before e.g. deleting one of them 2.9.03 RNC (10 June 2004) ------------------------------------------------- - 10 June 2004. Another timer problem (yields all the time), on my machine. High-performance CPU frequency: 3,579,545 Hz "Last poll took 0 ticks and last interpoll took 9,314,500,000,000 ticks" (approx; the number keeps ticking up by approx. 3,500,000 per second, i.e. by the clock frequency). In debug mode, last interpoll time is similar, though last poll time is now fixed at 14,829,735,431,805,717,965 The core poll code is now: (in WhiskerServer.h) LONGLONG m_llTicksLastPoll; LONGLONG m_llTicksLastInterpoll; (in WhiskerServer::Poll) LARGE_INTEGER liHighPerfTimerCounter_StartOfPoll; static LARGE_INTEGER liHighPerfTimerCounter_EndOfPoll; QueryPerformanceCounter(&liHighPerfTimerCounter_StartOfPoll); m_llTicksLastInterpoll = liHighPerfTimerCounter_StartOfPoll.QuadPart - liHighPerfTimerCounter_EndOfPoll.QuadPart; // (start of this poll) - (end of last) = ticks since end of last poll liMicrosecondsSinceLastPoll.QuadPart = (1000000 * m_llTicksLastInterpoll) / m_liHighPerfTimerFrequencyPerSecond; // don't bracket as 1000000 * (ticks / frequency) - a poll never lasts a second, // so that just gives integer division and rounds to zero. -- BREAKPOINT HERE: liHighPerfTimerCounter_EndOfPoll.QuadPart = 0 (WRONG!) liHighPerfTimerCounter_StartOfPoll.QuadPart = 9,316,038,060,473 m_llTicksLastInterpoll = 9,316,579,594,041 [on a different breakpoint!] m_liHighPerfTimerFrequencyPerSecond = 3,579,545 liMicrosecondsSinceLastPoll SHOULD therefore be (1,000,000 * 9,316,579,594,041) / 3,579,545 = 9.3E18 / 3.6E6 = 2.6E12. Actually... liMicrosecondsSinceLastPoll.QuadPart = -2,550,649,448,370 (WRONG!) ... do poll QueryPerformanceCounter(&liHighPerfTimerCounter_EndOfPoll); m_llTicksLastPoll = liHighPerfTimerCounter_EndOfPoll.QuadPart - liHighPerfTimerCounter_StartOfPoll.QuadPart; -- BREAKPOINT HERE: NEVER ARRIVES. Therefore problem is that the static LARGE_INTEGER liHighPerfTimerCounter_EndOfPoll is initialized to an unspecified value (0, it seems) that is a long way away from the current clock. The calculation of liMicrosecondsSinceLastPoll overflows and gives a negative result. Since this is less than the required minimum interpoll time, we never get a poll. Why does it go negative? It should be unsigned. Definitions: QueryPerformanceCounter returns a LARGE_INTEGER. LARGE_INTEGER.QuadPart is a 64-bit signed integer. LONGLONG is a 64-bit signed integer. ULONGLONG is a 64-bit unsigned integer. Perhaps I should be using ULONGLONGs for m_llTicksLastPoll and m_llTicksLastInterpoll. ... yes; changing the variables defined in WhiskerServer.h to ULONGLONG (and ensuring all are initialized to zero in the WhiskerServer constructor) sorted out the problem. Other variables also changed to ULONGLONG to match. 2.9.03 MRFA(08 July 2004) ------------------------------------------------- - Bug fix. Audioloadtone: tones of < 1000ms would fail, greater would be truncated to multiples of 1000ms only. I'd used integer arithmetic for calculating buffer lengths. . 2.9.04 RNC (15 Oct 2004) ------------------------------------------------- - Colour to mark out pegged lines. - Server warns client if (a) a pegged input sends them a message; (b) they try to manipulate a pegged line. See WSM_EVENTONPEGGEDLINE, WSM_TRYINGTOSETPEGGEDLINE. 2.10.0 MRFA (28 Oct 2004) ------------------------------------------------- re-merging of separate developments. Some stray versions may exist between 2.9.0 and 2.10.0, but not in the wild (only in-lab collaborators). Change to exclusive mode in the CVS? - Bugfix on AudioLoadTone that handles non-PCM format wav files without failing silently. - Added functions to support Serial port devices. We can use the 4 status lines (CD/CTS/DSR/RI) as inputs. Serial ports do not use TTL (GND | -5v), but +3 to +25V and - 3 to -25V relative to GND. The easiest way to use these as inputs is thus shorting to a line we know is low or high. Therefore, we'll force one control line high (= SPACE = 0), and the other low (= MARK = 1) and we allow the user to short their lines to one of these two lines for their input switch. Used in this way the RING line works fine [if you use event driven (we use polled) access, RI has a different logic for its Event bit in status register, relative to CD/CTS/DSR.] The lines are IDed by abbreviation, and pin numbers for a standard 9-pin COM port. OUTPUTS Green = pin4/25 = RTS Blue = pin20/25 = DTR INPUTS Red = pin 8/25 = CD / RLSD Yellow = pin 5/25 = CTS DSR = pin 6/25 RING = pin 22 (funny) -- looking at the response box currently used in ERTS (from Luke) we find: Pin 1 switched to Pin 3 (Data Carrier Detect (RSLD/CD) - Transmit Data (TD)) Pin 1 switched to Pin 2 (RSLD/CD - Receive Data (RD)) OK - so the ERTS button box is *not* using any EIA-232 compliant UART to talk to this device. We can adapt to these with a simple 'switch over' box. 2.10.1 RNC (16 Nov 2004) ------------------------------------------------- - Deadlock found. Six threads not in our code. Three threads in our code: ONE - TOUCHSCREEN THREAD - CALLED FROM TBABI. Didn't know that had its own thread! But that's 'cos I'm thick. THREAD IS FINE. DEADLOCK IS BETWEEN THREADS TWO/THREE. void _stdcall TouchscreenCallbackFunc(unsigned long context, _PointerData* data) void CUPDDTouchscreen::Callback(_PointerData* data) void CUPDDTouchscreen::TouchscreenEvent(int message, const CPoint& point) void WhiskerServer::Thread_TouchEvent(int iServerDisplayDevice, int message, const CPoint& point) ... CMclAutoLock autoLock(m_TouchQueueCritSec); >>> WAITING FOR THREAD THREE TWO - SOCKET THREAD, DEALING WITH CLIENT void CClient::Parse(CString strMsg, bool bImmediate) ... if (strCommand.CompareNoCase(WCM_DISPLAY_SHOWCHANGES)==0) { Display_ShowChanges(strMsg, bImmediate); return; } void CClient::Display_ShowChanges(CString &strParameters, bool bImmediate) ... CMclAutoLock autoLock(m_DisplayDocVectorGuard); ### 2A void CDisplayDocument::ShowChanges() void CDisplayDocument::Thread_UpdateWhiskerViews(int iHint) ... m_pClient->Display_Thread_UpdateAllViews_DisplayDoc(iHint, this); ### 2B - WAITING FOR m_pClient ... template T* CSyncAccess::operator -> () { // in SmartPtr.h ... m_rep->acquireAccess(); ~~~ MAY BE HERE THAT IT'S ACTUALLY STUCK? WAITING FOR THREAD THREE. m_acquired = true; THREE - GUI THREAD (because it's dealing with the touchscreen incoming events) void WhiskerServer::Thread_ProcessTouches() ... CThreadAutoLock autoLock1(g_DisplayGuard); // read CMclAutoLock autoLock2(m_TouchQueueCritSec); // ~~~ WhiskerServer thread-safing void CDisplayDevice::TouchscreenEvent(int message, const CPoint& point, long lTime) void CDisplayView::TouchscreenEvent(int message, const CPoint& point, long lTime) void CDisplayView::IncomingMouseInput(UINT message, UINT nFlags, const CPoint &point, long lTime) void CDisplayDocument::ProcessMouseEvent(UINT message, CPoint cursorPos, long lTime) void CDisplayDocument::Thread_UpdateWhiskerViews(int iHint) ... m_pClient->Display_Thread_UpdateAllViews_DisplayDoc(iHint, this); ### 3B - LOCKING THE CLIENT SMARTPTR void CClient::Display_Thread_UpdateAllViews_DisplayDoc(int iHint, CDisplayDocument* pDisplayDoc) ... int CClient::Display_GetDocNum(CDisplayDocument* pDocument) { // Scene of a ghastly bug, traced 6 Aug 2003. CMclAutoLock autoLock(m_DisplayDocVectorGuard); ### 3A >>> WAITING FOR THREAD TWO. SUMMARY: socket thread -> CClient::DisplayShowChanges() -> locks CClient::m_DisplayDocVectorGuard [A] -> CDisplayDocument::ShowChanges() -> calls client through CDisplayDocument::m_pClient [B] GUI thread -> touchscreen -> CDisplayDocument -> calls client through CDisplayDocument::m_pClient [B] -> locks CClient::m_DisplayDocVectorGuard [A] SO THE PROBLEM IS THAT CDisplayDocument has a SmartPtr to m_pClient... not a normal pointer... SyncPtrs are used for (1) sockets [not intrinsically thread-safed] (2) the one CMMTimerWhisker [only one thread of it...] (3) CDisplayDocument::m_pClient -- why? CClient does its own thread-safing? (4) CDisplayView::m_pClient -- why? CClient does its own thread-safing? Action: change (3) and (4) to simple CClient*. ... seems to work... ... but risk is of reading m_pClient while another thread is halfway through writing it... ... only thing that writes CDisplayDocument::m_pClient is CDisplayDocument::RegisterOwner, and that's only called by CClient::Display_CreateDocument, just after it creates that document. ... only thing that writes CDisplayView::m_pClient is CDisplayView::OnInitialUpdate, which is called via the MFC framework from the view's document (via CDocument). ... so I think it's safe to use un-thread-safed pointers here. (Alternative would be to have atomic access for pointer reading/writing without locking across pointer dereferencing with ->. CAtomic doesn't support this but it would be simple to code by hand with a critical section shared by GetClientPtr() and SetClientPtr() functions that were the only ones to touch m_pClient directly - but there seems to be no need in this case.) (The only other issue is when the client is deleted - but that situation is already handled by other code.) 2.10.2 RNC (9 Dec 2004) ------------------------------------------------- * bugfix in CDisplayObject_Line::ObjectExtent(), q.v. * DisplayGetDocumentSize command added * DisplayGetObjectExtent command added * help updated (including some out-of-date error message documentation) * internal references (and help references) changed from DisplayQuerySize to DisplayGetSize 2.11.0 RNC (14 Apr - 17 May 2005) ------------------------------------------------- - shift "configure hardware" menu to its own item on the main menu bar - option to free all lines simultaneously - fake audio devices - continuous-loop audio device testing - server sends a warning whenever pegging/freeing a line generates an event - CSoftwareLine::CheckForChange() - tidied up registry access routines in CWhiskerUserConfig (to match CWhiskerHardwareConfig) - If server can't find current version details in registry, it scans through a (predefined) list of previous versions, to retrieve its details. (People were getting fed up with the redo-registry-settings-on-upgrade problem.) - ABET hardware support for Campden Campden stuff called "Behavioural net controller (BNC)". Mark 1, and probably Mark 2, use Advantech cards. Two computers: one runs hardware and tasks using a real-time OS; the other talks to it. Lafayette hardware ("ABET") uses National Instruments card (ultimately 82C55), multiplexed. - NI = National Instruments - PCI-DIO-96 manual = http://www.ni.com/pdf/manuals/320938c.pdf - Running the "Measurement & Automation Explorer" (shortcut installed on desktop) communicates/tests the card - Having installed NI drivers, see \Program Files\National Instruments\NI-DAQ\readme.html - ? Will be using the NI-DAQmx API (there's a choice of API to use: alternative is Traditional NI-DAQ). ... but I suspect (based on the string-addressing scheme used in NI-DAQmx, and other things in the readme) that this requires other NI software to be installed... best with the simpler Traditional version? (see e.g. DAQmxLoadTask... tasks configured in other software, namely MAX...) ... also, the newer software appears to default to queuing up data in channels for the user to read (see e.g. DAQmxReadDigitalU32); suspect the Traditional one will work better with the Whisker polling system. - NI-DAQmx library appears to be in "NI-DAQ\DAQmx ANSI C Dev" (NIDAQmx.h; NIDAQmx.lib) - NI-DAQmx example code in "NI-DAQ\Examples\DAQmx ANSI C" - Traditional NI-DAQ library in NI-DAQ\Lib (nidaq32.lib) and NI-DAQ\Include (nidaq.h; also regdefs.h, nidaqcns.h, nidaqerr.h) - Traditional NI-DAQ examples in NI-DAQ\Examples\VisualC - In the NI-DAQ\Docs directory, see particularly - cdaqmx.chm = NI-DAQmx C Reference Help (full function reference for NI-DAQmx, linking other help files) - nidaqpc.chm = Traditional NI-DAQ function reference help ... Traditional NI-DAQ Functions ... Listed by hardware product ... Traditional NI-DAQ functions listed by hardware product ... DIO-24 and DIO-96 device function list Config_DAQ_Event_Message DIG_Block_Check DIG_Block_Clear DIG_Block_In DIG_Block_Out DIG_In_Line DIG_In_Prt * DIG_Out_Line DIG_Out_Prt * DIG_Prt_Config * DIG_Prt_Status DIG_SCAN_Setup Get_DAQ_Device_Info Get_NI_DAQ_Version Init_DA_Brds * - ABET hardware addressing scheme (based on info from Vern Davidson, Lafayette, 9 Mar 2005): The card is a 96-way card. There are up to 16 chambers, each with up to 16 inputs and 32 outputs - total 768 devices. The devices are multiplexed by the Lafayette interface. On the PCI-DIO-96: RNC: Address outputs (4 bits): APA<3:0> Port 0.3 to port 0.0 - output Output enable (1 bit): APA<4> Port 0.4 - output Service request inputs: DPA<0:7>(chambers 1-8) Port 9 - input DPB<0:7>(chambers 9-16) Port 10 - input Data inputs: CPA<0:7>(inputs 1-8) Port 6 - input CPB<0:7>(inputs 9-16) Port 7 - input Data outputs: APB<0:7>(outputs 1-8) Port 1 - output APC<0:7>(outputs 9-16) Port 2 - output BPA<0:7>(outputs 17-24) Port 3 - output BPB<0:7>(outputs 25-32) Port 4 - output BPC (port 5) not used, it seems CPC (port 8) not used DPC (port 11) not used The lines are active high (high voltage = on, low voltage = off). In the ABET software from Lafayette, the 16 service request lines are constantly monitored. When an input to a chamber is activated, the service request line for that chamber goes high. The time that occurs is recorded as the event time; the chamber is then addressed to determine which input occurred. The address outputs are tied to a multiplexer: 0000 = chamber 1 0001 = chamber 2 0010 = chamber 3 etc. The output enable line is used to latch the outputs on the addressed chamber, like this: Set the address. Set the outputs. Pulse the output enable line. The outputs are now latched. Please note that the inputs are also latched. Pulsing the output enable line will reset the input latches, so it is necessary to read the inputs before setting the outputs. - Turns out the hardware DOES NOT DETECT "OFF" TRANSITIONS on lines. See Whisker help for full details. - option to disable all devices, inc. Amplicon and Advantech cards. - check what happens if incomplete registry info is read? Answer: it reads what it can and fills in the gaps with sensible defaults. - failsafe lines, and off-detecting paired lines, now do not accept either normal (client) aliases or server device/group definition aliases. (Not that clients can claim them anyway, so just the latter.) - error when setting OFF events if !m_bCanDetectOffTransitions... [and new error message in help] - error calling ReadState if !m_bCanDetectOffTransitions... [and new error message in help] - Whisker Database Manager written as a support application - "Exclude Mouse" with border - mouse not excluded in the border area, and should be (Terry Echard for customer C5). Exclusion is being done by CDisplayView, but not CDisplayFrameWnd. So we add CDisplayFrameWnd::OnMouseMove to match that in CDisplayView? And OnMouseLeave_RNC. Yes, that works. - text alignment (p933 of Yuan GDI book) (-top, -baseline, -bottom, -left, -centre, -right options to text object) - support text width reading properly (p944 of Yuan GDI book: GetTextABCExtent; also PreciseTextOut) - text now touchable 2.11.1 RNC (18 May 2005) ------------------------------------------------- - WhiskerTestClient improvements - Fixed bug (introduced in 2.11.0) with non-Truetype font sizing/placing. 2.11.2 RNC (24 May 2005) ------------------------------------------------- - NIDAQ32.DLL required because NIDAQ32.LIB included in all builds, and the DLL wasn't being distributed. So v2.11.0 it wouldn't run on a machine without NIDAQ software installed. - ..\..\COMMON\libraries\NationalInstruments\nidaq32.lib - NIDAQ32.DLL distributed NIPALU.DLL also required NIPALUT.DLL also required NIPAL32.DLL also required NISTCU.DLL also required NIBFFRU.DLL also required NIMDSU.DLL also required NISCALE etc. NIRPC NIMXPRXU NIMDBGU NIORBU NIMXDFU NICFQ32 NIPSM ... and with that lot, WhiskerServer starts but (on machines without NI-DAQ installed) gives the error message "The application failed to initialize properly (0xc0000142). Click on OK to terminate the application" on a machine without NIDAQ properly installed. In the debugger, that comes out as unhandled exception 0xc0000142 (from NTDLL.DLL) - DLL failed to initialize. There's only one thread running at that point, and that's in NTDLL... and we haven't even reached InitInstance. - I suspect NI-DAQ uses IMPLICIT LINKING (see "static loading DLLs" or "Using Implicit Linking" in the help) - So, realistically - only option is to add two editions: STANDARD/NI, MULTIMEDIA/NI. Don't distribute any DLLs with either. Use #define NATIONALINSTRUMENTS. Link in ..\..\COMMON\libraries\NationalInstruments\nidaq32.lib only for those builds (and debug builds). Update .ISS files to reflect this option. - greyed out a few more options on the Programmer's Edition menus (other I/O cards) - see OnUpdateServerUI 2.11.3 MRFA (07 June 2005) ------------------------------------------------- - Text opacity: when opaque, "get text extent" can be out by >1 pixel on the *left* (MRFA noticed) - regardless of L/R alignment - e.g. ask text for its extent, drop a rectangle on it. - the text itself is covered by the rectangle, but the "border" isn't... e.g. 3 pixels out (depends on text height) - sometimes right not perfect either... - top/bottom seem to be of box, left/right seem to be of letters - just document it? - see whether it's correct without the fiddling in GetTextABCExtent? - ensure it works with large italic TimesNewRoman "fishy", too... Ahhh- The text is carefully aligned by Rudolf's code to align the glyphs (using A & C widths to account for edge whitespace). This is exactly what you want, unless you are painting a background too. So, only care about edge whitespace for non-opaque text objects... Implemented and documented in the help 2.11.4 MRFA (10 June 2005;13 June 2005) ------------------------------------------------- - Added -Middle option to DisplayAddTextObject chat with RNC about timing of displays. He made a salient pt that the client thread (i.e. the immediate socket) will return with the *time at which an update message is posted to the main GUI thread for a redraw* - Added a synchronization object to CClient. This can be used to pause the immediate socket until another thread has processed an update message. At present, only used with CClient::DisplayShowChanges. This is therefore the call which clients should use if they want very accurate timestamps on the *actual redraw* for synch object reasons (document pointer array) it can only signal the other thread when *all updating* is finished, so good timing only happens when only one display is attached. 2.11.5 MRFA (Aug-Oct 2005) ------------------------------------------------- - Annoying problem with the Serial DIO box option: at least one type of laptops 'power down' the COM port if no serial coms occur within a certain time (around 10sec). Tried a few things to stop it doing this - best solution turned out to be to 'toggle' the modem control lines (DTR & CTS) that are used for switching for port powered devices. Added this as an option. 2.12.0 MRFA (Dec 2005-Jan 2006) ------------------------------------------------- - Added support for BNC Controller (Campden Instruments) digital IO. -Testing by David Maul (Campden); initial issue found with reading configuration on incomplete racks (the failure of the configuration read could lead to the Read/Write signals not resetting in the interface). Issue resolved, DM reports now working. 2.12.1 MRFA (5 Jan 2006) ------------------------------------------------- - help clearer on Lafayette hardware (instructions were in hard-to-find place) - in .ISS file, move "NI" (National Instruments) disambiguation to the text bit, so it fits - Added a '-debugtouches' option to displaycreatedevice. Minor changes to: Client.cpp & WhiskerMessages.h [new command option] DisplayDevice .cpp & .h [allows a device to be 'always showing touches'] This operates with the 'debugview' option in the displayclaimdevice call within then clientlibrary. If the debugview option is selected, the Clientlib creates and maintains a second view (like the server's view) of the document which can be used to monitor the document, and any touches, in a separate window. 2.12.2 RNC (29 Apr 2006) ------------------------------------------------- - Option to save logs to disk. Note: don't use ID_FILE_SAVE; the framework overrides this and uses a CDocument mechanism. I'm using ID_SAVE instead (no default). 2.12.3 MRFA (23 May 2006) ------------------------------------------------- - BUGFIX - Issue with BNC controller (thread collisions possible during read/write, meaning some inputs or output events occasionally missed. 2.12.4 RNC (24 May 2006) ------------------------------------------------- - main screen tells you about edition in more detail (e.g. UPDD 2, UPDD 3, version with/without NI card support) 2.12.5 MRFA/RNC (31 July 2006) ------------------------------------------------- - Problem: some Campden equipment sets are configured to deliver power only when Whisker switches on the safety relays (NB this is not ideal - touchscreen power should be independent of Whisker). On top of this, some ELO Caroll Touch touchscreens do not sync up with the UPDD driver properly at power-on. The problem is not with Whisker-UPDD communication, but with UPDD-touchscreen communication. Re-initializing UPDD with the TBApiReinit call successfully brings the touchscreens on-line. Note that WhiskerServer::WhiskerServer calls InitAddDioBoards (which sets failsafe relays) *before* it calls InitAddTouchscreens, so we can't fix this problem by a simple re-ordering. Campden think that these touchscreens need a few seconds to power-up before they'll communicate with UPDD. However, what we could try is: (1) call TBApiReinit for every touchscreen being initialized (we never call this fn at present); (2) have an option to manually call TBApiReinit for a touchscreen; and if (1) doesn't fix it, (3) have an option to call TBApiReinit with a *configurable delay* after Whisker starts. This s a bit ugly, as we have to maintain the order of startup, and just pause the startup of Whisker to prevent e.g. connection of clients prior to the touchscreen init. This is done, inelegantly, by a ::sleep() in the main UI thread, i.e. before polling starts RNC/MRFA: (1)-(3) all implemented (in synchrony). - bugfix: OnUpdateTouchscreenUI called Audio_SetToSelection, not Touchscreen_SetToSelection 2.12.6 RNC (22 Aug 2006) ------------------------------------------------------- - The 'Biotronix' workshop... want to configure Amplicon chips such that each of X/Y/Z chips are set to A Output: B Output : C Input. - Known non-Whisker issue discovered Sep 2006: if touchscreen touches appear too prolonged, check the lift-off time configured in UPDD. See the Whisker Help ("Troubleshooting") for more details. 2.12.7 MRFA (7 Jan 2006) ------------------------------------------------------- - The 'Biotronix' workshop option flipped round... want to configure Amplicon chips such that each of X/Y/Z chips are set to A Input: B Output : C Output. - A bit of housekeeping - some of the resources were tagged as English (U.S.), now all English (U.K.) 2.12.8 MRFA (Feb-Mar 2006) ------------------------------------------------------- - implemented 27 Jan 2007 for next compile: default is to scale server views of displays. This is done by setting m_bServerCopiesWouldLikeToScale = true in CDisplayDevice::CDisplayDevice. - A change in the handling of the multiplexing for the BNC controller. Some problems (occasional) at Mario Negri. Essentially, it appeared that one possible BNC board stopped responding. This could be hardware, or could be software. Possible causes of software problem are within: bool CBNCDIOBoard::ReadCardData(int Card, bool bConfig) { int result; CString msg; DWORD dwTimeOut; CMclAutoLock autoLock1(m_CritSec); CMclAutoLock autoLock2(g_AdvantechCritSec); // ~~~ This function calls the Advantech library if (!SetCardAddress(Card, bConfig)) return false; //msg sent by SetCardAddress // ### This sets !Ren is set, i.e. // ### it ensures they are 'there to be cleared // ### ?? Possible that this does not ensure that it is 'set' // ### for long enough, if it !REN was unset last time // ### one possible solution is to call // ### SetCardAddress(Card,bConfig) // ### at every possible exit point, thus ensuring // ### we don't ever leave !REN unset dwTimeOut = ::timeGetTime() + 1; // ### ^^ Oooh this might be a Bad Thing? // ### It's unsigned. Is it correct to do // ### dwTimeStart = ::timeGetTime() // ### and then test // ### (::timeGetTime() - dwTimeStart) > 1 // ### Might this acct for Mirjana's problem if it // ### times out first loop?^^ ... Now, seeing as both dwTimeStart and timeGetTime are unsigned, I can't see the unsigned issue being a big problem, as it would only 'fail' at wraparound (unless I'm missing something). Aaaargh the +1 isn't unsigned! Should it be... dwTimeOut = ::timeGetTime() + (unsigned) 1; does this matter? Should be the same whatever (shurely). Anyway, I've replaced this using the 'difference between two unsigned longs' as above, which is the recommended way of dealing with such matters. Also implemented the 'always set !WEN and !REN on entry, as well as on exit', just in case... ... 2.12.9 RNC (6 Mar 2006) ------------------------------------------------------- - No functional change; changes to #include namespace for ERROR RELATED TO MULTIPLE SOUND CARDS, Sep 2008. NOT A WHISKER BUG; DOCUMENT NONETHELESS. ------------------------------------------------------- This bug is extremely bizarre. When Whisker is running and talking to multiple sound cards (e.g. any two of an onboard Realtek, an additional Audigy, and an AOpen card), then other processes can occasionally fail in floating-point-to-int casts, e.g. the call double m_fMinIntertrialTimeSec = 1.0; for (int loop = 0; loop < 100000000; ++loop) { int testcast5 = (DWORD)(m_fMinIntertrialTimeSec*1000); if (testcast5 != 1000) { /* error */ } } fails on about 14 out of 100,000,000 calls, returning 0 instead of 1000. This didn't happen if Whisker wasn't running, or if only one sound card was active. The end result was that MonkeyCantab appeared to lock, as a random number requested via calls like these went out of bounds. It may be a bug in a kernel-mode sound driver that corrupts some sort of floating point stack when Whisker uses the sound card(s) intensively. Investigation in progress... ... done. The upshot was that it happens whether or not Whisker is running, and is therefore not a Whisker bug. It happens when the sound cards are being used in certain ways. According to Campden (Craig Hauser 22/9/8), goes away if VC6.0 is replaced with VC2003 or VC2005 when compiling the relevant behavioural clients. 2.12.10 RNC (5 Dec 2008) ------------------------------------------------------- - Minor changes to quote handling (for ReportName, ReportStatus). 2.12.11 RNC (31 Dec 2008) ------------------------------------------------------- - Microsecond reporting of timing (to avoid the erroneously poor claim of a "2ms" poll, meaning a 1.001ms poll. 2.12.12 RNC (7 Jan 2009) ------------------------------------------------------- - Some more microsecond-level benchmarking (irrelevant to users). 2.13.1 RNC (11 Jan 2009) ------------------------------------------------------- - UPDD v4.1 support. - Failed to switch on compiler optimizations for all release builds (Settings / C++ / Optimizations / Maximize speed). Previously set to none (debug). This requires disabling precompiled headers. And then IO_extras.h breaks for lack of CString. And #include breaks lots of other things. Incompatibility between PCHs and optimization could be due to other parts of the project (http://www.codeguru.com/forum/showthread.php?s=&postid=829275#post829275). But anyway, complicated. So sod it. It runs fast enough (see recent benchmarking). - STUCK HERE: new UPDD libraries fail to link. Awaiting reply from Touch-Base 11/1/9. See below. 3.0.0 RNC (12 Jan 2009) ------------------------------------------------------- - improvement to failure checking in WhiskerServer::SetWhiskerPriority - UPDD v4.1 support complete (needed extra #define). Needs PATH to include "C:\Program Files\UPDD" to find TBAPI.DLL. Typically: Start > Settings > Control Panel > System > Advanced > Environment Variables > find "PATH" under "System Variables" > click "Edit" > add a semicolon and then type C:\Program Files\UPDD > OK > OK > log off and on. Details added to readme. - VISTA SUPPORT, as follows... - THIS WORKS: - recognizes own hostname/IP address (as an IP v4 address) - communication with clients - polls correctly - sounds works (both DirectSound and Windows audio) - displays, DirectDraw, display event processing, test displays, mouse handling, bitmaps/other media - fake lines, line control, line events - Advantech digital I/O drivers -- tested by Oxford group (in BNC hardware) - On first run, need to unblock for the Windows Firewall; see Help or readme. - Whisker should be run with admin privileges, for two reasons: (1) use of HKEY_CURRENT_CONFIG; (2) more importantly, use of the real-time process priority class. See the Help or Readme files for full details, and how to configure this. - Windows Vista obsoletes "loopback" and makes "localhost" refer to the IPv6 local address ::1, not the IPv4 address 127.0.0.1. See the web site FAQ for instructions on what to do about this. - Defaults changed to "localhost" for the following clients: Demonstration clients } build with Whisker WhiskerTestClient } WhiskerStatus } WhiskerSDK (embedded control help text only) } PigTab SeekTakeShock AttMem ConditionedReinforcement DialysisStimuli FearCond FiveChoice ImpulsiveChoice LeverAutoshaping LeverReversals MonkeyCantab PIT RatBat SameOpposite SecondOrder SeekTake SimpleSchedules VisualAutoshaping - IPv6 IMPLEMENTATION ON SERVER: - Since Windows before Vista doesn't support dual-stack IPv6+IPv4 sockets, we need to make *both* an IPv4 socket and an IPv6 socket for each of our (two) listening sockets, and respond to either. - The version of MFC we're using doesn't support IPv6. - So we could change CSocket and CAsyncSocket. http://www.ipv6style.jp/en/apps/20030120/index.shtml - Basically, CIPv6AsyncSocket replaces CAsyncSocket throughout. - CAsyncSocketEx doesn't work (its message model is incompatible with my thread model). - CIPv6AsyncSocket taken from MFC CAsyncSocket with code added from CAsyncSocketEx. - This fixes the significant problem that local clients were recognized as having an IP address of the hostname, and were recognized as foreign. - The code also attempts to open open IPv6 ::1 as well as IPv4 127.0.0.1 socket - Other background info: http://www.microsoft.com/technet/network/ipv6/ipv6faq.mspx http://msdn.microsoft.com/en-us/library/ms741416(VS.85).aspx http://www.ipv6style.jp/en/apps/20051128/index.shtml http://gsyc.escet.urjc.es/~eva/IPv6-web/ipv6.html - Also, problem connecting from a foreign machine to a Vista box running Whisker *** IPV6 - However, IPv6 still doesn't initialize correctly on Vista (still gives WSAEAFNOSUPPORT). *** IPV6 - Should run checkv4.exe on current code (from latest Windows SDK). *** IPV6 - NOT YET TESTED ON VISTA: - Amplicon digital I/O drivers - ICS Advent 82C55A I/O drivers - National Instruments I/O drivers - tested (by Dec 2014) ... one of these has been successfully tested by Oxford group; unclear as yet which - a/w Julie's reply 15/1/9 - Serial port I/O devices - UPDD - tested (by Dec 2014) - ABET-II done: just changes the wiring map, implement the two-chamber-per-multiplex-set option (cable splitter!) and remove unused lines from the main line map. - NI-DAQmx 8.9 - OLD (Traditional NI-DAQ [Legacy]): http://sine.ni.com/nips/cds/view/p/lang/en/nid/14914 ... which is what Whisker uses - NEW: NI-DAQmx: http://www.ni.com/dataacquisition/nidaqmx.htm ... download (1.1 Gb!) ... Start > Programs > National Instruments > NI-DAQ > help > NI-DAQmx C Reference Help - try to make old version "NI TRAD". Retrying re distribution - if machine has no NI drivers on it, it takes a lot more than NIDAQ32.DLL/NICAIU.DLL to get WhiskerServer.exe to run. - if machine has NIDAQmx only, it takes a lot more than NIDAQ32.DLL to get it to run. - so it looks like we have to have a distribution for each NI system... damn... .. keep #define NATIONALINSTRUMENTS .. and NI_TRAD .. and add NI_MX - note for testing: Egret mx+trad; Wombat mx; Ghost none. - ABET NoLatch - see email from Terry regarding how the new multiplexer works - Terry Echard 16/1/9: "Within Whisker are you polling the Service Request to see when to read in inputs? You are correct the Service Request will only be set by the input when it is activated. With the original version of the hardware, the Service Request and inputs are set when a device is active and these cleared by reading the Service Request. The Service Request and input would not get set again until the device was deactivated and then re-activated, e.g. lever pressed (activated), lever released, lever pressed. Now with the latches removed the Service Request and input will stay activated as long as the device is active, e.g. lever pressed. So reading the Service Request and input will clear them but they will be set again immediately after reading. So now you can detect when the input is OFF. After reading the inputs (from a Service Request ) the inputs and service request will be set back to a 1 as long as the device is activated. When released the corresponding input and resulting Service Request will clear upon reading and stay cleared." New system implemented in CNIControllingABETDIOboard::UpdateChamber_NoLatches(). Terry confirms 9/2/9 that a write remains necessary following a read. 3.1.0 RNC (12 Jan 2009) ------------------------------------------------------- - This is the height of stupidity. But UPDD v4 insists that during initialization, the working directory contains tbupdd.ini (see http://touch-base.com/kb/index.php?action=article&cat_id=006&id=32 ) - when you call TBApiInit. Now, UPDD knows where that is, because it installs it in the registry: HKLM\SOFTWARE\Touch-Base: InstallDir (default: C:\Program Files\UPDD) But it wants the client software to work this out and change there. How dumb is that? Anyway, fixed via a user-configurable setting (read, by default, from the UPDD section of the registry). - Fixed lack of DAQmxClearTask cleanup in NIDAQmx (was leading to an exception on Whisker exit). - Revised information (Matt Croxall, Lafayette) about hardware configuration of ABET-Split interface. Says there may be no point discriminating "split" and "non-split" interfaces. SPLIT: 0-7 first box inputs 0-7 8-15 second box input 0-7 16-30 first box outputs 0-14 31 first box unused (w'd have been output 15) 32-46 second box outputs 0-14 47 second box unused (w'd have been output 15) They don't want Whisker to ignore the unused outputs; they want to flag them as unused in the DDF. NONSPLIT: 0-15 box inputs 0-15 16-47 box outputs 0-32 [sometimes with two of them unused, i.e. lines 31 and 47] - caps to the number of messages that I/O boards can send out from a high-frequency polling system! Otherwise, since NIDAQmx sometimes says it has boards that are not physically installed, you get a serious problem (e.g. on egret2...). Implemented via PollStatusMessage() for all applicable board types (ICS and serial seem not to do this anyway). 3.1.0 RNC (12 Jan 2009) ------------------------------------------------------- - Oops. DAQmxClearTask went into NationalInstrumentsDIOboard.cpp, but not NIControllingABETDIOboard.cpp. Fixed. 3.2.0 RNC (12 Jan 2009) ------------------------------------------------------- - Conversion to Visual Studio 2008. ... minor fixes to smartptr.h ... the usual <*stream.h> changing to <*stream> ... install current DirectX SDK to get dsound.h (DXSDK_Jun08.exe from http://www.microsoft.com/downloads/details.aspx?FamilyId=519AAE99-B701-4CA1-8495-39DDDE9D7030&displaylang=en ) ... fixes to vector erasure (iterators invalidated, picked up by _HAS_ITERATOR_DEBUGGING, q.v. - Optimization also switched on (maximize speed). - All ancillary programs converted. - Bug found. Steps to reproduce: (1) connect, (2) create a display view, (3) look at it in the server's tree, (4) forcibly shut down the socket from the client side. Crashed (deletion of document being viewed etc.). Modifications made to CClient::ShutDown and CClient::KillThyself, q.v., but this wasn't the source of the problem. CClient::WorkerThread_Housekeeping() delete (*devit); // delete the underlying memory CDisplayDevice::~CDisplayDevice() delete m_pBlackDoc; CDisplayDocument_Black::~CDisplayDocument_Black() CDocument::DisconnectViews() while (!m_viewList.IsEmpty()) ASSERT_KINDOF(CView, pView); ... in other words, something's still looking at the m_pBlackDoc. And that something is set by CWSLeftView::SelectionChanged (and thence CWSLeftView::SwitchViewInSplitter). Dealt with by creating WhiskerServer::Display_ClientDisplayDeviceDying. Note, however, that this bug is not reproducible on v2.12.6 release. 3.3.0 RNC (23 May 2009) ------------------------------------------------------- - Support for NI-PCI-6509 card under NIDAQmx (this card is not supported by NIDAQ Trad, I think; no code for it in the list). Campden report it's functionally identical to the NI-PCI-DIO-96 from the digital I/O standpoint, and works with ABET. 3.4.0 RNC (2 July 2009) ------------------------------------------------------- - Bug fix: release (but not debug) builds disallowed the 1/0/F keys on line views, through a bug involving the omission of "item.mask = LVIF_PARAM;" statements. Search for 2/7/9 for details. 3.5.0 RNC (8 Aug 2009) ------------------------------------------------------- - Support USB I/O hardware from Lafayette. ... note: documentation is in Cantab-USB_20July2009.ZIP (in whiskersdk\libraries\lafayetteusb), and other versions on Wombat etc. in Documents/Whisker/USB drivers from Lafayette - CLafayetteUSBDIOboard - Is only one such USB device allowed? The library doesn't appear to allow selection between multiple devices. NOTES: - The driver doesn't allow one of many boards to be selected, so assume only zero/one board installed. - Each board has 8 inputs and 16 outputs (of which 2 are used as a safety relay and not usable by Whisker; so you don't have to use Whisker's safety relay facility on top, if your safety relay is going through one of these devices). - Library .LIB stub (cantab-usb-driver.lib) won't allow executable to run (at all) unless DLL is found. - The DLL (cantab-usb-driver.dll) is happy to live in the {app} directory, even if you run it from some other path. - There's no auto-reset if error 6: I/O write error is seen - this suggests an unplugged device; if it's plugged in again, Whisker doesn't recover. Still, that's not the end of the world! Lafayette confirm no need to support hot-plugging. 3.5.1 RNC (19 Sep 2009) ------------------------------------------------------- - CLafayetteUSIDIOboard now sends SendSafetyRelayOff command on shutdown. 3.5.2 RNC (15 Oct 2009) ------------------------------------------------------- - Support for Advantech PCI-1756. - Option to refresh ABET outputs regularly. 3.5.3 RNC (16 Oct 2009) ------------------------------------------------------- - Fix to Advantech non-programmable cards (now all supported generically). 3.5.4 RNC (30 Nov 2009) ------------------------------------------------------- - Bugfix: newline not sent with reply to immediate socket Link message. Now see CImmediateSocket::SendWithNewline. 3.5.5 RNC (12 Feb 2010) ------------------------------------------------------- - Workaround for bug in UPDD 4.1.6 (confirmed as UPDD API bug by Touch-Base) - Symptom: if Whisker is run from the UPDD directory (typically C:\Program Files\UPDD), it talks to UPDD and finds touchscreens. If Whisker is run from its own directory, even if the Whisker UPDD directory settings are correct, it talks to UPDD but doesn't find the touchscreens. - Reason: UPDD advertises that its client (Whisker) needs to change to the UPDD directory for the call to TBApiInit(), but actually needs Whisker to be in the UPDD directory for this and the first - Corrected from the UPDD side from UPDD v4.1.8, they tell me (Gary Allitt, gary@touch-base.com, 10/2/10) - Whisker now does this after TBApiInit but before changing back out of the UPDD directory: DWORD dw; TBApiGetSettingDWORD(0,"fakereadtoinitsettings",&dw); = fix recommended by Touch-Base. 3.6.0 RNC (from 15 May 2010) ------------------------------------------------------- - Support for York Winter's (Charité Universitätsmedizin Berlin) TCP/IP-based hardware controller. New classes: CBerlinNetworkDIOboard CConfigBerlinNetworkDlg Plan: Whisker will talk via TCP/IP to their software, which talks to hardware (in some way we needn't care about). MESSAGE SYNTAX(from YW 1/2/10) for their server: Communication takes place via the TCP/IP stack. Whisker is the client here, and our software the server. Number of input channels Gets the number of input channels. The channels are numbered from 0 to max-1. Client sends: GetNumberOfInputChannels ======================== Server response: NumberOfInputChannels max --------------------- Number of output channels Gets the number of output channels. The channels are numbered from 0 to max-1. Client sends: GetNumberOfOutputChannels ========================= Server response: NumberOfOutputChannels max ---------------------- Set channel on Sets a channel to "high". Client sends: SetChannelOn id =============== - id is channel number Server response: no response Set channel on with pulse Set a channel for a certain time to "high". Client sends: SetChannelOnPulse id dur ======================== - id is channel number - dur is the duration in millisecond. Server response: no response Set channel off Sets a channel to "low". Client sends: SetChannelOff id ================ - id is cannel number. Server response: no response Sensor state Signals the state of a light barrier. Server sends: SensorState id state -------------------- - id is channel number - state is "0" or "1". Get sensor state Manual request of the state of a light barrier. Client sends: GetSensorState id ================= - id is cannel number. Server response: SensorState id state -------------------- OTHER (from YW 23/11/9): - By definition all inputs and outputs would be in inactive state at program onset. - You would receive dedicated messages only in the case an input event happens. No continuous polling would be required. OTHER (from YW 2/2/10): - We take CR+LF for delimiting of two messages. - Yes, we will disable the Nagle auto-packet-concatenation algorithm. - The state of a light barrier is automatically reported and can also be retrieved via "GetSensorState". - The software will also indeed run as a non-blocking socket system. - Yes, we used only one port. Use berlindummyserver.pl as a testbed. - YW 17/5/10: their default port is 5002; they'd like up to 16 controllers supported. 3.6.1 RNC (14 Aug 2010) ------------------------------------------------------- - bugfix to socket code for Berlin server (worked in debug but not release mode; uninitialized m_bBlockingMode was incorrectly being initialized to false in release mode; gave errors "Internal error: Problem with CBerlinSocket::GetReply, which returned error code 10035") 4.0.0 RNC (May - July 2011) MAIN ADDITION: VIDEO. ------------------------------------------------------- - In case the UPDD registry fails to come up with its location, default (C:\Program Files\UPDD) added (m_strUPDDv4Directory) - stdafx.h now includes whiskerserver.h (for compilation speed), and defines NOMINMAX; use std::min and std::max instead - exclude whiskerserver.hpj from the project (pointless, using Help&Manual) - DirectDraw thread locking made global: g_DirectXCritSec - Help edited re Perl, Python - Video COMMANDS: DisplayAddObject docname objectname video ... ... can fail for conventional reasons ... can fail if it'd create too many video views DisplaySetAudioDevice displaynameornumber audionameornumber ... requires prior claim of the display and the audio device ... must be set before videos are loaded ... refuses if the device is split ... only accepts/plays if the device is a DirectX device, i.e. does not work for "Primary Sound Device". VideoPlay docname objectname VideoPause docname objectname VideoStop docname objectname ... stops and rewinds VideoSeekAbsolute docname objectname time_ms ... times outside the range of the video are truncated VideoSeekRelative docname objectname time_ms VideoGetDuration docname objectname ... returns value from the first matching video ... immsocket: Duration TTT_ms ... main socket: Document DDD video VVV duration: TTT_ms VideoGetTime docname objectname ... returns value from the first matching video ... immsocket: VideoTime TTT_ms ... main socket: Document DDD video VVV time_position: TTT_ms VideoTimestamps on|off ... see also DisplayEventCoords, Timestamps ... use () brackets ... if everything switched on, an event will look like: Event: VideoTouched (82503) 572 827 [18376] vtime x y timestamp CHANGES: DisplayShowDocument can now fail, if it'd create too many video views No reverse playback; not all demuxes support (etc.): http://stackoverflow.com/questions/1725534/directshow-reverse-playback Choice of video library: http://stackoverflow.com/questions/37956/c-whats-the-easiest-library-to-open-video-file re ffmpeg: http://www.codeproject.com/KB/audio-video/Using_FFMpeg.aspx http://graphcomp.com/ffmpeg/ http://www.salyens.com/mingw/ http://www.codeproject.com/KB/audio-video/Using_FFMpeg.aspx?msg=3406679&display=Mobile ... creating lib to go with DLL http://www.coderetard.com/2009/01/21/generate-a-lib-from-a-dll-with-visual-studio/ re gstreamer: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-bad-plugins/html/gst-plugins-bad-plugins-directdrawsink.html CDisplayObject_Video; CMovie2. http://msdn.microsoft.com/en-us/library/dd375454%28VS.85%29.aspx VMR-9, windowless mode "Video Rendering" "VMR Windowless Mode" "Using Windowless Mode" "Audio/Video Playback in DirectShow" http://www.codeproject.com/KB/audio-video/cpdirectshow.aspx http://stackoverflow.com/questions/3101060/why-is-my-directshow-based-video-player-producing-choppy-results-while-wmp-plays http://www.codeproject.com/KB/audio-video/MediaPlayer.aspx synchronizing video (for subject and user copies?): http://www.ms-news.net/f3598/how-play-2-videos-synchronized-2113497.html TEST SYNTAX: displayclaim 0; displaycreatedocument doc; displayshowdocument 0 doc; displaysetbackgroundcolour doc 0 0 255 audioclaim 1;displaysetaudiodevice 0 1 DisplayAddObject doc vid video 50 50 c:\started.wmv -loop -playnow -width 100 -height 300 DisplayAddObject doc vid2 video 50 600 c:\security.wmv -loop -playnow -width 100 -height 300 displaydeleteobject doc vid videogetposition doc vid displaydeletedocument doc displayaddobject doc box rectangle 200 200 900 900 -brushsolid 255 0 0 OVERVIEW - A Filter Graph Manager controls the flow of information; About DirectShow Filters http://msdn.microsoft.com/en-us/library/dd373390%28v=VS.85%29.aspx How To Play A File http://msdn.microsoft.com/en-us/library/dd389098%28v=VS.85%29.aspx - The RenderFile method call builds a whole chain of filters for the filetype. - Ways to build a graph: http://msdn.microsoft.com/en-us/library/dd390951%28v=VS.85%29.aspx - Example of rendering video to multiple renderers: http://stackoverflow.com/questions/5220691/getting-multiple-video-renderers-in-directshow-net-in-c ... though the VMR9 is preferred to IVideoWindow: http://msdn.microsoft.com/en-us/library/dd377276%28v=vs.85%29.aspx - Example of adding audio/video renderers by hand: http://msdn.microsoft.com/en-us/library/ff625882%28v=VS.85%29.aspx e.g. CLSID_DSoundRender; see also RenderFileToVideoRenderer() function in dshowutil.h ... re selection of audio devices: see http://msdn.microsoft.com/en-us/library/dd375473%28v=vs.85%29.aspx - Building a filter graph: http://msdn.microsoft.com/en-us/library/dd318237%28v=vs.85%29.aspx - pins probably take only one output, so need this: Infinite Pin Tee Filter http://msdn.microsoft.com/en-us/library/dd390336%28v=vs.85%29.aspx ... ideally, split it after the decoder, not before (reduces amount of data) (empirically) VIDEOS ALWAYS RENDER TOPMOST, to a rectangle. Non-rectangular rendering is sometimes possible using WPF: http://msdn.microsoft.com/en-us/library/ms742196.aspx ... but not easily using the sort of framework we have. Possible to sort this by blending a bitmap? http://msdn.microsoft.com/en-us/library/dd390448%28v=vs.85%29.aspx http://msdn.microsoft.com/en-us/library/dd375479%28v=vs.85%29.aspx ... a solid video overlain by a bitmap that's solid where drawn and transparent otherwise... ... see SetAppImage, UpdateAppImage (which have SILLY names) So for now, video is topmost. - So, USER WARNINGS: - putting things over a video may fail (video goes topmost) - creating overlapping videos may look silly (each tries to go topmost) - left/right audio thing? ... I suspect also that the left/right sound system (which uses sound buffer panning) won't be up to running as an audio renderer. Will need the physical sound device. - may be lack of perfect sync with server preview copy - Sound choice: either (a) associate the video with a sound output device, or (b) associate the screen with a sound output choice. (a) is probably easier; do it at the time of creation; problematic if the client owns two displays and moves one document between them (b) makes more sense; would (optionally) assign no sound to the server view, or have a global option for "play server-view videos on which sound device (inc. option for none)?"; better for yoking. --- IMPLEMENTED, approximately, with DisplaySetAudio command. - On Vista (but not on XP under Shrike's VirtualBox?), playing with two null renderers leads to an instant crash (to graphedt.exe as well as Whisker). Note: graphedt.exe illustrates that this graph has no clock... ... clock comes from the audio... ... though this suggests that the system time is used if there's no clock: http://msdn.microsoft.com/en-us/library/aa451394.aspx and indeed, GraphEdit manages with just source -> video decoder -> VMR9. ... maybe require that there is an audio output at all times, or pause the video if not. - general filter selection advice for different filetypes: http://wmwiki.com/mcored/knowledgebase/kb-directshowfix.htm - pause versus stop: http://www.gdcl.co.uk/q_and_a.htm - deadlock bug info: http://www.gdcl.co.uk/q_and_a.htm (at the bottom) - Other: http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/thread/6e416a8c-ada8-4f0f-8ae7-73f7f88b577d/#976cafef-5d57-42f7-96f7-a4693cdf8197 http://social.msdn.microsoft.com/profile/geraint%20davies/?type=forum&referrer=http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/thread/51ce1c7d-b8c4-4f58-868b-2b709f6173d8 http://www.ms-news.net/f3598/problems-connecting-vmr9-in-a-renderer-graph-with-gmfbridge-2119668.html http://stackoverflow.com/questions/5031695/microsoft-streambufferengine-support-two-sources-to-one-sink - Built GMFBridgeW. Turned off autologging in release builds. - Made GMFBridgeW an appfilter, not a DLL filter. (Tricky!) Acknowledged in help. - More on inftee "lack of copy" problem: http://sourceforge.net/projects/directshownet/forums/forum/460697/topic/3347591 - Deadlock problem: seems to match this: http://social.msdn.microsoft.com/Forums/en/windowsdirectshowdevelopment/thread/18ca82b1-f977-4973-b0d9-9cbf8dd42d7a - Debugging system calls: see http://msdn.microsoft.com/en-us/library/b8ttk8zy%28v=VS.90%29.aspx symbol source = http://msdl.microsoft.com/download/symbols - MULTIPLE DEADLOCKS until I stopped trying to send audio as well as video through the bridge. All blocked at this point: ... wait-for-object kernel code ... from CVMRPinAllocator ... via some filters ... from BridgeSinkInput::Receive(IMediaSample *pSampleIn) specifically: hr = m_pCopyAllocator->GetBuffer(&pOut, NULL, NULL, 0); So I presume that since the audio and video samples were coming separately, there was some sort of random order dependency and it broke everything (esp.: starting/stopping a bridge). - Doesn't look like we can pass some sort of multiplexed thing through the bridge. Not entirely clear we wanted to anyway (only reason: over-fancy server copy, and/or yoked video). And an AVI Mux filter, though it accepts audio+video in and produces a single output, won't connect to the AVI Splitter. The other option would be separate bridges for audio and video... - Server event log runs off local copy. Trivial vector size bugfix. - Server > Video configuration ... maximum number of true displays a video can be shown on (minimize for performance) ... enable audio tracks in video files ... allow server video preview? We do NOT allow server "copies" of audio (to match general audio behaviour). - Server > Logging options ... log server events Y/N? ... write server log to disk (as whiskerserverlog.txt) Y/N? ... event logging is on for new clients Y/N? ... comms logging is on for new clients Y/N? ... write client event logs to disk (as client_n_events.txt)Y/N? ... write client comms logs to disk (as client_n_comms.txt) Y/N? ... server logging directory (default c:\whiskerlogs)? - File / Save &as, not &Save as. - UPDD4 dialogue now has browse button. - bug: deleted playing video; assert from GMFBridgeW.dll; ended up at BridgeStream::NotifyQuality code: if (m_pInputPin && m_pInputPin->IsConnected()) with m_pInputPin == 0xfeeefeee (the "freed memory" code) and all other elements of "this" being I-am-deleted codes ... so the BridgeStream has been deleted from BridgeSourceOutput::Notify code: return GetStream()->NotifyQuality(pSender, q); where GetStream() returns (having locked) m_pInputPin ... "this" elements look sensible ... something's been deleted ... think the problem is that the render graphs are running, passing information back through the bridge, and elements there are nonexistent. So stop the render graphs as part of the early phase of source graph destruction? Yes, that fixed it. - shortcut keys to menu changed - DirectDraw back-buffering NOT COMPATIBLE (it seems) with VMR9 windowless mode. Might be possible with VMR9 renderless mode (with custom allocator-presented); lots and lots of work. So currently not an option. Note that client-created windows use this by default, so turn it off with "DisplayCreateDevice ... -directshow off" to use them with video. - Crosshairs in server's null video image not perfect! Not a major problem. - SDK v4.0 - Amended DemoDisplayClient to test SDK video. - *** We should probably handle WM_DISPLAYCHANGE messages (calling the VMR's IVMRWindowlessControl::DisplayModeChanged function). However, resolution changes ignored for the moment. See http://msdn.microsoft.com/en-us/library/ms787876%28v=vs.85%29.aspx ... subsection "Handle Window Messages" - If the server's console view is resized large-to-small (e.g. by double-clicking the title bar), you can get "leftover" video still that's not correctly invalidated. (Not a subject-side functional bug.) -> fixed in CDisplayView::OnEraseBkgnd - new renderer when paused -> problems solution: start renderer paused in this situation - crashes/long pauses/deadlocks: - go to no renderers, then render again -> blank but time ticking (fixed by seek) ... and at one point a deadlock-type crash from BridgeStream::BeginFlush() - ultimately "m_pPin->BeginFlush();" within COutputQueue::BeginFlush() - we had a crash; currently crashes can be generated by DemoDisplayClient (in video mode) - seeking -> COutputQueue::BeginFlush VERSUS BridgeSinkInput::Receive / m_pCopyAllocator->GetBuffer() deadlock? I wonder if GMFBridge (and DirectShow) is designed to run as a single instance only... ... is the BeginFlush() call set holding an appropriate critical section for its own graph(s), but ignoring the fact that another graph is in a Receive() call, and that's breaking DirectShow? ... see threading: http://msdn.microsoft.com/en-us/library/dd407193%28v=vs.85%29.aspx - these problems seemed to go away by switching to COINIT_APARTMENTTHREADED ... no, didn't like that, back to COINIT_MULTITHREADED - the test of multithreaded access is (a) play etc. (client thread); (b) loop (server window, GUI thread) ... still one situation where a graph stops (but can be restarted with Stop). ... no, still not stable? Again, it's CSourceGraph::SeekToPosition that triggers the deadlock (and subsequently, via the source graph critsec, locks the GUI). It gets stuck in COutputQueue::BeginFlush, likely at its m_pPin->BeginFlush() call. Why's that stuck? There are some ongoing GetBuffer calls... Not dissimilar: http://social.msdn.microsoft.com/Forums/en/windowsdirectshowdevelopment/thread/18ca82b1-f977-4973-b0d9-9cbf8dd42d7a Based on that, try pausing everything (render graphs too) when seeking. Seems to fix it. - Currently, some WMV files work and some don't, depending on the codec. [update 2018-02-04: moved to EVR over VMR9 as default] THESE WORK: - video codec type Windows Media Video 9 (and audio codec type Windows Media Audio 9.2) ... known as WMV9 ... uses the "WMVideo Decoder DMO" filter and works - things encoded with video codec "wmv2" and audio codec "wmav2" by ffmpeg (see below) SOME FILES WON'T LOAD, and say so. Convert them (see below). SOMETIMES AUDIO FAILS: - in which case the server log might say e.g. Couldn't use bridge to create source graph - DirectX error in CSourceGraph::CSourceGraph: Cannot play back the audio stream: the audio format is not supported. - use the -noaudio flag in this case SOME FILES LOAD AND DON'T COMPLAIN BUT VIDEO PLAYS AS A BLANK SCREEN. - example: video codec type Windows Media Screen V7 (and audio codec type Windows Media Audio V8) ... fail because the VMR9 renderer insists on a colour space converter when used from an infinite tee, and the "WMV Screen decoder DMO" filter used by this format won't play through a CSC (as tested in GraphBuilder), unlike the "WMVideo Decoder DMO" filter TO INSPECT WMV VIDEO CODE TYPES: - run Windows Media Player; load file; then File > Properties GENERAL PROCEDURE TO MAKE SOMETHING WORK: - Install ffmpeg. On a Ubuntu/Debian Linux box: sudo apt-get install ffmpeg On a Windows box: see http://www.ffmpeg.org/ - ffmpeg -i INPUTVIDEO.XXX -vcodec wmv2 -acodec wmav2 OUTPUT.wmv - This also allows conversion from FLV, MPEG, etc. - potential timer error on millisecond clock (timeGetTime) wraparound, every 49.71 days, under restricted circumstances; changed to difference method of calculations in CSoftwareLine::CheckForChange (safety timers) and CClientTimer::Poll (main timers). - DLL loading made dynamic: Advantech -- ADSAPI32.DLL Lafayette USB -- CANTAB-USB-DRIVER.DLL, FTD2XX.DLL Amplicon -- DIO_TC.DLL NI -- NIDAQ32.DLL (NI-DAQ Trad); NICAIU.DLL (NI-DAQmx) UPDD -- not yet done (it's TBAPI.DLL, at least for v4, but older versions might not be DLL-based) Note for editing project dependencies by hand: edit (carefully) whiskerserver.vcproj 4.1 RNC (Aug-Sep 2011) ------------------------------------------------------- - Textual amendments to multimedia installation .ISS, to clarify re UPDD versions (e.g. that UPDD v4.01.06 is later, not earlier, than v4.1, thanks to slightly odd notation from UPDD) - Additional clarity in failed-to-load-DLL messages - Dynamic loading of TBAPI.DLL v4 (= Dec 2007 onwards). - By the way, a good DLL explorer is: http://www.nirsoft.net/utils/dll_export_viewer.html - Support for Advantech Bionic DAQ (or BioDAQ): http://support.advantech.com/support/DownloadSRDetail.aspx?SR_ID=1-FC2XDX&Doc_Source=Download ... necessary for "DAQ Navi" (= Advantech BioDAQ Navigator) drivers ... necessary for Advantech support under Windows 7 ... it worries me, in BDaqCL.h, that BDaqLib::instHandle is regularly tested against NULL, but doesn't ever appear to be initialized to NULL. However, it has helpful thread-safing code in it. Also, perhaps it is default-initialized to zero: http://bytes.com/topic/c/answers/134111-should-pointer-members-zero-initialized-default "Pointers that are local variables are not zero initialized. Same holds for pointers that are member variables of local objects (ie. objects created on the function stack or created with new). Pointers that are class static, function static, global, or namespace variables are zero initialized. I think the same holds for pointers that are member variables of class static, function static, global, or namespace objects." Here we have a function-static object (in ) containing a pointer. It should be zero-initialized: http://stackoverflow.com/questions/3803153/what-are-primitive-types-default-initialized-to-in-c ... OK; so we use their classes (see POLICY in DLL_Advantech_detailed.h) MODIFICATIONS AFFECT: Advantech direct card support BNC support via Advantech cards =============================================================================== 4.2 RNC (16 Dec 2011) =============================================================================== - Addition of column to lines view (WSServerLineView.cpp) to display #on transitions, even for decent lines. Helps with faulty hardware problem-tracking. =============================================================================== 4.3 RNC (10 Feb 2012 - 15 May 2012) =============================================================================== - Bug-fixing relating to (1) new Advantech drivers ... including only detecting devices 0 and 1 (2) speed of loading re NI board scanning on new drivers - Help fixed (1 - text in dialogue box; 2 - text in main help) re Amplicon "IOO" (not OOI) mode; reflects actual changes in WhiskerServer v2.12.6 to v2.12.7. - Options to place bitmaps and video by centre point, rather than top left point. - Some compiler warnings averted. - For iPad support (via Air Display): Configure display / "Exclude mouse" (m_bExcludeMouse in CDisplayDevice, CDisplayFrameWnd, CDisplayView) currently hides the cursor and ignore mouse input. We'd like these combinations, at least: * Mouse hidden and ignored (e.g. animal multimonitor). * Mouse hidden and responded to like a touchscreen (e.g. iPad/Air Display). * Mouse visible and responded to like a touchscreen (easy conversion of tasks). * Mouse visible and responded to like a mouse (as usual). So we want m_bHideMouseCursor and m_eMouseBehaviour; then we get all 2x3 combinations. Old settings are still read from the registry if new settings aren't present. - Reversed date order of milestones list. =============================================================================== 4.4.0 RNC (June-July 2012) =============================================================================== - UPDD v4.1.10 support. This edition of UPDD changes its initialization routines. If you use UPDD v4.1.10 with previous versions of Whisker, you'll get this error: "The procedure entry point _DLL_TBApiInit@0 cannot be located in the dynamic link library TBApi.DLL" or (in the Whisker log): "Couldn't read function from TBAPI DLL: _DLL_TBApiInit@0. Touchscreens will not be available."; - Make UPDD version-detection/DLL loading automatic, if possible. Note also UPDD init API change recently, from TBPI v4.1.10: http://www.touch-base.com/documentation/APIFunctions/TBApiInit.htm http://www.touch-base.com/documentation/API-GettingStarted.htm However, note that UPDD v2 doesn't seem to supply a DLL, other than TBHook.dll (which seems to have other stuff in). - Release_Full_UPDD2: preprocessor: WINVER=0x0500;NDEBUG;WHISKER_FULL;WHISKER_MULTIMEDIA;TBAPI_V2;WIN32;_WINDOWS Release_Full_UPDD3: preprocessor: WINVER=0x0500;NDEBUG;WHISKER_FULL;WHISKER_MULTIMEDIA;TBAPI_V3;WIN32;_WINDOWS Release_Full_UPDD4_1: preprocessor: WINVER=0x0500;NDEBUG;WHISKER_FULL;WHISKER_MULTIMEDIA;TBAPI_V4_1;WIN32;_WINDOWS Release_Full_UPDD2: linker: Strmbase.lib quartz.lib strmiids.lib mcl.lib mcl4mfc.lib DDRAW.LIB DSOUND.LIB DXGUID.LIB UPDD_v2_TBAPI_release.lib pplibnt.lib WINMM.LIB htmlhelp.lib ws2_32.lib cryptlib_release.lib Release_Full_UPDD3: linker: Strmbase.lib quartz.lib strmiids.lib mcl.lib mcl4mfc.lib DDRAW.LIB DSOUND.LIB DXGUID.LIB UPDD_v3_TBAPI_release.lib pplibnt.lib ws2_32.lib WINMM.LIB htmlhelp.lib cryptlib_release.lib Release_Full_UPDD4_1: linker: Strmbase.lib quartz.lib strmiids.lib mcl.lib mcl4mfc.lib DDRAW.LIB DSOUND.LIB DXGUID.LIB pplibnt.lib WINMM.LIB htmlhelp.lib ws2_32.lib cryptlib_release.lib - Consideration given to removing UPDD v2 support, and to shifting to DLL support for v3 (possible?). Old method #define renamed from TBAPI_V3 to TBAPI_V3_STATIC Old method #define renamed from TBAPI_V2 to TBAPI_V2_STATIC Old method #define renamed from TBAPI_V4_1 to TBAPI_DLL - However, data structures for callbacks remain different for different versions. We'd need to create per-UPDD-version namespaces to handle v2/v3/v4 in a single executable, and even then the linkage would be hard/?impossible. - D/w Dave Bhattacharjee 21/6/12: Current UPDD version is 5. v4 is prevalent. v3 is past it. v2 is antique [version 2.0 released on 28 May 1999] D/w Gary Allitt 21/6/12. DLLs emerged around v3-v4. Looks like v3 does come with DLLs (at least late v3). No significant changes in API from 4.1.10 to 5. He endorses my strategy of distinguishing 3/4.0-4.1.8/>4.1.10 by function header. In the future they'll include some "get version number" call for the DLL. - So: chuck UPDD v2 support chuck UPDD v3 static linkage support UPDD v3 via DLL continue to support UPDD v4.0-v4.1.8 via DLL add support for UPDD v4.1.10+ via DLL ... and now we have a single executable for each of Standard, Programmer's, Multimedia. - Note that the DLL export convention changed during v3, so there's "old v3" and "new v3". - DLL exports... a summary: 1. A new one (UPDD v4, newer v3, etc.): DLL Export Viewer: _DLL_TBApiMousePortInterfaceEnable@4 dumpbin exports: _DLL_TBApiMousePortInterfaceEnable@4 ... this is __stdcall: http://msdn.microsoft.com/en-US/library/x7kb4e2f(v=VS.80).aspx 2. An old one (older v3): DLL Export Viewer: int __stdcall DLL_TBApiMousePortInterfaceEnable(int) dumpbin /exports: ?DLL_TBApiMousePortInterfaceEnable@@YGHH@Z ... the latter is what we need to look up with ... this is probably a C++ type-safe decoration: http://msdn.microsoft.com/en-us/library/5x49w699(v=vs.80).aspx ... for example, undname ?DLL_TBApiMousePortInterfaceEnable@@YGHH@Z yields Undecoration of :- "?DLL_TBApiMousePortInterfaceEnable@@YGHH@Z" is :- "int __stdcall DLL_TBApiMousePortInterfaceEnable(int)" - Here's the old v3 DLL: Microsoft (R) COFF/PE Dumper Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file c:\Program Files\UPDD\TBAPI.DLL File Type: DLL Section contains the following exports for TBApi.dll 00000000 characteristics 3DDB9FEE time date stamp Wed Nov 20 14:45:02 2002 0.00 version 1 ordinal base 149 number of functions 149 number of names ordinal hint RVA name 1 0 000015D0 ?DLL_TBApiActivateCalibrationStyle@@YGHEPBDH@Z 2 1 00001550 ?DLL_TBApiAddDevice@@YGHHEPBD0@Z 3 2 00001210 ?DLL_TBApiAddToolbar@@YGHPBD@Z 4 3 000011A0 ?DLL_TBApiApply@@YGHXZ 5 4 000011B0 ?DLL_TBApiApplyNoReload@@YGHXZ 6 5 00001910 ?DLL_TBApiAutoSetSwapXY@@YGHE@Z 7 6 00001230 ?DLL_TBApiCalibrateToolbar@@YGHPBD@Z 8 7 00001860 ?DLL_TBApiClone@@YGHEPBD@Z 9 8 00001030 ?DLL_TBApiClose@@YGXXZ 10 9 000019B0 ?DLL_TBApiDeleteRegistryValueCached@@YGXPAXPBD1@Z 11 A 00001500 ?DLL_TBApiDeviceSetMouseScaling@@YGHEJJJJ@Z 12 B 00001530 ?DLL_TBApiDeviceStopMouseScaling@@YGHE@Z 13 C 00001190 ?DLL_TBApiDriverEnable@@YGHH@Z 14 D 000016A0 ?DLL_TBApiDump@@YGHXZ 15 E 00001760 ?DLL_TBApiEnumAdhocValues@@YGXEP6GXPAXPBD1KH@Z0@Z 16 F 000016D0 ?DLL_TBApiEnumStringTokens@@YGXP6AXPAXPBD@Z011@Z 17 10 000011D0 ?DLL_TBApiGetADX@@YGHEPAK@Z 18 11 000011F0 ?DLL_TBApiGetADY@@YGHEPAK@Z 19 12 00001610 ?DLL_TBApiGetCalibrationAsStyle@@YGHEPAU_CalStyle@@@Z 20 13 00001590 ?DLL_TBApiGetCalibrationStyle@@YGHEHPAU_CalStyle@@@Z 21 14 00001570 ?DLL_TBApiGetCalibrationStyleByName@@YGHEPBD@Z 22 15 000016B0 ?DLL_TBApiGetComPortNames@@YGHPADH@Z 23 16 00001060 ?DLL_TBApiGetCommsErrors@@YGHEPAK@Z 24 17 000018A0 ?DLL_TBApiGetControllerDWORD@@YGHHPBDPAK@Z 25 18 00001880 ?DLL_TBApiGetControllerSZ@@YGHHPBDPADH@Z 26 19 00001840 ?DLL_TBApiGetDefaultDWORD@@YGHEPBDPAK@Z 27 1A 00001820 ?DLL_TBApiGetDefaultSZ@@YGHEPBDPADH@Z 28 1B 00001660 ?DLL_TBApiGetDeviceAddress@@YGHEPAK@Z 29 1C 00001270 ?DLL_TBApiGetDeviceFromSegment@@YGEPBD@Z 30 1D 00001680 ?DLL_TBApiGetDeviceIrq@@YGHEPAK@Z 31 1E 00001110 ?DLL_TBApiGetDriverVersion@@YGHPAD@Z 32 1F 00001630 ?DLL_TBApiGetEventSelectorOneHitMode@@YGHPAK@Z 33 20 00001140 ?DLL_TBApiGetEventSelectorState@@YGHPAK@Z 34 21 00001420 ?DLL_TBApiGetGlobalSettingDWORD@@YGHPBDPAK@Z 35 22 000018E0 ?DLL_TBApiGetHelpFileName@@YGXPADH@Z 36 23 00001990 ?DLL_TBApiGetIProduct@@YGHEPAK@Z 37 24 00001170 ?DLL_TBApiGetMacroStatus@@YGHEPAK@Z 38 25 00001920 ?DLL_TBApiGetMaxX@@YGHEPAK@Z 39 26 00001940 ?DLL_TBApiGetMaxY@@YGHEPAK@Z 40 27 00001970 ?DLL_TBApiGetMonitorPlacement@@YGHHAAUtagRECT@@@Z 41 28 00001120 ?DLL_TBApiGetNakMessage@@YGHEPADH@Z 42 29 00001260 ?DLL_TBApiGetNamedDevice@@YGEPBD@Z 43 2A 000010E0 ?DLL_TBApiGetNonPagedAllocation@@YGHPAK@Z 44 2B 00001100 ?DLL_TBApiGetNonPagedHWM@@YGHPAK@Z 45 2C 00001080 ?DLL_TBApiGetOverflowErrors@@YGHEPAK@Z 46 2D 000010D0 ?DLL_TBApiGetPagedAllocation@@YGHPAK@Z 47 2E 000010F0 ?DLL_TBApiGetPagedHWM@@YGHPAK@Z 48 2F 000017F0 ?DLL_TBApiGetRawEventState@@YGHEHPAH@Z 49 30 000014D0 ?DLL_TBApiGetRecentToolbarButton@@YGHEPBDGGPAGPAK@Z 50 31 00001240 ?DLL_TBApiGetRelativeDevice@@YGEH@Z 51 32 00001250 ?DLL_TBApiGetRelativeDeviceFromHandle@@YGHE@Z 52 33 000012A0 ?DLL_TBApiGetRotate@@YGHPAK@Z 53 34 000013A0 ?DLL_TBApiGetSettingDWORD@@YGHEPBDPAK@Z 54 35 00001470 ?DLL_TBApiGetSettingDWORDEx@@YGHEPBD0PAK@Z 55 36 00001380 ?DLL_TBApiGetSettingSZ@@YGHEPBDPADH@Z 56 37 00001440 ?DLL_TBApiGetSettingSZEx@@YGHEPBD0PADH@Z 57 38 000010A0 ?DLL_TBApiGetSyncErrors@@YGHEPAK@Z 58 39 000012D0 ?DLL_TBApiIgnoreToolbars@@YGHH@Z 59 3A 00001010 ?DLL_TBApiInit@@YGXE@Z 60 3B 00001900 ?DLL_TBApiInternalGetBundle@@YGEPBD@Z 61 3C 00001040 ?DLL_TBApiIsApiActive@@YGHXZ 62 3D 00001720 ?DLL_TBApiIsTouchingKeyboard@@YGHXZ 63 3E 000012C0 ?DLL_TBApiMousePortInterfaceEnable@@YGHH@Z 64 3F 00001020 ?DLL_TBApiOpen@@YGHXZ 65 40 00001780 ?DLL_TBApiRawDataMode@@YGHEH@Z 66 41 000017A0 ?DLL_TBApiRawDataModeBlockSize@@YGHEK@Z 67 42 000018C0 ?DLL_TBApiRegDeleteBranchCached@@YGHPAXPBD@Z 68 43 00001330 ?DLL_TBApiRegisterDataCallback@@YGHEKKP6GXKPAU_PointerData@@@Z@Z 69 44 000016F0 ?DLL_TBApiRegisterKeyboard@@YGHKJJJJ@Z 70 45 00001160 ?DLL_TBApiReinit@@YGHE@Z 71 46 000011C0 ?DLL_TBApiReloadNoApply@@YGHXZ 72 47 000015F0 ?DLL_TBApiRemoveCalibrationStyle@@YGHEPBD@Z 73 48 000019D0 ?DLL_TBApiRemoveDevice@@YGHH@Z 74 49 00001220 ?DLL_TBApiRemoveToolbar@@YGHPBD@Z 75 4A 000010C0 ?DLL_TBApiResetErrorCounts@@YGHXZ 76 4B 00001310 ?DLL_TBApiScaleCoordinates@@YGHPAJPAU_PointerData@@PAG2@Z 77 4C 00001280 ?DLL_TBApiSendData@@YGHEPAXKK@Z 78 4D 00001740 ?DLL_TBApiSendMacro@@YGHEPBD@Z 79 4E 00001960 ?DLL_TBApiSendUnloadMessage@@YGHXZ 80 4F 00001650 ?DLL_TBApiSetApiTraceLevel@@YGXH@Z 81 50 000015B0 ?DLL_TBApiSetCalibrationStyle@@YGHEHPAU_CalStyle@@@Z 82 51 00001640 ?DLL_TBApiSetEventSelectorOneHitMode@@YGHK@Z 83 52 00001150 ?DLL_TBApiSetEventSelectorState@@YGHK@Z 84 53 00001400 ?DLL_TBApiSetGlobalSettingDWORD@@YGHPBDK@Z 85 54 000012B0 ?DLL_TBApiSetRotate@@YGHK@Z 86 55 000012E0 ?DLL_TBApiSetScaleDimensions@@YGHPAJKKKK@Z 87 56 000017E0 ?DLL_TBApiSetScreenSaverMode@@YGHH@Z 88 57 000013E0 ?DLL_TBApiSetSettingDWORD@@YGHEPBDK@Z 89 58 000014B0 ?DLL_TBApiSetSettingDWORDEx@@YGHEPBD0K@Z 90 59 000013C0 ?DLL_TBApiSetSettingSZ@@YGHEPBD0@Z 91 5A 00001490 ?DLL_TBApiSetSettingSZEx@@YGHEPBD00@Z 92 5B 000017C0 ?DLL_TBApiSetVirtualDesktopMetrics@@YGHKKKK@Z 93 5C 00001810 ?DLL_TBApiSettingsCacheDirty@@YGHXZ 94 5D 00001050 ?DLL_TBApiTerminate@@YGXXZ 95 5E 00001350 ?DLL_TBApiUnregisterDataCallback@@YGHP6GXKPAU_PointerData@@@Z@Z 96 5F 00001360 ?DLL_TBApiUnregisterDataCallbackContext@@YGHP6GXKPAU_PointerData@@@ZK@Z 97 60 00001730 ?DLL_TBApiUnregisterKeyboard@@YGHK@Z 98 61 00001540 ?DLL_TBApiValidateDevice@@YGHE@Z 99 62 000022C0 _Java_tbcalibj_TBApiClass_LocateMorphFile@12 100 63 00001BD0 _Java_tbcalibj_TBApiClass_TBApiAbortDriver@8 101 64 000020D0 _Java_tbcalibj_TBApiClass_TBApiActivateCalibrationStyle@20 102 65 00001B00 _Java_tbcalibj_TBApiClass_TBApiAddToolbar@12 103 66 00001AD0 _Java_tbcalibj_TBApiClass_TBApiApply@8 104 67 00001AE0 _Java_tbcalibj_TBApiClass_TBApiApplyNoReload@8 105 68 00002160 _Java_tbcalibj_TBApiClass_TBApiAutoSetSwapXY@12 106 69 00001A00 _Java_tbcalibj_TBApiClass_TBApiClose@8 107 6A 00002040 _Java_tbcalibj_TBApiClass_TBApiDeviceSetMouseScaling@28 108 6B 00002070 _Java_tbcalibj_TBApiClass_TBApiDeviceStopMouseScaling@12 109 6C 00001AC0 _Java_tbcalibj_TBApiClass_TBApiDriverEnable@12 110 6D 00002090 _Java_tbcalibj_TBApiClass_TBApiGetCalibrationStyleByName@16 111 6E 00001A30 _Java_tbcalibj_TBApiClass_TBApiGetCommsErrors@16 112 6F 00001AB0 _Java_tbcalibj_TBApiClass_TBApiGetGlobalSettingDWORD@16 113 70 00001AB0 _Java_tbcalibj_TBApiClass_TBApiGetMacroStatus@16 114 71 00001B80 _Java_tbcalibj_TBApiClass_TBApiGetRelativeDevice@12 115 72 00001B90 _Java_tbcalibj_TBApiClass_TBApiGetRelativeDeviceFromHandle@12 116 73 00001C30 _Java_tbcalibj_TBApiClass_TBApiGetSettingDWORDEx@24 117 74 00001C00 _Java_tbcalibj_TBApiClass_TBApiIgnoreToolbars@12 118 75 000019E0 _Java_tbcalibj_TBApiClass_TBApiInit@12 119 76 00001A10 _Java_tbcalibj_TBApiClass_TBApiIsApiActive@8 120 77 00001BE0 _Java_tbcalibj_TBApiClass_TBApiMousePortInterfaceEnable@12 121 78 000019F0 _Java_tbcalibj_TBApiClass_TBApiOpen@8 122 79 00001AA0 _Java_tbcalibj_TBApiClass_TBApiReinit@12 123 7A 00001AF0 _Java_tbcalibj_TBApiClass_TBApiReloadNoApply@8 124 7B 00002120 _Java_tbcalibj_TBApiClass_TBApiRemoveCalibrationStyle@16 125 7C 00001B40 _Java_tbcalibj_TBApiClass_TBApiRemoveToolbar@12 126 7D 00001C30 _Java_tbcalibj_TBApiClass_TBApiScaleCoordinates@24 127 7E 00001A90 _Java_tbcalibj_TBApiClass_TBApiSetEventSelectorState@12 128 7F 00001E30 _Java_tbcalibj_TBApiClass_TBApiSetGlobalSettingDWORD@16 129 80 00001BC0 _Java_tbcalibj_TBApiClass_TBApiSetRotate@12 130 81 00001C20 _Java_tbcalibj_TBApiClass_TBApiSetScaleDimensions@28 131 82 00001DF0 _Java_tbcalibj_TBApiClass_TBApiSetSettingDWORD@20 132 83 00001FD0 _Java_tbcalibj_TBApiClass_TBApiSetSettingDWORDEx@24 133 84 00001D80 _Java_tbcalibj_TBApiClass_TBApiSetSettingSZ@20 134 85 00001F40 _Java_tbcalibj_TBApiClass_TBApiSetSettingSZEx@24 135 86 00001A20 _Java_tbcalibj_TBApiClass_TBApiTerminate@8 136 87 00002080 _Java_tbcalibj_TBApiClass_TBApiValidateDevice@12 137 88 00001BA0 _Java_tbcalibj_TBApiClass_TBJApiGetRotate@8 138 89 00003050 _Java_tbcalibj_TBApiClass_TBJApiUnregisterDataCallback@12 139 8A 00002270 _Java_tbcalibj_TBApiClass_TBMOEnglishText@16 140 8B 00002220 _Java_tbcalibj_TBApiClass_TBMOLocaliseText@16 141 8C 00002890 _Java_tbcalibj_TBApiClass__1TBApiGetCalibrationAsStyle@16 142 8D 00002340 _Java_tbcalibj_TBApiClass__1TBApiGetCalibrationStyle@20 143 8E 00001CF0 _Java_tbcalibj_TBApiClass__1TBApiGetSettingDWORD@20 144 8F 00001C40 _Java_tbcalibj_TBApiClass__1TBApiGetSettingSZ@24 145 90 00001E70 _Java_tbcalibj_TBApiClass__1TBApiGetSettingSZEx@28 146 91 000026C0 _Java_tbcalibj_TBApiClass__1TBApiSetCalibrationStyle@20 147 92 00003060 _Java_tbcalibj_TBApiClass__1TBJApiPollDataCallback@12 148 93 00002FB0 _Java_tbcalibj_TBApiClass__1TBJApiRegisterDataCallback@20 149 94 00002170 _Java_tbcalibj_TBApiClass__1TBMOInit@24 Summary 11000 .data 5000 .rdata 4000 .reloc 1C000 .text - Testing: - old v3: initializes (Shrike VM + ACR lab); no hardware for testing - new v3: initializes (Shrike VM); no hardware for testing - old v4 (v4.0-4.1.8): initializes (v4.1.6, Wombat VM), works physically (ACR lab, build 04.01.06R / 1222 / D13642, 2009) - new v4 (v4.1.10): initializes (build 04.01.10R / 2373 / D17205, Shrike VM). - Good stuff: single Whisker .exe per Edition. =============================================================================== 4.4.1 RNC (Nov 2012) =============================================================================== - Advantech driver: add support for "input ports and output ports numbered independently" mode via BioDAQ driver (for PCI-1756). =============================================================================== 4.4.2 RNC (3 Dec 2012) =============================================================================== - Flush server log before the housekeeping process starts. - "Disable video" option. =============================================================================== 4.4.3 RNC (19 Apr 2013) =============================================================================== - Fix for UPDD v5.0.2 bug in which UPDD crashes if its INI file is missing. =============================================================================== 4.4.4 RNC (23 Apr 2013) =============================================================================== - v4.4.3 was using the wrong paths for UPDD; try again. =============================================================================== 4.4.5 RNC (1 May 2013 -) =============================================================================== - cosmetic message changes =============================================================================== 4.4.6 RNC (16 July 2013) =============================================================================== - Support for NI PCIe-6509 (from Craig Hauser: functions exactly like the PCI-6509, but fits in a PCIe x1 slot). =============================================================================== 4.5.0 RNC (4 Feb 2014) =============================================================================== - Support for starting Whisker and a client immediately upon boot. - First, user must ensure that WhiskerReset does not run automatically (remove it from HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run). - Windows GUI processes can be run so they appear to return immediately (e.g.: whiskerserver.exe) or wait until they complete entirely (e.g.: START /WAIT whiskerserver.exe -- which will then yield the %errorlevel%), but it's not clear that they can hang around until a midpoint and then return to the calling program. - Listening sockets were open before m_bServerRunning was set (from WhiskerServer::InitNetwork); extra code added to reject them (in WhiskerServer::ProcessPendingAccept and WhiskerServer::ProcessPendingImmediateAccept). - InitNetwork split into DetermineIPAddress/InitNetwork and the latter moved to later. - Early m_bServerRunning(false) call. - Obtain IP address before starting disk log and change log so it no longer reports "n/a" for its IP addresses at startup. - Changed log message "Whisker server started." to "Whisker server ready to start comms." - After comms start, we still have: WhiskerServer::SetWindowInformation ... in log produces e.g. "Finished initializing sound devices to a window." CWhiskerServerApp::Run() -> pWhisker->StartPolling() -> WhiskerServer::StartTimer ... in log produces e.g. "High-resolution timer started" - Comms start moved to CWhiskerServerApp::Run(). Now all that occurs after it is CMainFrame::OnUpdateAllViews with HINT_DISPLAY_INITWINDOW -> CDisplayDevice::InitializeWindow() ... which is called by the MFC framework, though the hint request comes from CDisplayDevice::CDisplayDevice(). - So we hook into HINT_DISPLAY_DISPLAYADDED and start comms when all have been done. Plus, we may have none to do, in which case we continue to do it from CWhiskerServerApp::Run(). See m_bDisplayInitsPending, StartCommsAndPollingIfNoDisplayInitsPending(). - Working info: All crashes have logs ending: 09:52:29 (72618), Server, Connection request received 09:52:29 (72646), Server, Immediate socket connection request received 09:52:29 (72646), Server, Unlinked immediate socket accepted 09:52:29 (72647), Unlinked immsocket, Immediate socket says: Link 0_41 09:52:29 (72647), Server, Immediate socket is trying to link to a client 09:52:29 (72647), Server, Immediate socket was connected to client 0 ... except one case only followed by more: 10:09:21 (66961), Touchscreen 2, Scaling touches for 1024x600 display 10:09:21 (66964), Touchscreen 2, Scaling touches for 1024x600 display 10:09:21 (66995), Server, Connection request received 10:09:21 (67030), Server, Immediate socket connection request received 10:09:21 (67030), Server, Unlinked immediate socket accepted 10:09:21 (67030), Unlinked immsocket, Immediate socket says: Link 0_41 10:09:21 (67031), Server, Immediate socket is trying to link to a client 10:09:21 (67031), Server, Immediate socket was connected to client 0 10:09:22 (67718), Lafayette CANTAB-USB device 0, Input line status requested 10:09:22 (67723), Lafayette CANTAB-USB device 0, Input line status requested 10:09:22 (67723), Lafayette CANTAB-USB device 0, Line status received. 10:09:22 (67725), Lafayette CANTAB-USB device 0, Line status received. An appcompat.txt: A manifest.txt: Server=watson.microsoft.com UI LCID=1033 Flags=1672016 Brand=WINDOWS TitleName=WhiskerServer MFC Application DigPidRegPath=HKLM\Software\Microsoft\Windows NT\CurrentVersion\DigitalProductId ErrorText=The program is not responding. HeaderText=You chose to end the nonresponsive program, WhiskerServer MFC Application. EventLogSource=Application Hang Stage1URL= Stage1URL=/StageOne/WhiskerServer_exe/1_0_0_1/hungapp/0_0_0_0/00000000.htm Stage2URL= Stage2URL=/dw/stagetwo.asp?szAppName=WhiskerServer.exe&szAppVer=1.0.0.1&szModName=hungapp&szModVer=0.0.0.0&offset=00000000 DataFiles=C:\WINDOWS\TEMP\WER86f1.dir00\WhiskerServer.exe.mdmp|C:\WINDOWS\TEMP\WER86f1.dir00\appcompat.txt Heap=C:\WINDOWS\TEMP\WER86f1.dir00\WhiskerServer.exe.hdmp ErrorSubPath=WhiskerServer.exe\1.0.0.1\hungapp\0.0.0.0\00000000 DirectoryDelete=C:\WINDOWS\TEMP\WER86f1.dir00 About appcompat.txt and .dmp/.mdmp files: http://stackoverflow.com/questions/14335034/program-crash-how-to-read-appcompat-txt WINDBG ... http://msdn.microsoft.com/en-gb/windows/hardware/hh852365.aspx ... requires Windows SDK 7.1 ... Start > Programs > Debugging Tools for Windows (x86) ... File > Open > the .hdmp file ... enter as a command: !analyze -v Still not clear. However, the likely thing is that some sort of network comms is happening too early. Another possibility is that WhiskerReset was running and interfering with everything. - Changed thread interlocking code on Whisker shutdown - should now be guaranteed clean. WhiskerServer::~WhiskerServer(), ::StopTimer(), ::Poll(), ::PollMain(), etc. - Possible to crash Whisker with this sequence: DisplayClaim 0 -alias testdisplay; DisplayCreateDocument testdoc DisplayAddObject testdoc video1 video 0 0 "C:\temp\textureHorizontal.ABETwmv" -wait DisplayAddObject testdoc video2 video 500 0 "C:\temp\luminanceVertical.ABETwmv" -wait -loop DisplayShowDocument testdisplay testdoc VideoPlay testdoc video1 VideoPlay testdoc video2 VideoStop testdoc video1 VideoStop testdoc video2 DisplayDeleteObject testdoc video1 DisplayDeleteObject testdoc video2 Was due to deadlock between m_MovieCallbackVectorGuard and g_DirectXCritSec: (1) CMclAutoLock autoLock(m_MovieCallbackVectorGuard); from: WhiskerServer::DeregisterMovieCallback (IO_extras.cpp) DeregisterMovieCallback CSourceGraph::CancelCallback ... WHICH DOES: CMclAutoLock autoLock2(g_DirectXCritSec); // uses DirectX CSourceGraph::Shutdown ... WHICH DOES: CMclAutoLock autoLock2(g_DirectXCritSec); // uses DirectX CMovie2::CloseMovie // does NOT use g_DirectXCritSec CMovie2::~CMovie2 // does NOT use g_DirectXCritSec DeleteAndSetNull CDisplayObject_Video::~CDisplayObject_Video // does NOT use g_DirectXCritSec CDisplayDocument::DeleteObject CClient::Display_DeleteObject ... from the client socket thread (2) CMclAutoLock autoLock2(g_DirectXCritSec); // uses DirectX from: CSourceGraph::SeekToPosition CSourceGraph::MovieFinished CSourceGraph::ProcessBridgeEndOfSegment WhiskerServer::ProcessBridgeEndOfSegment ... WHICH DOES: CMclAutoLock autoLock(m_MovieCallbackVectorGuard); CMainFrame::OnBridgeEndOfSegment CWnd::OnWndMsg Possibilities are: - a flag-and-mop-up system so m_MovieCallbackVectorGuard is only accessed from one thread (but then: hard to create videos from clients too; what would be the point of the guard anyway, etc.) - edit CSourceGraph::Shutdown and CSourceGraph::CancelCallback so they don't ask for DirectX before they need it AND edit CSourceGraph::SetUpCallback similarly AND edit the chain that leads to it - make WhiskerServer's functions using m_MovieCallbackVectorGuard hold g_DirectXCritSec first - make WhiskerServer's functions using m_MovieCallbackVectorGuard let go of it before calling into movie functions. ... did this. - still an occasional lock, spontaneously, with looping videos main thread: m_pPin->BeginFlush(); from: COutputQueue::BeginFlush BridgeSourceOutput::DeliverBeginFlush BridgeStream::BeginFlush BridgeSinkInput::BeginFlush ... quartz.dll and similar ... BridgeSink::SetPositions ... quartz.dll ... CSourceGraph::SeekToPosition (calling m_pMediaSeeking->SetPositions) CSourceGraph::MovieFinished CSourceGraph::ProcessBridgeEndOfSegment WhiskerServer::ProcessBridgeEndOfSegment CMainFrame::OnBridgeEndOfSegment CWnd::OnWndMsg Amongst the other threads: _threadstartex thread: this one has a corrupted call stack, unreliable: if ( pins.Begin() == pins.End() ) from BridgeSink::GetStopPosition several COutputQueue::InitialThreadProc threads doing DbgWaitForSingleObject(m_hSem); from COutputQueue::ThreadProc ... these presumably being GMFBridge worker threads So: in CSourceGraph::MovieFinished, use SeekToPosition(0, FALSE); instead of SeekToPosition(0, TRUE); IF ANY FURTHER PROBLEMS, consider trying AM_SEEKING_Segment / AM_SEEKING_NoFlush options to m_pMediaSeeking->SetPositions() call from CSourceGraph::SeekToPosition(), though I'm not sure whether GMFBridge is equally happy with it in that mode. Anyway, have been unable to reproduce the crash since changing that call. ... also tried those flags... ... but the framework alters them en route to BridgeSink::SetPositions(), it seems! ... so hacked BridgeSink::SetPositions() to add AM_SEEKING_NoFlush... ... no, still happened (after e.g. 34 minutes of continuous double-video double-view with audio in debug mode, or at the first loop in release mode...) ... tried implementing this bit from GMFBridge 1.0.0.20 (15 May 2012): "When copying audio samples, nBlockAlign is now respected correctly." ... is inside BridgeSinkInput::CopySample ... no, still locks. ... potentially the same problem as July 2011. ... CSourceGraph::SeekToPosition was using a GetState(100, &fs) call; why not INFINITE? ... made no difference. ... additional CAutoLock streaminglock(&m_csReceive); calls on BridgeSinkInput::BeginFlush() and BridgeSinkInput::EndFlush() ... definite improvement, managed 1 loop in release mode with 2 views/2 videos/audio... ... no, actually managed another deadlock... with a DirectX global critsec relating to the GUI thread/loss of a window DirectX surface ... but still crashing; just as above; no clue as to what the m_pPin->BeginFlush() call is waiting on; the COutputQueue::... threads are all waiting on different semaphores; except one that's at hr = pQSink->Notify(pSender, q); from BridgeStream::NotifyQuality BridgeSourceOutput::Notify ... HRESULT hr = m_pInputPin->ReceiveMultiple(m_ppSamples, lNumberToSend, &nProcessed); COutputQueue::ThreadProc ... anyway, major other clue is that the seek-to-position code works from the GUI thread went done manually, but not when looping back to the start, and the difference is the use of the CMovie2::SeekToPosition (works) versus CSourceGraph::SeekToPosition (doesn't) - so presumably the answer is that the rest of the graph (inc. renderers) needs to be paused, and pausing the source graph doesn't do this. ... rename some functions in CSourceGraph; too much overlap with CMovie2 ... so: callbacks still set up by the CSourceGraph, since its objects are the things that are involved in the callback events, but passing m_pMovie rather than itself, and the CMovie2 class now handles the callbacks. ... Sorted! - some code refactoring, esp. that different editions have much more in common (much simpler code, identical linked libraries; difference only in core processing code) - NB on XP under VirtualBox, DS_VerifyVMR() fails, indirectly, at its CoCreateInstance() call and just crashes at the Whisker startup dialogue (outside our control; possible known Microsoft bug). So: - video disabled by default (and the workaround for this crash is to disable video in the registry) - warning message with explanation added to log =============================================================================== 4.5.1 RNC (4-16 May 2014) =============================================================================== - Debugging crash in release mode upon connection. Likely due to a deadlock. void CImmediateSocket::OnReceive(int nErrorCode) ... had CMclAutoLock autoLock(m_CritSec) held throughout, not just in the sub-section (two calls) THEORIES FOR DEADLOCK: 1. A. CImmediateSocket::OnReceive ... CMclAutoLock autoLock(m_CritSec) // !!! HELD TOO LONG. m_pServer->LinkImmediateSocket(this, ...) ... CThreadAutoLock autoLock(m_ClientVectorGuard); (*it)->WillAcceptImmediateSocket(strCode) pSocket->SendWithNewline("Failure"); pClient->AttachImmediateSocket(pSocket); m_pImmediateSocket = pImmSocket; m_pImmediateSocket->SetClient(this); // 2. Transfer control to our thread m_pClientThread->m_hImmediateSocket = m_pImmediateSocket->Detach(); m_pClientThread->PostThreadMessage( WM_ACCEPT_IMMSOCKET, 0, 0); msg.Format( "Immediate socket was connected to client %d", pClient->View_GetClientNum() ); ... and that message was always seen ... hmm, not convincing... should exit, and then no "boom". B. BOOL CClientThread::PreTranslateMessage( MSG* pMsg) case WM_ACCEPT_IMMSOCKET: AcceptImmSocket(); void CClientThread::AcceptImmSocket() m_pOwner->m_pImmediateSocket->Attach(m_hImmediateSocket); m_pOwner->m_pImmediateSocket->SendWithNewline("Success"); CImmediateSocket::SendWithNewline(const CString&msg) return Send(msg + "\n"); int CImmediateSocket::Send(const CString&msg) CMclAutoLock autoLock(m_CritSec); // !!! BOOM? Not sure. 2. Logically, if "Immediate socket was connected to client %d" message was always seen, then WhiskerServer::LinkImmediateSocket completed, and CImmediateSocket::OnReceive completed (for the Link command). The only other thread that should be processing communications for that socket (and therefore holding up the client's blocking socket call) should be from CClientThread::PreTranslateMessage, as above. However, the only time that SendWithNewline calls CClient is for pClient->CommSendError(), which is rare and doesn't lock much of interest, and the only thing it locks is the immediate socket's critsec. - Seed random number generator, used for client linking codes - Debugging options: #define WHISKER_TRACE_STATUS_MESSAGE_THREADS #define RELEASE_TRACE_MACROS - Shifted a few critsecs to autolocks rather than Enter/Leave calls - Socket locks on Attach() and Detach() calls, in case that's the problem. - Socket rewrite so everything derives from CWhiskerAsyncSocketType (= IPv6AsyncSocket) and all derived classes use the parent class's (protected) m_CritSec. CClientSocket - done CImmediateSocket - doe NOT CBerlinSocket (at least for now) - this is a "client-style" socket, not a "server-style" socket, and does its own thing - See extensive investigation trace -- looks like heap corruption, rather than deadlock. - from byte* x = new byte[length]; ... somefunc(x); ... delete[] porttypearr; to vector x(length); somefunc(&x[0]); ... // autodeletes as per e.g. http://stackoverflow.com/questions/6632971/what-is-the-difference-between-stdarray-and-stdvector-when-do-you-use-one-o http://stackoverflow.com/questions/849168/are-stdvector-elements-guaranteed-to-be-contiguous ... e.g. in BNCDIOBoard.cpp - SyncPtr converted to scoped_ptr (mostly) given that thread safety should be managed in the classes, not via the way the classes are called. And this eliminates "delete" calls. - widespread use of shared_ptr to avoid "delete" calls ... except CDisplayDocument*, because that interacts heavily with the MFC document/view classes and it seems like a great deal of trouble. ... changed to shared_ptr within CClient, raw pointers exposed to MFC code ... and a tiny bit in CIPv6AsyncSocket, which looks too embedded in style with this sort of thing: _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState; // = AfxGetModuleThreadState() to mess around with. ... some safety-check updates to CIPv6AsyncSocket from the newer CAsyncSocket in sockcore.cpp - Application Verifier: runs happily on Release_Basic edition, but fails when UPDD is loaded - No memory leaks in Release_Basic edition, but plenty when UPDD is loaded. ... ah, no, actually some in strcore.cpp -- may be false-positive -- http://support.microsoft.com/kb/167929 - Conclusion: UPDD's fault? Not sure. - Compile on level 4 warnings, now without complaint. - One change made in CIPv6AsyncSocket::Bind; sp_freeaddrinfo not previously called. That one is plausibly critical! - cppcheck --force *.h *.cpp - one #ifdef typo in CMainFrame::CMainFrame - this file is unused: [AsyncSocketExLayer.cpp:531]: (error) Uninitialized variable: res - real ones: fixed [ConfigAdvantechDlg.cpp:28]: (error) Buffer is accessed out of bounds: m_bReverseInputs fixed [ConfigAdvantechDlg.cpp:28]: (error) Array 'm_bReverseInputs[8]' accessed at index 23, which is out of bounds. fixed [ConfigAdvantechDlg.cpp:29]: (error) Buffer is accessed out of bounds: m_bReverseOutputs fixed [ConfigAdvantechDlg.cpp:29]: (error) Array 'm_bReverseOutputs[8]' accessed at index 23, which is out of bounds. fixed [ConfigAdvantechDlg.cpp:30]: (error) Buffer is accessed out of bounds: m_bUseBoard fixed [ConfigAdvantechDlg.cpp:30]: (error) Array 'm_bUseBoard[8]' accessed at index 23, which is out of bounds. fixed [NIControllingABETDIOboard.cpp:702]: (error) Array 'm_bChamberNeedsWriting[4]' accessed at index 7, which is out of bounds. fixed (likely no functional change) [amplicondioboard.cpp:954]: (error) Uninitialized variable: ulGroupGains - CSoftwareLine destructor was calling into Lafayette USB code at an unsafe time. Revised again; bug was introduced just now when WhiskerServer destructor revised. - boost::atomic<> rather than CAtomic<> for simple (e.g. int, bool) types. (May be faster.) Note that this class is NOT OK for classes that are not trivially copyable, so stayed with CAtomic<> for CString. Suitability: http://www.boost.org/doc/libs/1_54_0/doc/html/atomic/interface.html Copying: see also: http://stackoverflow.com/questions/14182258/c11-write-move-constructor-with-atomicbool-member - Recompiled Mcl/Mcl4Mfc libraries. - Upgraded cryptopp to 5.6.2 (2013) from 5.6.0 (2009). - Removed nearly all "using namespace" statements. - Fixed memory-leak detector: http://stackoverflow.com/questions/6484800/memory-leak-problems ... see WhiskerServerApp.cpp ... no leaks. =============================================================================== 4.6 RNC (10 Nov 2014 - 7 Dec 2014) =============================================================================== - ClientDispatcher, as the CClient::Parse function was a bit dire in terms of unnecessary complexity. - ReportComment command (with Whisker SDK 4.6) - Report hardware descriptions from UPDD. - VideoSetVolume: sets volume associated with a video. cf. AudioSetSoundVolume - Allow audio tracks of video files to be played on left/right split audio devices. CClient::Display_SetAudioDevice The video's audio stream is passed to a DirectX audio renderer directly (rather than via the existing method of sound splitting). CDisplayView::DrawDocument CDisplayView::DrawVideoObjects ... if (m_pDisplayDevice) strAudioDirectXName = m_pDisplayDevice->GetPreferredAudioDevice(); [... having been set by CClient::Display_SetAudioDevice, calling WhiskerServer::Audio_GetDeviceDirectXName ... ] CDisplayObject_Video::DrawVideo( ... strAudioDirectXName ... ) CMovie2::AddAudioRenderer CMovie2::AddRenderer CRenderGraph() DS_AddSoundRenderer ... pSysDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory... ... check for matching name For these: IBasicAudio interface to the DirectSound renderer filter that's enumerated by CLSID_AudioRendererCategory http://msdn.microsoft.com/en-us/library/windows/desktop/dd389532(v=vs.85).aspx PROBLEM: VFW_E_MONO_AUDIO_HW Cannot change balance because audio device is mono only. Solution: This only happened when no source graph was attached. So call it from CRenderGraph::Run() instead. Works fine. =============================================================================== 4.6.1 RNC (17 Jan 2015-7 Apr 2015) =============================================================================== - typo fixed in response message - help updated with screenshots of UPDD 4.1.10 configuration - m_vColumnWidths_TouchscreenView; KVS_View_Touchscreen_Col - bugfix: touchscreen description boxes IDC_EDESC4/IDC_EDESC5 were the wrong way round visually in the dialogue box - broader support for National Instruments cards via NIDAQmx, via CNationalInstrumentsDIOboard - NOTE that the National Instruments simulated cards, by default, tick their inputs over at very high rates. This can make aspects of Whisker (e.g. user interface aspects such as the Line Details dialogue) run very slowly. - WireShark (formerly Ethereal) testing: - Ubuntu host, e.g. shrike on 192.168.10.91 - VirtualBox WinXP guest, using Bridged adapter (meaning it needs its own IP address -- using NAT with port forwarding is a nuisance because Whisker adds the secondary ports) e.g. shrike on 192.168.10.93 - For example, shrike - Display filter: e.g. ip.addr == 192.168.10.91 - Follow TCP stream So, for example: (1) ip.addr == 192.168.10.91 && tcp.port == 3233 (2) Follow TCP stream (3) Since in this case the immediate port is 1147, look at the other stream with: ip.addr == 192.168.10.91 && tcp.port == 1147 - Background colour option to video creation. See CDisplayObject_Video::DrawBlankBackground. NOTE THAT THIS IS SUBOPTIMAL. What works: - Create and display a single video on a visible document - Create and display >1 video on a hidden document, then show it What doesn't: - When videos are paused or stopped, the background colour is not restored. - If a new video is added on a visible document, other videos' backgrounds turn black. - If two videos are placed and then their document is displayed, there is a visible sequence effect. [SOLUTION TO THIS: randomize the order.] Essentially, there is no mechanism I've found within the VMR code to set an actual background colour, so the current system uses a half-baked method of painting a rectangle over the top once the video's been lined up. CONSIDER THIS METHOD EXPERIMENTAL. =============================================================================== 4.6.2 RNC (8 Apr 2015) =============================================================================== - Echo command. - Further network testing (with NetThrashClient) - client-side problem found; socket code made common to both clientlib/server. CBerlinSocket upgraded to new one. =============================================================================== 4.6.3 RNC (28 Apr 2017 to 1 May 2017) =============================================================================== - More debugging of touchscreen events, to see if a problem with specific touchscreens relates to Whisker or to UPDD not scaling coordinates properly as per its API docs at https://touch-base.com/documentation/apifunctions/tbapiScaleCoordinates.htm ... re UPDD 5.1.1464 and 5.1.1483 ... and touchscreens including but not limited to Clientop - Turns out the problem was that UPDD changed its API between 5.0.2 and 5.1.x so that touch up/down events, formerly via "button" events and _ReadDataTypeEvent, are now via "physicalEvent" events, via _ReadDataTypePhysicalEvent. Thus: interface changes and generalization, as well as more thorough debugging messages. - Also, the "Mouse behaviour" (Mouse/Ignore/Touchscreen) option (in "Configure hardware" -> "Display Devices") was misleading. Because CDisplayView::TouchscreenEvent() called CDisplayView::IncomingMouseInput(), and that implemented if (m_eMouseBehaviour == MOUSEBEHAV_IGNORE) { return; } then the "ignore" setting meant that touchscreen input was also ignored. THUS: split IncomingMouseInput into IncomingMouseInput and IncomingMouseOrTouchInput; the former handles the "mouse mimics touchscreen" or "mouse is ignored" option stuff. =============================================================================== 4.7.0 RNC (26 Jan 2018) =============================================================================== - Bugfixes to e.g. CDisplayDocument::SendToBack and similar - was incrementing invalidated operators. Not used, so won't ever have affected users, but now picked up by better STL vector checks in debug mode (which crashed with e.g. a _has_container() assertion. Checked those with .erase(): CDisplayDocument::DeleteObject -- was fine CDisplayDocument::BringToFront -- FIXED CDisplayDocument::SendToBack -- FIXED - Bugfix (apparent in debug mode) whereby an attempt was made to restart network comms (making the socket code complain) when an "extra" main window was created (in CMainFrame::OnUpdateAllViews / case HINT_DISPLAY_INITWINDOW). Fixed with new WhiskerServer.m_bWantCommsStart flag. - Video positioning or scaling bug as reported by Lafayette. This is quite odd. It occurs primarily with monitors in portrait mode, e.g. 768x1024. The bottom of videos is clipped. Typically, with a monitor of W 768, H 1024, the video is clipped at y=768 (not appearing below this). It occurs in both "main" windows and the Whisker console, though it's less likely to be noticed in the console because the console window extends to the bottom of the screen less often. (It also occurs in Whisker "extra" display windows.) As you move the console window up/down, the clipping edge stays fixed relative to the monitor, and therefore moves relative to the window. It also occurs when scaling. For example: monitor in 1200x1600 mode this command sequence: DisplayClaim 0 -alias DISPLAY0 DisplayCreateDocument testDoc # +/-: DisplayScaleDocuments 0 on DisplaySetDocumentSize testDoc 768 1024 DisplayShowDocument DISPLAY0 testDoc DisplayAddObject testDoc Background@0 rectangle 0 0 768 1024 -penstyle solid -penwidth 1 -pencolour 255 0 255 -brushsolid 255 0 255 -brushopaque DisplayAddObject testDoc Video@1 video 68 640 "C:\whisker\private_test_media\one.wmv" -loop -wait -noaudio -width 217 -height 217 VideoPlay testDoc Video@1 With DisplayScaleDocuments OFF, there's no clipping. With DisplayScaleDocuments ON, there is clipping. If you keep the scaling off and instead of the DisplayClaim, use this: DisplayCreateDevice DISPLAY0 -resize on -directdraw off -rectangle 50 50 1024 1024 then there is *also* clipping, and it's 75% of the way down the screen i.e. at y=1200, not at y=768 So, in summary, something is clipping videos for y_MONITOR > MONITOR_width. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ That is pretty bizarre. But it's not everything; the Windows 10 media player doesn't do it. Let's also try a giant video: DisplayClaim 0 -alias DISPLAY0 AudioClaim 1 -alias AUDIO0 DisplaySetAudioDevice DISPLAY0 AUDIO0 DisplayCreateDocument testDoc DisplayShowDocument DISPLAY0 testDoc DisplayAddObject testDoc Video@1 video 0 0 "C:\whisker\private_test_media\one.wmv" -loop -wait -noaudio -width 1200 -height 1600 VideoPlay testDoc Video@1 ... also clipped at y>1200. Also, if you move the server's copy between a portrait 1200x1600 monitor and a landscape 1600x1200 monitor, the clipping goes away on the landscape one. (Sometimes the monitor transition makes the pipeline stop, though!) Hard to find much that's relevant: http://www.virtualdub.org/blog/pivot/entry.php?id=306 ... though the VMR9 still looks perfectly supported: https://msdn.microsoft.com/en-us/library/windows/desktop/dd407344%28v=vs.85%29.aspx https://social.msdn.microsoft.com/Forums/en-US/98141ff2-2824-4517-ae35-f370a6317475/vmr9-strange-problem?forum=windowsdirectshowdevelopment ... see StressEVR application The possible bug locations are: - my code - GMFBridge -- I very much doubt it; no display code, just data routing - Microsoft's VMR9 and related DirectShow code -- YES; see below. Try this: C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\vmr9\vmrplayer ... has the SAME BUG. So... replace VMR9 with EVR? ~~~~~~~~~~~~~~~~~~~~~~ https://msdn.microsoft.com/en-us/library/windows/desktop/ms694916%28v=vs.85%29.aspx Should use EVR and fall back to VMR9 if unavailable. Temporary #define EVR_REPLACES_VMR, removed on success. - EVR, VMR9, and VMR7 all implemented and used in that order depending on availability. Note: VMR7 doesn't have the bug and nor does EVR, but VMR9 does. - Add status display: "running as administrator or not". - Side issue: wrong Windows SDK version used. Added check in stdafx.h via . - Side issue: "cannot update program database... vc90.pdb" See https://stackoverflow.com/questions/126751/compilation-fails-randomly-cannot-open-program-database Switched debug info to C7 format: Project Options -> C/C++ -> General -> Debug Information Format and set it to C7. - Side issue: replaced deprecated lstrcpy() calls with StringCchCopy(). (They were all safe anyway.) - Compiles with no warnings. - Also, BIZARRE INCIDENTAL BUG: Pressing Ctrl-F (to open the Find dialogue box) in Visual Studio 2008 forces a screen refresh of the Whisker full-screen display (even if the VS dialogue box opens on a different monitor). This in turn causes an audio/ video glitch. (Regardless of VMR7/VMR9/EVR.) Other application dialogue boxes don't do this. Doesn't look like any other windows are repainted, either. - Drawing sequence updated. From bottom to top, it's now, consistently: - main objects in their Z order - video "null" rectangles - touch crosshairs/gunsight - actual video (with crosshairs alpha-blended in) - Bugfix: ghost rectangle created on server copy when video added (before playback) - Only happens with real video, not "null" video rectangle, so maybe to do with the renderer initializing in the wrong place? - Ah, no. It's due to CDisplayObject_Video::DrawBlankBackground(). - It needs logical coordinates and was being given device coordinates. Fixed. - Bug found 9 Apr 2016 now fixed: text not being scaled when DisplayScaleDocuments on. DisplayClaim 0 -alias display DisplayCreateDocument doc DisplayShowDocument display doc DisplaySetBackgroundColour doc 255 0 255 DisplayScaleDocuments display on DisplayAddObject doc mytext text 50 50 "Hello! I am some text." -height 50 -font Arial -textcolour 255 255 255 -backcolour 0 0 255 -opaque -top -left DisplayAddObject doc myrect rectangle 50 0 100 50 -brushsolid 255 0 0 DisplayAddObject doc myrect rectangle 500 500 550 550 -brushsolid 0 255 0 Yes. The font size is scaled wrongly. Specifically, the font is kept intrinsically proportional, when its aspect ratio should be distorted to match the overall aspect ratio. Test alignment: DisplayAddObject doc vline line 500 0 500 1000 -penstyle solid -penwidth 1 -pencolour 0 0 0 DisplayAddObject doc hline1 line 0 200 1000 200 -penstyle solid -penwidth 1 -pencolour 0 0 0 DisplayAddObject doc hline2 line 0 400 1000 400 -penstyle solid -penwidth 1 -pencolour 0 0 0 DisplayAddObject doc hline3 line 0 600 1000 600 -penstyle solid -penwidth 1 -pencolour 0 0 0 DisplayAddObject doc hline4 line 0 800 1000 800 -penstyle solid -penwidth 1 -pencolour 0 0 0 DisplayAddObject doc mytext1 text 500 200 "Middle Centre Opaque" -height 50 -font Arial -textcolour 255 255 255 -backcolour 0 0 255 -opaque -middle -centre DisplayAddObject doc mytext2 text 500 400 "Baseline Centre Transparent" -height 50 -font Arial -textcolour 255 255 255 -backcolour 0 0 255 -baseline -centre DisplayAddObject doc mytext3 text 500 600 "Top Left Opaque" -height 50 -font Arial -textcolour 255 255 255 -backcolour 0 0 255 -opaque -top -left DisplayAddObject doc mytext4 text 500 800 "Bottom Right Transparent" -height 50 -font Arial -textcolour 255 255 255 -backcolour 0 0 255 -bottom -right DisplayAddObject doc rect1 rectangle 50 50 100 100 -brushsolid 255 0 0 -penstyle solid -penwidth 1 -pencolour 0 0 0 DisplayAddObject doc bmp1 bitmap 0 0 C:\Whisker\Whisker_help\Task_bitmaps\AttMem_Params.bmp Glorious. - Bugfix for line drawing with "-penwidth 1": they were sometimes invisible. CDisplayObject_Line::CDisplayObject_Line was if (logpen.lopnWidth.x > 1) { now if (logpen.lopnWidth.x > 0) { Similarly in: ... well, all the display objects. - Moved hardware config from privileged section of the registry (HKEY_CURRENT_CONFIG, within HKEY_LOCAL_MACHINE) to unprivileged (HKEY_CURRENT_USER, which now contains all Whisker information). Thoughts: What's administrator access required for? Is it (1) HKLM registry and (2) priority timing? Yes. ... remove the former requirement? (by having WhiskerHardwareConfig write to HKEY_CURRENT_USER instead of HKEY_CURRENT_CONFIG, and checking both for older version information via CWhiskerHardwareConfig::FindBestKeyRootString). Done. Disadvantages: - For systems where multiple users log in, Whisker must now be configured for each user. Advantages: - In practice, more people are confused by their registry settings not becoming permanent, so this should remove a source of confusion. - You can configure things a bit differently from your fellow users - not applicable to physical hardware, necessarily, but other quasi-hardware things (e.g. number of fake lines). - General good practice to avoid admin requirements (and machines are much faster than in 1999 so the realtime process priority is probably not so critical for many users). Neutral: - Just because you configured your hardware in administrator mode doesn't mean that you always run Whisker in administrator mode, i.e. using a privileged registry section doesn't guarantee the availability of runtime process priority. =============================================================================== 4.7.1 RNC (17 Mar 2018) =============================================================================== - Further locks added to CLafayetteUSBDIOboard, as serial overflow errors arising; Lafayette report that the DLL is not thread-safe, and not all functions used g_LafayetteUSBCritSec. Note that all DLL functions are named Laf_*. Locking added to: CLafayetteUSBDIOboard::CLafayetteUSBDIOboard CLafayetteUSBDIOboard::vSetOutput =============================================================================== 4.7.2 RNC (31 Mar 2018) =============================================================================== - Ongoing problems with the Lafayette USB board. Diagnosis from Craig Hauser: - during shutdown, Whisker sends lots of commands (mostly "set output state" as it deletes its sofware lines), but by this point has stopped polling to receive messages; - the kit/DLL will continue to be sending messages [RNC: including ACK messages]; - as a result, the USB kit gets out of whack and starts generating checksum/ overflow errors; - the Lafayette DLL does no buffering and is just a thin layer over calls to FTDI functions; - RNC: so this probably means that the FTDI input buffer is overflowing; see http://www.ftdichip.com/Documents/AppNotes/AN232B-04_DataLatencyFlow.pdf http://www.ftdichip.com/Support/Documents/ProgramGuides/D2XX_Programmer's_Guide(FT_000071).pdf - So, we need to stop its input buffer overflowing. - Therefore, add manual calls to ProcessPendingMessage() in every function that might cause the USB kit to send us a message. Search for: ProcessPendingMessages(); // added 2018-03-31 to prevent DLL input buffer overflow Those calls are always at the END of the functions using them, to aid re-entrancy if required. =============================================================================== 4.7.3 RNC (2 Apr 2018) =============================================================================== - Attempt to fix deadlock bug introduced in 4.7.2. Likely problem is that many functions in CLafayetteUSBDIOboard hold these locks: (1) m_CritSec, (2) g_LafayetteUSBCritSec. If a function that was only holding g_LafayetteUSBCritSec then calls one of those other functions, there's deadlock potential. See new locking rule at top of LafayetteUSBDIOboard.cpp. Search for CMclAutoLock autoLock(m_CritSec); // ~~~ Avoid deadlock 2018-04-02; see top The specific most likely case was that - vSetOutput() held (A1) g_LafayetteUSBCritSec but NOT m_CritSec - vSetOutput() called ProcessPendingMessages() [new in 4.7.2] - ProcessPendingMessages() held (A2, B1) m_CritSec // DEADLOCK POTENTIAL HERE // versus another thread (i.e. the polling thread) calling // ProcessPendingMessages() to hold (B1) m_CritSec then // (B2) g_LafayetteUSBCritSec. - ProcessPendingMessages() held (B2) g_LafayetteUSBCritSec =============================================================================== 4.7.4 RNC (19 Apr 2018) =============================================================================== - Ongoing problems with Lafayette USB code. My thoughts (to Craig Hauser e.g. 17 Apr 2018): ... this is almost certainly a buffer overflow problem, because: - it is a problem that has manifested with a change in computer speed, suggesting a timing problem; - the first error to be reported on subsequent sessions (after the one that makes things go wrong) is an overflow error, based on your logs; - you have worked out that it is a consequence of Whisker not reading early enough (before it starts writing) and/or not reading late enough (after it finishes writing) [and while this may be the proximal cause, it is not the "root" cause in the sense that this doesn't explain the adverse consequences of not reading]; - Whisker doesn't do anything with the information read, at that point; therefore, the problem must be a related to a consequence of reading upon the Lafayette DLL, FTDI DLL, or USB device; - there are very few likely side effects of reading except to clear the input buffer (I can't think of any others but am open to suggestions); - writing to the USB device makes it return data, i.e. data that will end up in the input buffer; - the Lafayette code tells us that the input buffer, of 64 bytes, can fill in 0.69 ms; - the FTDI documentation [http://www.ftdichip.com/Support/Documents/AppNotes/AN232B-03_D2XXDataThroughput.pdf , section 1.3, p4] says that if the read buffer overflows, the FTDI driver stops issuing USB requests (which presumably has adverse consequences). The documentation even says "the rate at which the application issues read requests has a direct bearing on the driver's ability to keep issuing USB requests". - The same FTDI documentation [section 1.4, p5] adds that the USB packet size is 64 bytes. (I don't know whether that's the final packet size that arrives with your device -- perhaps this is irrelevant -- but if it's not, this would mean that few packets can fit in the input buffer.) I predict that increasing the input buffer size (e.g. from 64 bytes to 4k) will reduce the frequency of the observed problem, with the existing version(s) of Whisker, and I suggest strongly that you have a go with that while I'm altering the threading code. As I said, I will make these changes to Whisker, but I would like to avoid the problem recurring (particularly as changes I've already made, to read after the last write, haven't been enough to solve the problem) and it may be that a 1 kHz read cycle is still pushing the boundaries of safety with a very small buffer, so I will try to make the reads operate substantially faster than that. - Threading pseudocode thoughts: // Assume the Lafayette/FTDI DLLs are entirely non-thread-safe. // Do not hold m_input_vector_guard at the same time as the DLL lock. CLafayetteUSBDIOboard::CLafayetteUSBDIOboard() { ... init_rx_comms_thread(); wait_for_comms_thread_to_be_started(); set_baud_rate(); other_init_stuff(); } CLafayetteUSBDIOboard::~CLafayetteUSBDIOboard() { stop_rx_comms_thread(); wait_for_rx_comms_thread_to_stop(); } CLafayetteUSBDIOboard::SetBaudRate() { // send USB message; call DLL } CLafayetteUSBDIOboard::ProcessPendingMessages() { MessageClass msg; bool found_message = true; while (found_message) { { // Lock input vector as briefly as possible. CMclAutoLock autoLock3(m_input_vector_guard); found_message = !m_input_vector.empty(); if (found_message) { msg = m_input_vector.front(); m_input_vector.pop_front(); } } // Input vector lock is now released, and we may have a message if (found_message) { ProcessMessage(msg); } } } // --- LafayetteUSBReaderThread::Main() { CantabUSB_PartialState state; CantabUSB_Message msg; int retcode; while (!m_please_stop_signal) { { CMclAutoLock autoLock2(g_LafayetteUSBCritSec); // ~~~ This function calls the Lafayette CANTAB-USB library retcode = Laf_CantabUSB_ReadMessageA(m_usbh, &state); } if (retcode != CU_OK) { // Something WENT WRONG. StatusMessage("Error from CantabUSB_ReadMessageA()"); DescribeReturnCodeError(retcode, ...); } // If message is now complete, process it. if (state.finished) { CantabUSB_Message msg = state.message; // Now, msg has our message in it. CMclAutoLock autoLock3(*m_p_input_vector_guard); *m_p_input_vector.push_back(msg); } // RISK OF LOCKING THE DLL TOO MUCH HERE? SPINNING... // (May block writes via DLL on other thread? Or is Windows critsec // scheduling good enough?) // Can't sleep for <1ms (no Windows sleep function that takes a // microsecond parameter). } } WITH THIS METHOD: - can't use a blocking read call because of (a) need to check thread's m_please_stop_signal from time to time, and (b) would lock DLL nearly all the time - may lock DLL a significant fraction of the time even so - potential high CPU usage ALTERNATIVE: - combined tx + rx thread (... one for all boards? Doesn't really matter; only 1 board supported by Lafayette). - only this thread talks to DLL - outbound queue too (with command class) - but still couldn't use blocking read (need to check outbound queue) No benefit from one tx + one rx thread (same basic problems). Lafayette DLL does not expose FTDI event notifications, e.g. FT_SetEventNotification. http://www.ftdichip.com/Support/Knowledgebase/index.html?ft_seteventnotification.htm - However, this was probably pointless. See subsequent e-mail 17/4/18. My best guess is that: - Whisker shuts down and sends 14 x "turn output off" + 1 x "safety relay off" command; - Whisker checks for all messages, but there are none because the device is slow; - Whisker closes the Lafayette and therefore FTDI driver; - The device sends replies but isn't being prevented by flow control; - The FTDI's *chip* buffer (fixed at 384 bytes; see http://www.ftdichip.com/Support/Documents/AppNotes/AN232B-04_DataLatencyFlow.pdf section 4.3) overflows; - Subsequent confusion. A/w Craig 17/4/18. Also subsequent confusion re logs/versions. - Therefore, CHANGE: - at shutdown, wait for CU_ACK for CU_SAFETY_RELAY_OFF, or up to a fixed time. See m_bReceivedSafetyRelayOffAck, MAX_SHUTDOWN_WAIT_TIME_MS. =============================================================================== 4.7.5 RNC (21 Apr 2018) =============================================================================== - Turns out the actual USB problems are: (1) The Lafayette USB device can't cope with messages being sent fast at it. (2) It can't cope with messages not being read from it fast, for as long as it's in operation. Both might cause buffer overflows (and certainly the latter). CHANGE: threaded (tx + rx) model. - NOTE THAT BUFFER OVERFLOW ERRORS ALSO OCCUR IF YOU SEND COMMANDS WITH THE SAFETY RELAY TURNED OFF, e.g. "Invalid start byte" -> "Controller board receive buffer overflow" from the Cantab-USB-Tester program. - Collecting user-mode crash dumps: https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181(v=vs.85).aspx HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps DumpFolder REG_EXPAND_SZ DumpCount REG_DWORD DumpType REG_DWORD 1 = mini, 2 = full CustomDumpFlags REG_DWORD only applicable if DumpType is 0 The app may also write .mdmp files, e.g. to C:\Users\\AppData\Local\Temp Install windbg in one of these ways: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/ ... e.g. by installing the Windows SDK and choosing just "Debugging Tools for Windows". Then, e.g. "C:\Program Files\Windows Kits\10\Debuggers\x86\windbg.exe" Then see http://windbg.info/doc/1-common-cmds.html https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-a-deadlock https://stackoverflow.com/questions/1649117/analysing-crash-dump-in-windbg http://www.debuginfo.com/articles/easywindbg.html#solvereallife ~ # show threads; the one with "Suspend: 0" is running !ntsdexts.locks !locks ~4 kb # show stack backtrace for thread 4 ~* kb # show stack backtrace for all threads; the one with "#" before the ID number is running !analyze -v .ecxr # exception context record Threads stuck at a critical section look like: ... ntdll!KiFastSystemCallRet ... ntdll!ZwWaitForSingleObject+0xc ... ntdll!RtlpWaitOnCriticalSection+0x13e ... ntdll!RtlpEnterCriticalSection+0x150 To ship a temporary debug build, you need whiskerserver.exe # the debug version! whiskerserver.pdb # debugging symbols BUT you get a "side-by-side" error because the VC90 debug runtimes aren't installed. See https://www.codeproject.com/Questions/533936/HowplustoplusdeployplusDebugplusCRTplusVS-plusm If you can't get a debug build to run (and you can't use the fancier /DEBUG:FULL option available in later versions of Visual Studio that would probably help a lot here), use /MAP. This produces a textfile with lines like 0002:00027630 ??_R2?$sp_counted_impl_p@VCAmpliconDIOboard@@@detail@boost@@8 0053d630 WhiskerServer.obj ^^^^^^^^ flat address Then use the flat addresses reported by the debugger to find the error. As a concrete example: - The crash dump (~* kb) contains: # 19 Id: 15d4.1554 Suspend: 0 Teb: 7ffa8000 Unfrozen # ChildEBP RetAddr Args to Child 00 05bef7d0 76ed652c 75086a8e 00000002 05bef824 ntdll!KiFastSystemCallRet 01 05bef7d4 75086a8e 00000002 05bef824 00000001 ntdll!ZwWaitForMultipleObjects+0xc 02 05bef870 7511bf6e 05bef824 05bef898 00000000 KERNELBASE!WaitForMultipleObjectsEx+0x100 03 05bef8b8 7511bfdc 00000002 7ffd5000 00000000 kernel32!WaitForMultipleObjectsExImplementation+0xe0 04 05bef8d4 751308df 00000002 05bef908 00000000 kernel32!WaitForMultipleObjects+0x18 05 05bef940 75130b7a 05befa50 00000001 00000001 kernel32!WerpReportFaultInternal+0x186 06 05bef954 75130b28 05befa50 00000001 05bef9f0 kernel32!WerpReportFault+0x70 07 05bef964 75130aa3 05befa50 00000001 ea92b750 kernel32!BasepReportFault+0x20 08 05bef9f0 7141cc2c 00000000 7141ccc9 c0000417 kernel32!UnhandledExceptionFilter+0x1af 09 05befd28 7141ccd5 00000000 00000000 00000000 msvcr90!_invoke_watson+0xf9 0a 05befd40 00453d69 00000001 013ea960 00000000 msvcr90!_invalid_parameter_noinfo+0xc WARNING: Stack unwind information not available. Following frames may be wrong. 0b 05befd50 004f0aa8 0143cf10 0142e9c8 05befd90 whiskerserver+0x53d69 ^^^^^^^^ 0c 00000000 00000000 00000000 00000000 00000000 whiskerserver+0xf0aa8 - The map contains 0001:000efa20 ?flushPendingLineStatusRequests@LafUSBThread@@QAEXXZ 004f0a20 f LafayetteUSBThread.obj ^^^^^^^^ less than our crash 0001:000efb20 ?_Ufill@?$vector@VLafUSBReceivedMessage@@V?$allocator@VLafUSBReceivedMessage@@@std@@@std@@IAEPAVLafUSBReceivedMessage@@PAV3@IABV3@@Z 004f0b20 f i LafayetteUSBThread.obj ^^^^^^^^ more than our crash - This suggests a crash within flushPendingLineStatusRequests, in this case because I was trying to do an end() -> begin() iterator delete. Yes; fixed. =============================================================================== 4.7.6 RNC (29 May 2018) =============================================================================== - Updated for a new version of the Lafayette USB driver: cantab-usb-driver.h cantab-usb-driver.dll ... see docs in cantab-usb-driver-diff.pdf - There is specifically NO REQUIREMENT to be backwards compatible with the old DLLs (as per Craig Hauser, 18 May 2018). =============================================================================== 4.7.7 RNC (22-24 Jul 2018) =============================================================================== - Packaging release only, but server version changed just to make it consistent. - New device definition file for Lafayette USB. - cantab-usb-driver.dll is now the version from Craig Hauser (Lafayette) as of 13 July 2018 (86,192 bytes). - Added Visual Studio 2017 redistributable, and make it run by default from the relevant Whisker installers. =============================================================================== 4.7.8 RNC (15 Nov 2018) =============================================================================== - 2018-09-04: fixed documentation re (1) up/down codes for keyboard events, (2) AudioLoadTone, optional duration parameter. - 2018-11-15: attempt to fix crash with UPDD 05.01.1501. =============================================================================== 4.7.9 RNC (16 Dec 2018 - 23 Feb 2019) =============================================================================== - UPDD v6 support - See http://support.touch-base.com/Documentation/50119/API-version-5-to-version-6-conversion-notes Reminder re compiling WhiskerServer on new machines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Reminder re Boost installation: - https://www.boost.org -> "More downloads" -> "Prebuilt Windows binaries" - Then note that Visual Studio 2008 is MSVC 9 (https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B) - ... so download boost_1_69_0-msvc-9.0-32.exe, I think (2019-02-03). - It installs to C:\local\boost_, so that's where you might rediscover your old version. - Tools -> Options -> Project and Settings -> VC++ directories -> Win32 -> Include files -> Ensure the following: $(BOOSTROOT) # e.g. BOOSTROOT=c:\local\boost_1_55_0 $(WHISKERROOT)\CODE # e.g. WHISKERROOT=C:\Whisker $(MSSDK)\Samples\multimedia\directshow\baseclasses # for streams.h # ... and this last one must be BEFORE the SDK directory, which is e.g. # C:\Program Files\Microsoft SDKs\Windows\v7.1\include; see # https://stackoverflow.com/questions/15221270/build-error-in-the-refclock-h-header-file-a-part-of-windows-platform-sdk # The environment variable MSSDK is typically # C:\Program Files\Microsoft SDKs\Windows\v7.1 - Similarly, for "Library files", add: $(WHISKERLIBS)\ICScards # for pplibnt.lib $(WHISKERLIBS)\Microsoft # for ddraw.lib $(WHISKERLIBS)\Mcl\Lib # for mcl.lib $(WHISKERLIBS)\Mcl4Mfc\Lib # for mcl4mfc.lib $(WHISKERLIBS)\CryptoPP # for cryptlib_release.lib etc. $(DXSDK)\Lib\x86 # for dxguid.lib $(MSSDK)\Samples\multimedia\directshow\baseclasses\Release # for strmbase.lib $(BOOSTROOT)\lib32-msvc-9.0 # for libboost_atomic-vc90-mt-1_55.lib or similar. # DXSDK=C:\Program Files (x86)\Microsoft DirectX SDK (June 2010), or similar. # WHISKERLIBS=C:\Whisker\CODE\whiskersdk\libraries, or similar Touchscreen hardware ~~~~~~~~~~~~~~~~~~~~ - Campden Instruments touchscreen - Adjustable PSU (Amazon) - PL2303 serial-to-USB device ... installs as COM3 ... port default is 9600 8N1, no flow control; switched to hardware flow control in optimism ... use PuTTY ... baud rate unclear ... use oscilloscope ... Female 9-way RS223 port. https://en.wikipedia.org/wiki/Serial_port#Pinouts https://www.db9-pinout.com/ Female port is, looking in from the outside: G D T R D N T X X C D R D D D * * [RXD = device -> computer] 5 4 3 2 1 9 8 7 6 R C R D I T T S S S R Logic pulses are 3-15V. Oscilloscope settings (using channel 1 input) MODE = Ch1 only CH1 = DC, 5 V/div TIME = 2 ms/div or faster TRIGGER: source = Ch1, mode = auto (for hunting) or norm (for viewing), slope = "+" ... then adjust trigger level Outcome: Slowest temporal periodicity: pulse width = 1 div = 2ms interpulse gap = 3 div = 6ms Fastest: pulse width = 2.6 [big] divisions at 10 microsec/div = 26 microsec Then, helpfully: https://www.kumari.net/index.php/random/37-determing-unknown-baud-rate ... says baud rate = reciprocal of shortest pulse ... so 38400 bps Data coming, but either settings are wrong or it's a binary protocol. - Try RealTerm rather than PuTTY -- no, doesn't work. Termite has a hex view. - With 38400 8N1 we get 06 f8 60 86 66 86 f8 fe 06 00 98 86 e6 86 e6 fe .ø`†f†øþ..˜†æ†æþ 06 00 98 66 86 e6 80 06 06 18 86 f8 86 18 f8 06 ..˜f†æ€...†ø†.ø. 06 18 66 f8 86 f8 f8 06 06 98 66 86 86 7e fe 06 ..fø†øø..˜f††~þ. 18 18 80 f8 86 98 9e 00 fe fe e6 fe fe ..€ø†˜ž.þþæþþ - UPDD 06.00.444 from http://v6download.touch-base.com/releases/20190123191244/UPDD_06_00_444.exe Only serial (rather than USB) option is Intasolve Interact 400 ... Does notice touches; says "evaluation period has expired". - Turns out this is a Nexio serial device at 19200 8N1. - Then UPDD implementation/testing (see also BugTestUpdd6 code). - Success 2019-02-18 -> to testing phase. - Setting up UPDD: - Install it - Settings - Add serial device [Nexio, serial] - [Choose COM port; see Device Manager if unsure] - cmd.exe cd C:\Program Files (x86)\UPDD upddutils set rs232.baudrate 19200 - Configure ... = calibrate - Evaluation time/click limits: C:\Program Files (x86)\UPDD -- automatically removed following uninstall Registry is irrelevant; main UPDD stuff is at HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\tbupddsu\ HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\updd\ HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\upddvh\ The relevant thing is C:\Users\All Users\updd\db C:\ProgramData\updd\db ... these are the same file (whatever you do, same timestamp/size/contents). Try SQLiteStudio (run as administrator). Main table is updd_setting. ... e.g. rs232.baudrate is here; so is rs232.port [2 copies] ... registration.installable_until, license.eval_click_limit ... reboot to pick up changes - Calibrated and tested in multimonitor environments. New approach required as TBApiScaleCoordinates() has gone. - Updated InnoSetup scripts -- modular design via InnoSetup preprocessor, plus privilege elevation when installing Visual Studio redistributables. - Whisker build process successful under Windows 10/Visual Studio 2008. - Still problems: see e-mail below. 24/2/2019 Dear Gary, [This e-mail needs a monospaced font! Nasty ASCII art to follow.] Things are working reasonably at my end -- coordinates successfully translated and everything working on my multimonitor setup. But I have concerns that I haven't achieved a generic system yet, so it's not ready for production release; the reulting coordinates seem to depend on physical monitor layout. May I therefore ask you a further coordinate question? I'm using event type _EventTypeDigitiserEvent and reading all coordinates from data->pe.digitiserEvent. I'm after coordinates within a Windows window of interest (which will generally be a full-screen window covering one of several monitors) -- screenx/screeny plus translation to the coordinates of the destination window seems one possibility, but I have also implemented this using calx/caly, and examined internalx/internaly -- these seem to be the three coordinate systems available for the digitiser event. Please let me know if I've missed one. INITIAL PROBLEM ~~~~~~~~~~~~~~~ The unexpected part was that whereas Windows uses the convention of "x to the right", "y down", calx/caly appeared to use "x down, y left". I therefore needed to do this: const int i_touchscreen_max_coord = 0xFFFF; const double d_touchscreen_max_coord = i_touchscreen_max_coord; const int newx = i_touchscreen_max_coord - ev.caly; const int newy = ev.calx; const double xfraction = static_cast(newx) / d_touchscreen_max_coord; const double yfraction = static_cast(newy) / d_touchscreen_max_coord; ... this worked fine (and calx/caly are clearly calibrated coordinates, and are not tied to the touchscreen's physical coordinates -- e.g. rotating the touchscreen 180 degrees and recalibrating gives the same calx/caly system as before). Just a bit unexpected. So, more analysis. I'm using Windows 10 64-bit native, with UPDD 06.00.433 / 38230 and the Nexio touchscreen. The system I'm testing on had three 1600x1200 monitors in portrait mode (1200x1600), i.e. the Windows coordinates are normally Windows coordinates (decimal) +------------------++------------------++------------------+ | x= 0 x=1199 || x=1200 x=2399 || x=2400 x=3599 | | y= 0 y= 0 || y= 0 y= 0 || y= 0 y= 0 | | || || | ... M1 ... M2 ... M3 ... | || || | | x= 0 x=1199 || x=1200 x=2399 || x=2400 x=3599 | | y=1599 y=1599 || y=1599 y=1599 || y=1599 y=1599 | +------------------++------------------++------------------+ which can either operate as three separate screens or as one 3600x1600 screen (via NVidia Surround). Since I was expecting this: 0 --> x | | v y and am seeing this: y <-- 0 | | v x that led me to wonder if there is something about rotated monitors that isn't being translated correctly (my monitors are physically rotated clockwise from landscape to portrait). Something similar seemed to be true for screenx/screeny. In more detail: SITUATION 1 ~~~~~~~~~~~ Monitors as above; single 3600x1200 screen. With the touchscreen calibrated for the whole 3600x1200 desktop as above, i.e. +------------------++------------------++------------------+ monitors | || || | | UPDD TOP LEFT || || UPDD TOP RIGHT | | CAL. POINT (1) || || CAL. POINT (3) | | || || | ... M1 ... M2 ... M3 ... | || || | | UPDD BTM LEFT || || UPDD BTM RIGHT | | CAL. POINT (2) || || CAL. POINT (4) | | || || | +------------------++------------------++------------------+ calibrated to +------------------+ map of touch location on touchscreen | cal. 1 cal. 3 | | | | | | | | cal. 2 cal. 4 | +------------------+ ... I then see this in response to touches (~ means "approximately equal to"; I am ignoring what I think is some very minor calibration error because I can't physically put this touchscreen overlaying this monitor setup...): screenx/screeny (decimal) +------------------+ location of text indicates touch location at corners of touchscreen | x~ 0 x~ 0 | numbers indicate resulting coordinates | y~1600 y~ 0 | | | | x~3600 x~3600 | | y~1600 y~ 0 | +------------------+ calx/caly (hex) +------------------+ as above | x~ 0 x~ 0 | | y~FFFF y~ 0 | | | | x~FFFF x~FFFF | | y~FFFF y~ 0 | +------------------+ internalx/internaly (hex) +------------------+ as above | x~ 0 x~ 0 | | y~FFFF y~ 0 | | | | x~FFFF x~FFFF | | y~FFFF y~ 0 | +------------------+ The output of GetMonitorInfo() is as follows (from the rcMonitor rectangle of a MONITORINFO struct): Display device 0... Monitor coordinates: (0,0)-(3600,1600) ... displayed as (left,top)-(right,bottom) SITUATION 2 ~~~~~~~~~~~ Monitors arranged as above (all in portrait mode) but NVidia Surround disabled. With the touchscreen calibrated for the centre monitor, i.e. +------------------++------------------++------------------+ monitors | || || | | "Not a || CAL#1 CAL#3 || "Not a | | touchscreen" || || touchscreen" | | || || | ... M1 ... M2 ... M3 ... | || || | | || || | | || CAL#2 CAL#4 || | | || || | +------------------++------------------++------------------+ calibrated to +------------------+ map of touch location on touchscreen | cal. 1 cal. 3 | | | | | | | | cal. 2 cal. 4 | +------------------+ I get this: screenx/screeny (decimal) +------------------+ location of text indicates touch location at corners of touchscreen | x~1200 x~1200 | numbers indicate resulting coordinates | y~1200 y~ 0 | ... you might think left-hand y~1600, but no, ~1200 | | | x~2800 x~2800 | ... you might think x~2400, but no, ~2800 | y~1200 y~ 0 | +------------------+ calx/caly (hex) +------------------+ as above | x~ 0 x~ 0 | | y~FFFF y~ 0 | | | | x~FFFF x~FFFF | | y~FFFF y~ 0 | +------------------+ internalx/internaly (hex) +------------------+ as above | x~4650 x~4650 | | y~BF68 y~ 0 | | | | x~A280 x~A280 | | y~BF68 y~ 0 | +------------------+ The output of GetMonitorInfo() is as follows: Display device 0... Monitor coordinates: (0,0)-(1200,1600) Display device 1... Monitor coordinates: (1200,0)-(2400,1600) Display device 2... Monitor coordinates: (2400,0)-(3600,1600) SITUATION 3 ~~~~~~~~~~~ Three monitors, all in conventional landscape mode, left to right, no NVidia oddities: Windows coordinates (decimal) +------------------++------------------++------------------+ | x= 0 x=1599 || x=1600 x=3199 || x=3200 x=4799 | | y= 0 y= 0 || y= 0 y= 0 || y= 0 y= 0 | | || || | | x= 0 x=1599 || x=1600 x=3199 || x=3200 x=4799 | | y=1199 y=1199 || y=1199 y=1199 || y=1199 y=1199 | +------------------++------------------++------------------+ and with the touchscreen calibrated to the centre monitor: +------------------++------------------++------------------+ monitors | "Not a || CAL#1 CAL#3 || "Not a | | touchscreen" || || touchscreen" | | || || | | || || | | || CAL#2 CAL#4 || | +------------------++------------------++------------------+ against +------------------+ map of touch location on touchscreen | cal. 1 cal. 3 | | | | | | | | cal. 2 cal. 4 | +------------------+ I get this: screenx/screeny (decimal) +------------------+ location of text indicates touch location at corners of touchscreen | x~1600 x~3200 | numbers indicate resulting coordinates | y~ 0 y~ 0 | | | | x~1600 x~3200 | | y~1200 y~1200 | ... cheerful +------------------+ calx/caly (hex) +------------------+ as above | x~ 0 x~FFFF | | y~ 0 y~ 0 | | | | x~ 0 x~FFFF | | y~FFFF y~FFFF | ... cheerful +------------------+ internalx/internaly (hex) +------------------+ as above | x~558C x~A9EC | | y~ 0 y~ 0 | | | | x~558C x~A9EC | | y~FFFF y~FFFF | ... cheerful? (expected X range is 5555-AAAA, i.e. the +------------------+ middle third of 0-FFFF? But see below re upddapi.h) The output of GetMonitorInfo() is as follows: Display device 0... Monitor coordinates: (0,0)-(1600,1200) Display device 1... Monitor coordinates: (1600,0)-(3200,1200) Display device 2... Monitor coordinates: (3200,0)-(4800,1200) INFERENCE ~~~~~~~~~ In summary, UPDD appears to be giving touch coordinates based on a rotated coordinate system (relative to the Windows desktop coordinate system) when monitors are in portrait mode. I guess that somewhere in the UPDD calculations there is an assumption of monitors being in landscape mode, leading to the coordinate system being rotated away from the Windows coordinate x/y directions when portrait monitors are used. (I'd be hard-pushed to guess further where this stems from, given that GetMonitorInfo() is returning correct information.) MAIN QUESTION ~~~~~~~~~~~~~ Does UPDD provide something in plain "Windows coordinate space", regardless of the monitor configuration -- or even in "calibrated touchscreen space" in which the x and y directions can be relied upon? UPDD DOCS ~~~~~~~~~ Also, in upddapi.h: (a) Is this a minor typo: int32_t calx; // the calibrated co-ordinates values; a value from 0 - 0xffff, // giving the absolute position of touch in the range of the originating hardware int32_t caly; // so for example touching the centre of a screen will give // around 7ff regardless of the associated monitor ^^^ should that be 7fff (i.e. one more "f")? (b) Re internalx/internaly: int32_t internalx; // the corresponding windows co-ordinate value, the // primary monitor has the range 0xffff, and other monitors are scaled from that int32_t internaly; // so in the example given above the result is 0x17fee, 0x7fff [regarding two 1024x768 monitors side by side, left primary, touching the middle of the right one...] ... it sounds perfectly plausible but see the internalx/internaly range for my "situation 3" above; the x range is within 0-FFFF (specifically in the centre third), rather than above FFFF. Is that a doc error, or something that changes depending on the Windows version or monitor configuration? Thanks! all the best, Rudolf. ~~~ On 2019-02-25 08:52, Gary Allitt wrote: > Rudolf > > Before I answer in detail please confirm you have set the Windows > screen rotation setting appropriately to match your physical > orientation [screenshot] > Failure to do so would explain most of the below. > > I have corrected the typo you raise below. > > Regards > > Gary ~~~ 25/2/19 Dear Gary, Yes, I confirm. Rebooted and retested: - Situation 1: Windows reports a single monitor ("Landscape flipped"); desktop resolution 3600 x 1600; active signal resolution 1600 x 3600. This is an NVidia "special" so not the main concern. - Situation 2: (a) NVidia Control Panel reports all three are in Portrait mode; (b) Windows Display Settings reports all three are in Portrait mode; in "Advanced display settings", all report a desktop resolution of 1200 x 1600 with an active signal resolution of 1600 x 1200 (looks correct to me also for Portrait mode). Screenshots attached. Reconfirmed that calx/caly still give "x down, y left", as before, in the approximate range 0-0xFFFF. However, now screenx is approximately 0-768 in the "x down" direction and screeny is fixed at exactly zero. (No difference if I recalibrate the touchscreen itself in "portrait mode" against the portrait-mode monitor.) And internalx is approximately 0-14000 decimal (36B0 hex) in the "x down" direction and internaly is 0-40 decimal in the "y left" direction. - Situation 3 not retested yet as that was behaving itself reasonably in any case. Throughout this process, Whisker itself, which addresses monitors via GetMonitorInfo(), is happy and nothing is rotated there (relative to the human viewing the screens), like the rest of Windows. all the best, Rudolf. ~~~ 26/2/19 Dear Gary, > [ga]screen x / y is intended for this purpose Great; will default to that once we've sorted this problem out. > [ga] calx/y is really only for code compatibility with v5. On of the > other modes is likely best. OK. > [ga]correct, there are 3. OK. > INITIAL PROBLEM > ~~~~~~~~~~~~~~~ > > The unexpected part was that whereas Windows uses the convention of > "x to the right", "y down", calx/caly appeared to use "x down, y left". > > [ga] I would not have expected this. Can you clarify what you mean by > "to the right". Are you talking about moving from left to right with > respect to the orientation in which Windows desktop is displayed. (First, the Windows GDI coordinate convention: as x increases, physical position on the Windows desktop moves right; as y increases, physical position moves down; we all know this; e.g. https://docs.microsoft.com/en-us/windows/desktop/gdi/window-coordinate-system.) What I'm doing is: I calibrate the touchscreen. Right now I have "situation 2", i.e. three portrait-mode monitors side by side. I've put the touchscreen in a portrait orientation. I've calibrated it, using UPDD, to the middle monitor (in the obvious way: top left of the touchscreen maps to top left of the screen calibration display; etc.). I've set Whisker to report calx/caly. When my finger is at the top right of the touchscreen, calx and caly are both about 0. As I move my finger to the top left of the touchscreen, caly increases to 0xFFFF and calx stays at zero (so "y left"). Returning to the top right of the touchscreen: both back to zero. Move to the bottom right of the touchscreen: calx increases to 0xFFFF; caly stays at zero (so "x down"). When my finger is at the bottom left of the touchscreen, caly and caly are both around 0xFFFF. This calx/caly behaviour is consistent in portrait mode (but different to what happens with the monitor in landscape mode). > [ga] If so then I would expect calx to increase. So, no, calx decreases moving the finger from left to right. > [ga] If in this scenario calx increases, please perform these checks [presume "decreases"] > [ga] upddutils device n get private.screen_rotation.angle > this should show the currently effective rotation angle This works with n = 1. So: C:\Program Files (x86)\UPDD>upddutils device 1 get private.screen_rotation.angle private.screen_rotation.angle: 90 > [ga] upddutils device n get private.screen_rotation.calibrated.angle > this should show the rotation angle thatt was effective at > the time of calibration C:\Program Files (x86)\UPDD>upddutils device 1 get private.screen_rotation.calibrated.angle private.screen_rotation.calibrated.angle: 90 > [ga] test without your software active, so updd drives the mouse, is > touch in the correct orientation? Not currently driving the mouse, and UPDD icon has gone from the taskbar. (Though earlier -- exactly the same behaviour and it was there.) So, - Remove UPDD, fresh installation - "Evaluation period expired" -> so modified "registration.installable_until" to '2019-03-19' and "license.eval_click_limit" to '20000'; commit/reboot - Recalibrate as above - I find using this as a mouse pretty tricky, but definitely operating the right way -- touchscreen physical direction matches mouse cursor physical direction (easiest to see with a selection drag). - Test screenx/screeny via Whisker * top left of the touchscreen x=1200, y=1597 * top right x=1219, y=5 * bottom left x=2394, y=1595 * bottom right x=2396, y=3 - Quit Whisker - UPDD icon -> Settings -> Test * Not working correctly: touchscreen finger moves "up" -> debug cursor trail moves left; * ... touchscreen "right" -> debug cursor trail moves up * ... touchscreen "down" -> debug cursor moves right * ... touchscreen "left" -> debug cursor moves down - Quit test utility. Retested plain Windows mouse cursor (via selection drag): * ... touchscreen "left" -> mouse cursor moves left * ... touchscreen "right" -> mouse cursor moves right * ... touchscreen "up" -> mouse cursor moves up * ... touchscreen "down" -> mouse cursor moves down ... so that's notable: Windows cursor behaviour not matching UPDD internal test utility behaviour. - Rested the "upddutils device 1 get..." commands -- both still give 90 > [ga] If something is not as expected please take a UPDD diagnostic > trace, send the log to me and I will investigate. When taking the > trace please draw the vertical line (with respect to Windows > orientation) first - UPDD icon -> Status -> Diagnostics First test: (1) top-to-bottom (on the touchscreen), (2) left-to-right, (3) clockwise from 12 o'clock Second test (two fingers): top left to bottom right. File attached. > [ga] I have a vague recollection thath something is different in > Windows 10; but a quick look at the source code suggests otherwise. > The diagnostic trace would help tie this up. > ... > [ga] It's not completely clear to me here what you mean. But the aim > of rotation in UPDD is that by touching a given point on the visual > image on the screen the touch should be reported at the same poistion > regardless of the rotation settings. Great. That's what I hope we get to. Explanation as above > I am ignoring what I think is some very minor calibration error because > I can't > physically put this touchscreen overlaying this monitor setup...): > > [ga] My experience is this can be quite confusing when testing rotation! Yes :) But pretty sure we're not confused at present. > [ga] With the diagnostic I requested above I can form a better > understanding of your situation and advise further. As above. Thank you! all the best, Rudolf. ~~~ Sorry, two typos: > bottom left of the touchscreen, caly and caly are both around 0xFFFF. ... should have said "calx and caly are both..." > > This calx/caly behaviour is consistent in portrait mode (but different > > to what happens with the monitor in landscape mode, as above). > > > [ga] If so then I would expect calx to increase. > > So, no, calx decreases moving the finger from left to right. ... sorry, caly decreases moving the finger from left to right (calx stays the same, as above) -- caly is behaving like it's in the "x direction" and calx like it's in the "y direction". R ~~~ 27/2/19 Rudolf I've tested rotate in conjunction with the api and everything is working as it should. The explanation for the behaviour you see is that in Windows 10, if HID touch (as opposed to mouse) mode is in use, Windows transforms the co-ordinates for rotated touch so the driver must pass unrotated co-ordinates. Because everything in the driver is working from a common data source. This same data is posted to the API. It's debatable if this is correct or not, but given that if I change this there is a strong possibility I will break existing customer implementations I'm going to leave it asis; and if this is an issue in the future provide an additional option so that the API posts rotated data in this case. In your scenario there is a simple work around, if you do upddutils nodevice set active_touch_interface none You should get the behaviour you expect. I understand you only use the api and not updd touch. Alternately upddutils nodevice set active_touch_interface tbupddsu To get the desired behaviour and touch. NB I noticed that your settings tweak the licensing, and this will lead to unpredictable behaviour; I'm surprised it works at all; I tried it here and it stops the driver. You should get a notification message; but in the course of testing I saw that, since a recent Qt upgrade Windows notifications are not working. This is corrected in the next build, linked below. Regards Gary ~~~ 28/2/19 Dear Gary (CC all), Thanks. First: sequence. Then: broad conclusions. Tonight: - Old version (06.00.433 / 38230) - upddutils nodevice set active_touch_interface none - run Whisker - no difference to screenx/screeny (still "y left, x down") - quit Whisker - upddutils nodevice set active_touch_interface tbupddsu - run Whisker - no difference to screenx/screeny (still "y left, x down") - quit Whisker - upddutils nodevice set active_touch_interface none - reboot - run Whisker - system at first almost unusable as constant mouse clicks coming once Whisker launched (with pulsating translucent touch circles around the top right of the centre monitor and the top left of the right-hand monitor); then they stopped; touchscreen unresponsive; UPDD said "No devices connected" when I selected UPDD -> Configure - quit Whisker - upddutils nodevice set active_touch_interface tbupddsu - reboot - recalibrate -- OK - lots of spurious ?mouse clicks -- all my windows closed, start menu popped up on command and went away again - ... settled down - run Whisker - still broken actual central monitor screen coords are (1200,0)-(2400,1600) top left of touchscreen -> screenx = 1200, screeny = 1598 top right of touchscreen -> screenx = 1206, screeny = 21 bottom left of touchscreen -> screenx = 2394, screeny = 1590 bottom right of touchscreen -> screenx = 2398, screeny = 0 - quit Whisker - one more go: - upddutils nodevice set active_touch_interface none - recalibrate -> "No devices connected" - run Whisker - touchscreen unresponsive - quit Whisker - New version (06.00.465 / 38876) - uninstall old version - http://v6download.touch-base.com/releases/20190227153207/UPDD_06_00_465.exe - install new version - about 20 sec of "identifying monitors..." - UPDD -> Settings -> Add serial device -> only choice Nexio, FW1.5x (5 byte), serial -> COM4 - accidentally clicked "Add Serial Device" again -- next one undeletable? "A comprehensive settings program is in development. This is a basic program to cater for a few commonly used settings." - Calibrate -> calibrated. - run Whisker - happiness! (Briefly) top left of touchscreen -> screenx = 1202, screeny = 1 top right of touchscreen -> screenx = 2380, screeny = 9 bottom left of touchscreen -> screenx = 1203, screeny = 1599 bottom right of touchscreen -> screenx = 2399, screeny = 1574 - Situation 3 (three landscape monitors) - recalibrate - fine - Situation 1 (one NVidia Surround monitor) - recalibrate - both x/y reversed - ... can't have everything; Windows thinks this is "landscape (flipped)" mode and it's a bit obscure (for gaming) - Back to Situation 2 (three portrait monitors) - recalibrate - back to "x down, y left" - reboot - recalibrate - UPDD Settings -> Test - still broken -- now I look closely, I can see simultaneously (a) a ghost "touch" circle that moves congruently with my finger i.e. correctly (b) the red line, which moves left when my finger goes up; right when my finger goes down; down when my finger goes left up when my finger goes right - Whisker behaviour matches the debug red line. so... - upddutils nodevice set active_touch_interface none - UPDD Settings -> Test ... ghost circle still correct ... red line still rotated - ... Whisker screenx/screeny matches red line - reboot - spurious mouse clicks at ~0.5 Hz render system unusable for ~1 min or so, settle down [is this UPDD's prolonged "monitor detection" mouse chaos? Don't know] - UPDD Settings -> Test ... ghost circle still correct ... red line still rotated - ... Whisker screenx/screeny matches red line So... is it that a fresh installation works, and then once it's broken, it's broken? - Uninstall - Reboot for good measure - Install - 35s (by counting) from "Identifying monitors, don't touch stuff" to "... done" -- what takes it this long? I have no idea. - Settings -> Add serial device (etc.) - Calibrate - Test ... this time, red line moves congruently and ghost touch circle moves with reversed x/y ... by this point I am convinced that the red line is the API screenx/screeny, so I expect Whisker to see correct values ... yes, it does. - EXCEPT not quite: top left touch -> screenx = 1204, screeny = 4 [fine] top right touch -> screenx = 2773, screeny = 12 [PROBLEM: monitor is 1200w, x diff is 1600] bottom left touch -> screenx = 1207, screeny = 1194 [PROBLEM: monitor is 1600h, y diff is 1200] bottom right touch -> screenx = 2796, screeny = 1185 [both problems as above] - Recalibrate to first monitor: happy, correct, x span is 1200 and y span is 1600 - Recalibrate to second monitor again: x/y right directions but x span is incorrectly 1600 and y span is incorrectly 1200 - Recalibrate to third monitor: - screenx ranges from 0 to 754 or so (presumably 768), yet should be in the range 2400-3600 - screeny ranges from 0 to 0, which is useless So, my conclusions are: - You've said that screenx/screeny is what I should be using. - I note in passing that you didn't plan to fix the API output for screenx/screeny in terms of rotation, for fear of breaking other customers' code; this surprises me as the header advertises these to be screen coordinate values, so what people are interpreting them as at present, if not that, is a mystery to me; presumably it's also possible that others are interpreting them as screen coordinates and are unaware they might break with some monitor rotation settings. - UPDD 06.00.433 / 38230 and UPDD 06.00.465 / 38876 do not at present reliably report screenx/screeny in desktop-relative coordinates (neither with absolute reliability nor sufficient reliability for our needs). - calx/caly may not be intended, but at least in some circumstances they report consistent information in the correct direction. - For now, I shall therefore set Whisker to use calx/caly and presume that they are in the correct ("x right, y down") orientation. That will sometimes be true and sometimes not, and Whisker can't know which, but it's testable. - This is working at present across all monitor settings for me, EXCEPT that when returning from landscape to portrait mode, it gets "x/y rotated" and this is "cured" by removing and reinstalling UPDD. - So, I will release WhiskerServer 4.7.9 to Lafayette for testing. It uses calx/caly. all the best, Rudolf. ~~~ 1/3/2019 Rudolf This is quite a long email so I will summarise here. You should use "upddutils device N set active_touch_interface none" sorry for ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ NOT YET TESTED the previous incorrect advice. The false touches sound like a hardware issue. It is possible that the monitor detection code is generating these but it does not sound like it to me. I'm reviewing this. Use "upddutils deldevice" to delete a device Calx/y and Screenx/y are derived from the same points, I really doubt you will see a difference in the orientation between them. The red line in updd test is showing data from the api regards ~~~ 2/3/19 Dear Gary, Thanks -- noted. I think I will leave Whisker with calx/caly at present. I've never seen calx/caly differ in orientation from screenx/screeny, but screenx/screeny sometimes gets the absolute positioning wrong, or collapses (for example) the y range to zero, as per my previous e-mail. The only thing I think I've seen go wrong with calx/caly is that it rotates its axes incorrectly sometimes when monitor settings are changed (fixable by reinstalling UPDD) -- not ideal, but significantly better than screenx/screeny (which did not seem reliable). Since I know where my windows are on the Windows desktop, a calibrated relative position within the touchscreen is fine and I can map to Windows coordinates directly. If Laf[a]yette find this sufficiently reliable on their kit, that means that I can leave touchscreen comms and calibration to UPDD and I don't have to write those aspects. Thanks for your help. all the best, Rudolf. =============================================================================== 4.7.10 RNC (30 Jun 2019 to 2 Jul 2019) =============================================================================== - CNIControllingABETDIOboard: problem in which writing outputs (in interfaces 2 and higher) causes spurious very brief inputs ... but that goes away if poll time period increased from 1ms to 10ms ... potentially a pulse timing issue (?related to computers getting faster since original code 14 years ago) - also long poll times observed - also poll time period not persistent -- made persistent DISCUSSION INCLUDES TE -> RNC 2019-06-24 20:16 ... In 2005 the hardware that was used was our 81400 Series Interfaces (81401, 81402, 81401-NL, 81402-NL). In the fall of 2010 we introduced the 81500 Series Interfaces. The ‘-NL’ prefix does mean Non-Latched. The addressing scheme did not change, i.e. they method for addressing Starter (81501-NL) and Expander (81502-NL) interfaces. Interface circuitry has changed for robustness, vendor availability, etc.. From Whisker Server Version Tracker … Now with the latches removed the Service Request and input will stay activated as long as the device is active, e.g. lever pressed. So reading the Service Request and input will clear them but they will be set again immediately after reading. Polling --- Input Activated … Read Service Request (SR) -> indicates an Input has occurred. Read Inputs from Addressed chamber – This clears the SR/Input for the chamber. (Log – message client that an input has occurred on line ‘nn’). Remember the Input Line State – ON Do not Message Client for an Input until the Input is clear. Does Polling “see” the SR clear for the same input that is still active after addressing Chamber? Is there a 1khz poll timing issue with the SR latched flag that goes clear – returns high (because input is still high). See the highlighted comment. We are writing outputs (On / Off) at the same time inputs are occurring (On / Off). Could Polling be ‘see’ Service Request going ON/OFF and counting this as an INPUT change. Rather Polling should be looking at Inputs to see if this has changed. Are you polling SR or Inputs (i.e. addressing chambers) Discussion ... SR indicates Input – Address Chambers Check Inputs – Record Line # and message client – Input transition ON Next Poll … SR indicates Input – Address Chambers Check Inputs – Record Line # (SAME INPUT) and no message to client Versus … ( if this is occurring ??) SR indicates Input – Address Chambers Check Inputs – Record Line # and message client – Input transition ON Next Poll … Address change (is there a “wait” time for Address to settle and to have valid data on inputs?, should be very short No SR (timing issue??? Because of Clear when Read ??) Next Poll… SR indicates Input – Address Chambers Check Inputs – Record Line # message client – Input transition ON (As if New Input ??) Note: This is only occurring on the Release of the Input, i.e. input going from Active to Not Active. – And we are tuning OFF an output at the same time. Independent Inputs (i.e. no outputs going ON / OFF at “same time”) do not seem to ‘see’ extra counts. Further testing - We are going to feed a 10Hz signal in to an input without any outputs – then with outputs being triggered to see what we get. Regards, Terry TE -> RNC 2019-06-25 16:22 Hello Rudolf – The issue seems to when 2nd (and above) interfaces are addressed and Outputs are written as “Off” (not active). We get erroneous inputs at the same time. Attached is data from ABET and Whisker again for the same chamber designations. Interface 1 – Chamber 1 – this data is clean Interface 2 – Chamber 3 - this date shows erroneous inputs when the Output is turned OFF. The input device is a simple pulse with about 104ms ON and 94ms interval. The Onset of the input is used to Pulse the Output for 104ms. You’ll see in the data for Chamber 1 that the input and outputs are clean, i.e. periodic with small variance. Note the input is running prior to the start of the ABET schedule. You’ll see in the data for Chamber 3 the input contains erroneous transitions, very short OFF-ON times 2ms or so. Looks like when Whisker is writing Outputs on the 2nd and above interface it is interfering with Input status. Can you have a look at your code? TE -> RNC 2019-06-25 18:53 Hello Rudolf – Pulse - for an Input – Onset = Shunt to Ground – Offset = removal of shunt to ground. (i.e. Active Low input). This results in a logic 1 onset for the shunt to ground and a logic 0 for its removal as seen by Whisker / ABET. I.e. Shunt to Ground = Active. Pulse – Output – An Output that ‘Active’ for a short duration. The physical output is again Active Low. A pulse for 104ms is a High to Low to High where the Low lasts for 104ms. Questions:: 632 // Query: for how long is the service request bit activated? What clears it? In Latched Hardware the SR bit is activated High until the inputs from the chamber associated with SR bit chamber are read. In Un-Latched Hardware SR bit is activated High until the inputs from the chamber associated with SR bit chamber are read. If any input remains High the SR bit will be immediately activated. The SR bit will be set as long as an associated input is activated. 634 // Query: presumably also activated when an input goes off? No- SR Bit is only activated on input activation onset for latched hardware. For Non-Latched hardware the SR Bit remains active as long as an associated input is activated. SR Bit need will be cleared by reading inputs and no input is activated. 819 // Query: do we have to wait for some time for the multiplexer to get its act together, 820 // once we've sent it the address information? 821 // Answer: empirically, no. 822 Actually – Yes but it should be quick. Need some time for Inputs to “settle” (change state from previously addressed interface). LIC Engineering – What is the settling time? 831 // if we read from the hardware, we also write, so that the "transition" bits are cleared 832 // ready for the next poll. 833 // Terry Echard confirms 9/2/9 that this remains necessary for the no-latch hardware. I think this is correct – Craig & David can you confirm? 855 // Query: can we leave this bit high with no problems? 856 // Answer: no, we never get another input service request if we don't clear it. 857 // Outstanding question: empirically, it works to unset it again immediately. But is that guaranteed? True – SR Bits are needed. Regarding new hard (81500 Series latest version) SR Bit may remain high as long as any of the associated inputs are active. SR Bits are unset when no associated inputs are activated AND ports are read. {{{ MAY BE }}} LIC Engineering need to CONFIRM! NOTE:: 800 // In the no-latch system, any chamber that doesn't have its service request set 801 // has no active inputs. May not be a true statement!!! If you read Outputs for the addressed chamber – the SR Bit may be unset for a time (should be sort, micro-seconds or nano-seconds). An input could be high/set/active – SR Bit set. Read Outputs – could unset SR Bit temporarily. ??? Need to confirm. Sequence – every Poll Read Service Requests … (only reading inputs if SR-Bit set but do outputs too if any) If yes – Determine Which Interface(s) or Chamber(s) Address Interface (small wait?) - Read Inputs - Any Output need serving? (on or off) - if Yes then do Any Outputs need servicing? … (Do every poll but – this will clear the SR-Bit even if set after the read/write above) - If yes - Determine Which Interface(s) or Chamber(s) - Address interface – set outputs as needed I’m still looking at your code but want to get this out. We need answer the hardware timing concerns … Regards, Terry RNC -> TE, 2019-07-02 01:50 Dear Terry et al., I have done some timing tests but I suspect the short version of what's below "~~~" (below!) is that writes are quite slow (though I'm not seeing the timings change much with the number of interfaces, as you'd found), but I think the slow polls are probably not the problem and it's more likely that your diagnosis in relation to the Service Request ports is correct, Terry. There were a couple of points you said you wanted engineering confirmation of in your e-mail of 2019-06-25 18:53 [that's probably UK time, BST = UTC+1], in particular whether reading [writing?] outputs "could unset SR bit temporarily". The essence of what I'm doing during each poll is: - read both SR ports - if it's time to force an output refresh, mark all chambers as "chamber needs writing" - if the SR bit for a chamber is set, mark that chamber as "chamber needs reading" - for each chamber: - for the "no-latch" hardware (UpdateChamber_NoLatches): - if the chamber is not marked as needing reading, then its SR bit was not set, and that means that it has no active inputs, so turn the state off as far as Whisker is concerned -- is this the problem? I have the note "In the no-latch system, any chamber that doesn't have its service request set has no active inputs." But you're suggesting that the SR bit is cleared by previous operations, then is turned on again "immediately" for chambers with active inputs -- is the problem that this is not quite immediate, and Whisker is re-reading (during the next poll) before that happens? It sounds like that would explain most things, possibly all. If so, how long does it take (after a preceding operation that affects the SR bits) before this statement is guaranteed to be true? - if the chamber is not marked as requiring reading or writing, return (and move on to the next chamber) - write the chamber address to the address output port; do not wait - if the chamber is marked as "needs reading", read the input ports, and set the input line state accordingly, and mark the chamber as "needs writing" [I have forgotten exactly what this means but the comment is: 'if we read from the hardware, we also write, so that the "transition" bits are cleared ready for the next poll. Terry Echard confirms 9/2/9 that this remains necessary for the no-latch hardware.'] - if the chamber is marked as "needs writing", write the output state to the four output ports; turn the output enable line on (writing the address output port to do so); turn it off again immediately - mark the chamber as no longer needing reading or writing Less critical observations/questions (I think) below. ~~~ With the debugging version on your machine now at C:\Users\Software Developer\rudolf\whiskerserver.exe there are two software changes: - I've made the timer period/resolution options persistent (but haven't used them for testing yet) - there is a debugging option that reports, at roughly 1 Hz, for the most recent ABET controller poll, (a) the total poll time, and (b) the time within that poll [always less than (a)] updating the chambers. The difference between the two, a - b, is the time taken to read the chambers ("do you need attention?"). When I fired it up, it was set the default timer period/resolution of 1ms, with 2 ABET interfaces, and to "Refresh outputs every [ 10 ] polls." In this state, a - b (the read time) was 156-185 microseconds, and b (the write time) was 1879-2574 microseconds. [It is reporting timings every 1000 polls, so it's possible that all were the "force refresh" polls.] Similarly, the "longest recent poll" shown in the main Whisker status window was hovering around 2500 us. If I set "Refresh outputs every [ 0 ] polls", the total poll time drops sharply to about 129-190 us (and b is consistently zero, since I wasn't triggering any inputs). When set to 1 ABET interface, the figures are pretty similar -- I'm not seeing the jump in poll time you observed before. Does that jump only occur if you have active inputs? This, plus your experience that reducing Whisker's poll rate makes the problem go away, suggests: each ReadPort() call, of which there are two per poll, takes roughly ~60-90 us the update, which for "write only" updates involves one WritePort() to choose the chamber and 6 WritePort() calls to write the data (4 data writes + output enable line on + output enable line off) = 7 write port calls, takes about 1879-2574 us or about 260-370 us per write. So: Is this inferred write time similar to what you see with your software? It does seem quite long and I had presumed this is the NIDAQ code -- but it is waiting for confirmation from the hardware? My WritePort() function (a) locks a critical section, then, assuming this is NIDAQ-MX, (b) calls DAQmxWriteDigitalScalarU32(taskhandle, true /* autostart */, 0 /* timeout */, data, NULL /* reserved */). Does the spurious input go away with "Refresh outputs every [ 0 ] polls" (and can the hardware cope with that)? It wasn't a feature really intended as a keepalive, just to cope with unreliable hardware. all the best, Rudolf. CH -> RNC, 2019-07-02 21:59 ... I was looking more closely in your UpdateChamber_NoLatches method that you provided. I see that you have in your code a separation of read and write routines, from your comments it is to improve performance speed however I strongly suspect that this action of not obtaining current inputs every time the chamber is serviced is leading to a state inconsistency we are observing. One solution would be to move the read/write checks more to the top of the routine and if either are set then both operations are done. [TE] Note. The error or observed issue is when an output is being forced off and the input is transitioning from Active to In-active. This is when we get extra ‘counts’ on the input. There is a schedule on the PC that uses an input from the repeat cycle timer to trigger an output of about the same pulse length. ‘Simple...Triggered.’ This produces the condition I mentioned above, an output going off while the input is transitioning to in-active. [CH] On other notes : I didn’t see the need to separate read/write and bundled them as a single “Service this interface” action that… Sets address, reads input, writes output, latch bit, done. The code I use to determine if it needs servicing is from either 1) Service request line, 2) code changed an output, 3) background periodic roll through all interfaces to keep power on. Implemented as above; see USE_JULY2019_NOLATCH_METHOD. ... made permanent (removed #ifdef) 2019-07-09. =============================================================================== 4.7.11 (RNC, 8 Jul 2019) =============================================================================== - Changes above worked. But polling remains slow. - Trying new trial design. See NI_FASTER_TASKS. - Craig's example (email of 8 July 2019): I create each task and start them after I configure. If I have any errors then an exception is thrown/handled. Where ‘myDevice’ is the current NIDAQ device to bind to. Note: in c# a $ before a string indicates that string interpolation is used. // address lines this.apa = new Task("APA", myDevice); this.apa.CreateOutputChannel($"{myDevice.DeviceName}/port0", "address and latch"); this.apa.SetStreamTimeout(0); this.apa.SetWriteSleepTime(0.0001); this.apa.Start(); // output lines this.doTask = new Task("DOTask", myDevice); this.doTask.CreateOutputChannel($"{myDevice.DeviceName}/port1:4", string.Empty); this.doTask.SetStreamTimeout(0); this.doTask.SetWriteSleepTime(0.0001); this.doTask.Start(); // input lines this.diTask = new Task("DITask", myDevice); this.diTask.CreateInputChannel($"{myDevice.DeviceName}/port6:7", string.Empty); this.diTask.SetStreamTimeout(0); this.diTask.SetReadSleepTime(0.0001); this.diTask.Start(); // interrupt lines this.dirqTask = new Task("DIRQTask", myDevice); this.dirqTask.CreateInputChannel($"{myDevice.DeviceName}/port9:10", "interrupt 0-15"); this.dirqTask.SetStreamTimeout(0); this.dirqTask.SetReadSleepTime(0.0001); this.dirqTask.Start(); - NI-DAQmx docs: NI-DAQmx C reference help: http://zone.ni.com/reference/en-XX/help/370471AA-01/ NI_DAQmx C functions: http://zone.ni.com/reference/en-XX/help/370471AA-01/TOC3.htm Task State Model: http://zone.ni.com/reference/en-XX/help/370466Y-01/mxcncpts/taskstatemodel/ DAQmxCreateTask: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxcreatetask/ DAQmxCreateDIChan: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxcreatedichan/ DAQmxCreateDOChan: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxcreatedochan/ DAQmxStartTask: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxstarttask/ DAQmxReadDigitalScalarU32: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxreaddigitalscalaru32/ DAQmxWriteDigitalScalarU32: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxwritedigitalscalaru32/ [unnecessary?] DAQmxStopTask: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxstoptask/ DAQmxClearTask: http://zone.ni.com/reference/en-XX/help/370471AA-01/daqmxcfunc/daqmxcleartask/ There seems to be no C equivalent of SetStreamTimeout() or SetReadSleepTime(). Though DAQmxReadDigitalScalarU32() has a "timeout" parameter. - But note: DAQmxReadDigitalScalarU32() doesn't allows you to specify a port. It reads a 32-bit unsigned integer, from a port of up to 32 lines. Its signature is int32 DAQmxReadDigitalScalarU32(TaskHandle taskHandle, float64 timeout, uInt32* value, bool32* reserved); So, it doesn't look like we can move away from "one task per port". That makes things simpler... So we just need to try starting each task, and hope it keeps running and goes faster. It does say in the DAQmxStartTask help: "If you do not call DAQmxStartTask and DAQmxStopTask when you call NI-DAQmx Read functions or NI-DAQmx Write functions multiple times, such as in a loop, the task starts and stops repeatedly. Starting and stopping a task repeatedly reduces the performance of the application." - Looks like that did the trick! Poll time down from 2600-2800 microseconds to 150-230 microseconds, just by starting the tasks at the beginning. - Tested and successful. Released 2019-07-11. =============================================================================== 4.7.12 (RNC, 21-22 Sep 2019) =============================================================================== - Some code refactoring for better style/legibility. Was the MFC style: CSomeClass SomeFunction iSomeIntegerVariable strSomeStringVariable m_iSomeMemberVariable Now the CamCOPS / near-Stroustrup style, as per https://camcops.readthedocs.io/en/latest/developer/cpp_code_style.html: SomeClass someFunction some_integer_variable some_string_variable m_some_member_variable Note that all actual MFC objects have their traditional style still. Headers: at "K". - Note that "mutable" is very appropriate for mutexes/critical section member variables. This allows appropriate "const" function declarations. - Option not to play "blank" sound -- generates sound energy on some systems. HOWEVER, turning off the blank sound risks spurious sounds from a Windows bug; see above re version 2.8.03 (2 Mar 2004). =============================================================================== 4.7.13 =============================================================================== - Trivial year bump in messages. =============================================================================== Docfix, 12 Dec 2022 =============================================================================== - In the Whisker docs, "DisplaySetObjectEventTransparency" was erroneously listed as "DisplaySetEventTransparency". =============================================================================== 4.7.14, 26 Mar 2024 =============================================================================== - Update Cantab USB DLLs (cantab-usb-driver.dll, ftd2xx.dll) to version of 2023-06-08.