USB Badge Reader
mjones2620
Junior Member
Hello All,
I have a USB badge reader, that I'd like to use for authenticating users to access a touch panel. I have a CSV file of all of the users that are allowed access. I'm trying to figure out the best way to program the panel for authentication. Basically, the badge reader just works as a keyboard function, inputting the badge ID number when it's scanned.
Thanks,
Matt
I have a USB badge reader, that I'd like to use for authenticating users to access a touch panel. I have a CSV file of all of the users that are allowed access. I'm trying to figure out the best way to program the panel for authentication. Basically, the badge reader just works as a keyboard function, inputting the badge ID number when it's scanned.
Thanks,
Matt
Comments
-
If this is a standard HID device you may be able to attach it to an X or S Series panel and have it function the same way as an external keyboard. On you're UI you will then need a 'text input' button (you can set this under the 'General' tab in the button properties editor). You can also have this hidden (opacity: 0) so that this input side stays completely transparent to your users.
When text comes in from this it will then fill into this button. From here you a couple of options:
1) Add a button with it's port set to the loopback port (0) and channel of 'Keyboard: Exit'. When pressed this will fire the contents of all of your text inputs on that page back to the master as a string event prefixed with the text input button's name. Depending on your desired interaction pattern you could also have this hidden and use a do_push *shudder* at say a one second interval when your are in this area of your UI.
2) Alternatively, you could query the contents of this button at a set interval. This would keep all controls hidden and enable your users to auth etc just by the physical interaction with the reader. -
If this is a standard HID device you may be able to attach it to an X or S Series panel and have it function the same way as an external keyboard. On you're UI you will then need a 'text input' button (you can set this under the 'General' tab in the button properties editor). You can also have this hidden (opacity: 0) so that this input side stays completely transparent to your users.
When text comes in from this it will then fill into this button. From here you a couple of options:
1) Add a button with it's port set to the loopback port (0) and channel of 'Keyboard: Exit'. When pressed this will fire the contents of all of your text inputs on that page back to the master as a string event prefixed with the text input button's name. Depending on your desired interaction pattern you could also have this hidden and use a do_push *shudder* at say a one second interval when your are in this area of your UI.
2) Alternatively, you could query the contents of this button at a set interval. This would keep all controls hidden and enable your users to auth etc just by the physical interaction with the reader.
Hey Kim,
Thanks so much for the help! I am trying to do exactly what you thought, hooking it directly to the panel and using it as the keyboard function. What I think I am going to need the most help with is getting it to authenticate based on the numbers it reads. I have a CSV file with all of the badge numbers available, but how do I get the master to check against these numbers?
If you could provide any netlinx code I'd appreciate any help I can get!
THANKS! -
With that you will probably just want to read the CSV into a structure at boot or if it is being updated regularly if you cannot identify a user following the first attempt. I actually did something very similar to this to provide the functionality we would normally get from LDAP at a trade show down here in Australia a few weeks back. You can see the bit of code that does the CSV read here. The explode function that it uses is from the NetLinx Common Libraries.
Depending on your environment though (number of users, frequency of updates etc.) you may want to store this sort of data in an external service though and query it as required. -
With that you will probably just want to read the CSV into a structure at boot or if it is being updated regularly if you cannot identify a user following the first attempt. I actually did something very similar to this to provide the functionality we would normally get from LDAP at a trade show down here in Australia a few weeks back. You can see the bit of code that does the CSV read here. The explode function that it uses is from the NetLinx Common Libraries.
Depending on your environment though (number of users, frequency of updates etc.) you may want to store this sort of data in an external service though and query it as required.
Thanks Kim,
I'm a novice programmer, so I think this is going to be way above my head. For testing of the equipment purposes, is there a simple way to see if the reader is working and make it flip pages?
Thanks,
Matt -
Sure is. If you just want to see if the reader works before diving in too deep just create a touch panel file with a single page and pop a text input button on there. Plug your reader in and see if it fills with content when a read takes place. If so, you're reader is compatible.
Also, don't be afraid I've diving into something that seems over your head. If you keep doing what your comfortable doing you never have any fun
-
Sure is. If you just want to see if the reader works before diving in too deep just create a touch panel file with a single page and pop a text input button on there. Plug your reader in and see if it fills with content when a read takes place. If so, you're reader is compatible.
Also, don't be afraid I've diving into something that seems over your head. If you keep doing what your comfortable doing you never have any fun
Okay thanks,
So I tried that, and sure enough it did read the badge and numbers appeared in the box. Without getting overly complicated for now, how would I create a string of code in netlinx that would authenticate a page flip based on if only my badge ID was entered? I'll dive into the CSV file and all that once I get a better sense of what I'm doing! Thanks for the words of encouragement! You seem like a very talented programmer.
Matt -
Awesome. If you have a look at the programmers guide for your touch panel you will be able to find a few commands in there for reading button text as well as setting it. A nice approach to implementing this may be to have a timeline (check the NetLinx Keywords help from NetLinx Studio for some info on these if you haven't played with them in the past) that checks to see if there's something in here the same length as your card ID's say once a second. If there is you then you have an ID to auth and can clear this out (again, check the programmers guide for the syntax to set button text) so that it's ready for your next read. As I mentioned before you can then make this text input button completely transparent if you like so that a user can walk up to panel and just auth with their card without even noticing what goes on behind the scenes.
-
Awesome. If you have a look at the programmers guide for your touch panel you will be able to find a few commands in there for reading button text as well as setting it. A nice approach to implementing this may be to have a timeline (check the NetLinx Keywords help from NetLinx Studio for some info on these if you haven't played with them in the past) that checks to see if there's something in here the same length as your card ID's say once a second. If there is you then you have an ID to auth and can clear this out (again, check the programmers guide for the syntax to set button text) so that it's ready for your next read. As I mentioned before you can then make this text input button completely transparent if you like so that a user can walk up to panel and just auth with their card without even noticing what goes on behind the scenes.
Been fooling around with this all day: Can't seem to get it to work just for basic testing. Any ideas where I'm going wrong?
PROGRAM_NAME='Keypad Testing'
DEFINE_DEVICE
dvTP = 10001:1:0 //AMX Modero NXTCV10
dvMaster = 0:1:0 //NI700 Master Controller
DEFINE_START
SEND_COMMAND dvTP, "'?TXT-1,0'"
DEFINE_VARIABLE
NON_VOLATILE char Text[11]
DEFINE_EVENT
DATA_EVENT[dvTP]
{
ONLINE:
{
SEND_COMMAND dvTP, 'PAGE-Swipe'
}
}
BUTTON_EVENT[dvTP,212]
{
PUSH:
IF(Text == '13960017776')
{
SEND_COMMAND dvTP, 'PAGE-Error'
}
ELSE
{
SEND_COMMAND dvTP, 'PAGE-Main'
}
} -
IT WORKED!
I was able to use one badge id to authenticate... now how to get multiple badges using a text file is the next step..
PROGRAM_NAME='Keypad Testing'
DEFINE_DEVICE
dvTP = 10001:1:0 //AMX Modero NXTCV10
dvMaster = 0:1:0 //NI700 Master Controller
DEFINE_VARIABLE
NON_VOLATILE CHAR Text
DEFINE_START
SEND_COMMAND dvTP, "'^TXT-1,3',ITOA(Text)"
DEFINE_EVENT
DATA_EVENT[dvTP]
{
ONLINE:
{
SEND_COMMAND dvTP, 'Page-Swipe'
}
STRING:
{
IF
(FIND_STRING(DATA.TEXT,'139600177',1))
SEND_COMMAND dvTP, 'Page-Main'
ELSE
SEND_COMMAND dvTP, 'Page-Swipe'
}
}
BUTTON_EVENT[dvTP,212]
{
PUSH:
{
SEND_COMMAND dvTP, '@PKP'
}
} -
Attached is some code I used to test the NFC functionality. You should be able to adapt this for your USB reader. This code taps into custom event 700 which occurs when the panel reads an NFC card. It uses a file named NFCPriv.txt to hold the data which includes the starting page to flip to when the user logs in. FTP the data file to the master for use. Instead of triggering on the custom event, adapt this to use your string event.
Please note that this code has been edited from a MUCH larger file that tests lots of different things. I know it compiles and I think it will work OK but I don't have any hardware to test it with at the moment so YMMV. -
And where can the normal rabble like us find the list of secret magic event codes? ;-)
-
This particular one is not secret. For example, see page 127 of the Modero X Programming Guide. As for the others...
-
With that you will probably just want to read the CSV into a structure at boot or if it is being updated regularly if you cannot identify a user following the first attempt. I actually did something very similar to this to provide the functionality we would normally get from LDAP at a trade show down here in Australia a few weeks back. You can see the bit of code that does the CSV read here. The explode function that it uses is from the NetLinx Common Libraries.
Depending on your environment though (number of users, frequency of updates etc.) you may want to store this sort of data in an external service though and query it as required.
Kim:
My read_file function continually truncates the length of cSUBJECT with objects that I cannot see in Debug. For whatever reason, it thinks there is a comma or space in the CSV file. Can you tell what I'm doing wrong?DEFINE_FUNCTION fnREAD_FILE_MTG (CHAR cFILENAME[]) { LOCAL_VAR SLONG slFILE_VAL slFILE_VAL = FILE_OPEN(cFILENAME,FILE_READ_ONLY) IF(slFILE_VAL < 0) { SELECT { ACTIVE(slFILE_VAL == -2): SEND_STRING 0, "'Invalid Path'" ACTIVE(slFILE_VAL == -3): SEND_STRING 0, "'Invalid Value'" ACTIVE(slFILE_VAL == -5): SEND_STRING 0, "'I/O Error'" ACTIVE(slFILE_VAL == -14):SEND_STRING 0, "'Max Files Open'" ACTIVE(slFILE_VAL == -15):SEND_STRING 0, "'Invalid File Format'" } } ELSE IF(slFILE_VAL > 0) { LOCAL_VAR SLONG slVAL LOCAL_VAR INTEGER i LOCAL_VAR CHAR cBUFF[120] FOR(i=1; i<=MAX_LENGTH_ARRAY(uMTG); i++) { slVAL = FILE_READ_LINE(slFILE_VAL,cBUFF,MAX_LENGTH_ARRAY(cBUFF)) uMTG[i].cHOST_NAME = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cHOST_NAME,LENGTH_ARRAY(uMTG[i].cHOST_NAME)-1) uMTG[i].cSTART_DATE = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cSTART_DATE,LENGTH_ARRAY(uMTG[i].cSTART_DATE)-1) uMTG[i].cSTART_HOUR = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cSTART_HOUR,LENGTH_ARRAY(uMTG[i].cSTART_HOUR)-1) uMTG[i].cSTART_MINUTE = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cSTART_MINUTE,LENGTH_ARRAY(uMTG[i].cSTART_MINUTE)-1) uMTG[i].cEND_HOUR = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cEND_HOUR,LENGTH_ARRAY(uMTG[i].cEND_HOUR)-1) uMTG[i].cEND_MINUTE = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cEND_MINUTE,LENGTH_ARRAY(uMTG[i].cEND_MINUTE)-1) uMTG[i].cSUBJECT = REMOVE_STRING(cBUFF, ',',1) IF(i > MAX_LENGTH_ARRAY(uMTG)) { BREAK; } } FILE_CLOSE(slFILE_VAL) } } -
Have you got you uMTG definition handy for reference?
One thing I see as a possibility from the code below is your cBUFF variable is only 120 characters. This will result in a max line length of 120. As your subject is the last column of that CSV format it will be the item that is truncated during the read line. -
Have you got you uMTG definition handy for reference?
One thing I see as a possibility from the code below is your cBUFF variable is only 120 characters. This will result in a max line length of 120. As your subject is the last column of that CSV format it will be the item that is truncated during the read line.
I've attached code for reference. I tried to solve the cBUFF issue by having it ignore commas or blank space, but I'd prefer not to. Any tips would be helpful. I changed the cBUFF length back to 2048, what should it be?
Thanks,PROGRAM_NAME='Calendar Demo Rev 1' (***********************************************************) (* FILE CREATED ON: 01/16/2015 AT: 08:00:17 *) (***********************************************************) (* FILE_LAST_MODIFIED_ON: 02/03/2015 AT: 07:17:07 *) (***********************************************************) DEFINE_DEVICE dvMaster = 0:1:0 dvTP = 10001:1:0 dvTP_Locked = 10001:3:0 dvTP_Mtg = 10001:4:0 (***********************************************************) (* CONSTANT DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_CONSTANT MAX_USERS = 500 MAX_MEETINGS = 36 TL_FEEDBACK = 1 TL_FEEDBACK2 = 2 TL_FEEDBACK3 = 3 (***********************************************************) (* DATA TYPE DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_TYPE STRUCTURE _sUSERS //Presets Structure { CHAR cID[15] CHAR cFIRST_NAME[25] CHAR cLAST_NAME[25] CHAR cEMAIL[50] } STRUCTURE _sMTG //Mtg Structure { CHAR cHOST_NAME[25] CHAR cSTART_DATE[25] CHAR cSTART_HOUR[4] CHAR cSTART_MINUTE[4] CHAR cEND_HOUR[4] CHAR cEND_MINUTE[4] CHAR cSUBJECT[50] } (***********************************************************) (* VARIABLE DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_VARIABLE PERSISTENT _sUSERS uUSERS[MAX_USERS] PERSISTENT _sMTG uMTG[MAX_MEETINGS] VOLATILE CHAR cDATA[25] VOLATILE CHAR cBADGE[20] VOLATILE INTEGER nVALID_ENTRY VOLATILE INTEGER nDONE VOLATILE CHAR cHOST_NAME[25] VOLATILE CHAR cSTART_DATE[25] VOLATILE CHAR cSTART_HOUR[4] VOLATILE CHAR cSTART_MINUTE[4] VOLATILE CHAR cEND_HOUR[4] VOLATILE CHAR cEND_MINUTE[4] VOLATILE CHAR cSUBJECT[50] VOLATILE INTEGER nMINUTE VOLATILE INTEGER nHOUR VOLATILE INTEGER nMINUTE2 VOLATILE INTEGER nHOUR2 VOLATILE INTEGER nTIME NON_VOLATILE sINTEGER nCURRENT_HOUR NON_VOLATILE sINTEGER nCURRENT_MIN NON_VOLATILE sINTEGER nCURRENT_MEETING VOLATILE LONG nTIMER[] = {500} VOLATILE LONG nTIMER2[] = {5000} VOLATILE LONG nTIMER3[] = {10000} VOLATILE INTEGER nCLOSED NON_VOLATILE DEVCHAN dcMTGs[] = { {dvTP_Locked, 1}, {dvTP_Locked, 2}, {dvTP_Locked, 3}, {dvTP_Locked, 4}, {dvTP_Locked, 5}, {dvTP_Locked, 6}, {dvTP_Locked, 7}, {dvTP_Locked, 8}, {dvTP_Locked, 9}, {dvTP_Locked, 10}, {dvTP_Locked, 11}, {dvTP_Locked, 12}, {dvTP_Locked, 13}, {dvTP_Locked, 14}, {dvTP_Locked, 15}, {dvTP_Locked, 16}, {dvTP_Locked, 17}, {dvTP_Locked, 18}, {dvTP_Locked, 19}, {dvTP_Locked, 20}, {dvTP_Locked, 21}, {dvTP_Locked, 22}, {dvTP_Locked, 23}, {dvTP_Locked, 24}, {dvTP_Locked, 25}, {dvTP_Locked, 26}, {dvTP_Locked, 27}, {dvTP_Locked, 28}, {dvTP_Locked, 29}, {dvTP_Locked, 30}, {dvTP_Locked, 31}, {dvTP_Locked, 32}, {dvTP_Locked, 33}, {dvTP_Locked, 34}, {dvTP_Locked, 35}, {dvTP_Locked, 36} } (***********************************************************) (* LATCHING DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_LATCHING (***********************************************************) (* MUTUALLY EXCLUSIVE DEFINITIONS GO BELOW *) (***********************************************************) DEFINE_MUTUALLY_EXCLUSIVE (***********************************************************) (* SUBROUTINE/FUNCTION DEFINITIONS GO BELOW *) (***********************************************************) (* EXAMPLE: DEFINE_FUNCTION <RETURN_TYPE> <NAME> (<PARAMETERS>) *) (* EXAMPLE: DEFINE_CALL '<NAME>' (<PARAMETERS>) *) DEFINE_FUNCTION INTEGER fnCONVERT_REQUEST (INTEGER nSTART_HOUR, INTEGER nSTART_MINUTE, INTEGER nEND_HOUR, INTEGER nEND_MINUTE) { STACK_VAR INTEGER nDURATION STACK_VAR i STACK_VAR x IF(nSTART_HOUR == 1) { nSTART_HOUR = 13 } ELSE IF(nSTART_HOUR == 2) { nSTART_HOUR = 14 } ELSE IF(nSTART_HOUR == 3) { nSTART_HOUR = 15 } ELSE IF(nSTART_HOUR == 4) { nSTART_HOUR = 16 } IF(nEND_HOUR == 1) { nEND_HOUR = 13 } ELSE IF(nEND_HOUR == 2) { nEND_HOUR = 14 } ELSE IF(nEND_HOUR == 3) { nEND_HOUR = 15 } ELSE IF(nEND_HOUR == 4) { nEND_HOUR = 16 } ELSE IF(nEND_HOUR == 5) { nEND_HOUR = 17 } i = ((nEND_HOUR - nSTART_HOUR)*60) x = nEND_MINUTE - nSTART_MINUTE nDURATION = i + x RETURN nDURATION } DEFINE_FUNCTION INTEGER fnPROCESS_TIME (INTEGER nHOUR, INTEGER nMINUTE) { STACK_VAR INTEGER t IF(nHOUR > 12) { nHOUR = nHOUR - 12 } IF(nHOUR == 8 && nMINUTE < 15) { t = 1 } ELSE IF(nHOUR == 8 && nMINUTE >= 15 && nMINUTE < 30) { t = 2 } ELSE IF(nHOUR == 8 && nMINUTE >= 30 && nMINUTE < 45) { t = 3 } ELSE IF(nHOUR == 8 && nMINUTE >= 45) { t = 4 } ELSE IF(nHOUR == 9 && nMINUTE < 15) { t = 5 } ELSE IF(nHOUR == 9 && nMINUTE >= 15 && nMINUTE < 30) { t = 6 } ELSE IF(nHOUR == 9 && nMINUTE >= 30 && nMINUTE < 45) { t = 7 } ELSE IF(nHOUR == 9 && nMINUTE >= 45) { t = 8 } ELSE IF(nHOUR == 10 && nMINUTE < 15) { t = 9 } ELSE IF(nHOUR == 10 && nMINUTE >= 15 && nMINUTE < 30) { t = 10 } ELSE IF(nHOUR == 10 && nMINUTE >= 30 && nMINUTE < 45) { t = 11 } ELSE IF(nHOUR == 10 && nMINUTE >= 45) { t = 12 } ELSE IF(nHOUR == 11 && nMINUTE < 15) { t = 13 } ELSE IF(nHOUR == 11 && nMINUTE >= 15 && nMINUTE < 30) { t = 14 } ELSE IF(nHOUR == 11 && nMINUTE >= 30 && nMINUTE < 45) { t = 15 } ELSE IF(nHOUR == 11 && nMINUTE >= 45) { t = 16 } ELSE IF(nHOUR == 12 && nMINUTE < 15) { t = 17 } ELSE IF(nHOUR == 12 && nMINUTE >= 15 && nMINUTE < 30) { t = 18 } ELSE IF(nHOUR == 12 && nMINUTE >= 30 && nMINUTE < 45) { t = 19 } ELSE IF(nHOUR == 12 && nMINUTE >= 45) { t = 20 } ELSE IF(nHOUR == 1 && nMINUTE < 15) { t = 21 } ELSE IF(nHOUR == 1 && nMINUTE >= 15 && nMINUTE < 30) { t = 22 } ELSE IF(nHOUR == 1 && nMINUTE >= 30 && nMINUTE < 45) { t = 23 } ELSE IF(nHOUR == 1 && nMINUTE >= 45) { t = 24 } ELSE IF(nHOUR == 2 && nMINUTE < 15) { t = 25 } ELSE IF(nHOUR == 2 && nMINUTE >= 15 && nMINUTE < 30) { t = 26 } ELSE IF(nHOUR == 2 && nMINUTE >= 30 && nMINUTE < 45) { t = 27 } ELSE IF(nHOUR == 2 && nMINUTE >= 45) { t = 28 } ELSE IF(nHOUR == 3 && nMINUTE < 15) { t = 29 } ELSE IF(nHOUR == 3 && nMINUTE >= 15 && nMINUTE < 30) { t = 30 } ELSE IF(nHOUR == 3 && nMINUTE >= 30 && nMINUTE < 45) { t = 31 } ELSE IF(nHOUR == 3 && nMINUTE >= 45) { t = 32 } ELSE IF(nHOUR == 4 && nMINUTE < 15) { t = 33 } ELSE IF(nHOUR == 4 && nMINUTE >= 15 && nMINUTE < 30) { t = 34 } ELSE IF(nHOUR == 4 && nMINUTE >= 30 && nMINUTE < 45) { t = 35 } ELSE IF(nHOUR == 4 && nMINUTE >= 45) { t = 36 } RETURN t } DEFINE_FUNCTION INTEGER fnFILL_CALENDAR (INTEGER nHOUR, INTEGER nMIN) { STACK_VAR INTEGER x STACK_VAR INTEGER nINDEX FOR(x=1; x<=MAX_LENGTH_ARRAY(uMTG); x++) { IF(LENGTH_ARRAY(uMTG[x].cSUBJECT) > 0) { IF(uMTG[x].cSUBJECT == uMTG[x].cSUBJECT) { ON[dvTP_Locked, x] } } ELSE { OFF[dvTP_Locked, x] } } IF(nHOUR > 12) { nHOUR = nHOUR - 12 } nINDEX = fnPROCESS_TIME(nHOUR, nMIN) IF(LENGTH_ARRAY(uMTG[nINDEX].cSUBJECT) > 0) { SEND_COMMAND dvTP, "'^TXT-1,0,',uMTG[nINDEX].cSUBJECT" RETURN TRUE } ELSE { SEND_COMMAND dvTP, "'^TXT-1,0,Vacant'" RETURN FALSE } } DEFINE_FUNCTION INTEGER fnPROCESS_REQUEST (INTEGER nINDEX, INTEGER nNUM_MTGs) { STACK_VAR INTEGER i IF(LENGTH_ARRAY(uMTG[nINDEX].cSUBJECT) < 1) { FOR(i=1; i<=nNUM_MTGs; i++) { IF(LENGTH_ARRAY(uMTG[nINDEX].cSUBJECT) < 1) { RETURN TRUE } ELSE IF(LENGTH_ARRAY(uMTG[nINDEX].cSUBJECT) > 1) { RETURN FALSE BREAK; } nINDEX++ } } } DEFINE_FUNCTION fnRead_BadgeID (CHAR cFILENAME[]) { LOCAL_VAR SLONG slFILE_VAL slFILE_VAL = FILE_OPEN(cFILENAME,FILE_READ_ONLY) IF(slFILE_VAL < 0) { SELECT { ACTIVE(slFILE_VAL == -2): SEND_STRING 0, "'Invalid Path'" ACTIVE(slFILE_VAL == -3): SEND_STRING 0, "'Invalid Value'" ACTIVE(slFILE_VAL == -5): SEND_STRING 0, "'I/O Error'" ACTIVE(slFILE_VAL == -14):SEND_STRING 0, "'Max Files Open'" ACTIVE(slFILE_VAL == -15):SEND_STRING 0, "'Invalid File Format'" } } ELSE IF(slFILE_VAL > 0) { LOCAL_VAR SLONG slVAL LOCAL_VAR INTEGER i LOCAL_VAR CHAR cBUFF[2048] FOR(i=1; i<=MAX_LENGTH_ARRAY(uUSERS); i++) { slVAL = FILE_READ_LINE(slFILE_VAL,cBUFF,MAX_LENGTH_ARRAY(cBUFF)) uUSERS[i].cID = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uUSERS[i].cID,LENGTH_ARRAY(uUSERS[i].cID)-1) uUSERS[i].cFIRST_NAME = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uUSERS[i].cFIRST_NAME,LENGTH_ARRAY(uUSERS[i].cFIRST_NAME)-1) uUSERS[i].cLAST_NAME = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uUSERS[i].cLAST_NAME,LENGTH_ARRAY(uUSERS[i].cLAST_NAME)-1) uUSERS[i].cEMAIL = cBUFF } FILE_CLOSE(slFILE_VAL) } } DEFINE_FUNCTION fnREAD_FILE_MTG (CHAR cFILENAME[]) { LOCAL_VAR SLONG slFILE_VAL slFILE_VAL = FILE_OPEN(cFILENAME,FILE_READ_ONLY) IF(slFILE_VAL < 0) { SELECT { ACTIVE(slFILE_VAL == -2): SEND_STRING 0, "'Invalid Path'" ACTIVE(slFILE_VAL == -3): SEND_STRING 0, "'Invalid Value'" ACTIVE(slFILE_VAL == -5): SEND_STRING 0, "'I/O Error'" ACTIVE(slFILE_VAL == -14):SEND_STRING 0, "'Max Files Open'" ACTIVE(slFILE_VAL == -15):SEND_STRING 0, "'Invalid File Format'" } } ELSE IF(slFILE_VAL > 0) { LOCAL_VAR SLONG slVAL LOCAL_VAR INTEGER i LOCAL_VAR CHAR cBUFF[2048] FOR(i=1; i<=MAX_LENGTH_ARRAY(uMTG); i++) { slVAL = FILE_READ_LINE(slFILE_VAL,cBUFF,MAX_LENGTH_ARRAY(cBUFF)) uMTG[i].cHOST_NAME = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cHOST_NAME,LENGTH_ARRAY(uMTG[i].cHOST_NAME)-1) uMTG[i].cSTART_DATE = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cSTART_DATE,LENGTH_ARRAY(uMTG[i].cSTART_DATE)-1) uMTG[i].cSTART_HOUR = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cSTART_HOUR,LENGTH_ARRAY(uMTG[i].cSTART_HOUR)-1) uMTG[i].cSTART_MINUTE = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cSTART_MINUTE,LENGTH_ARRAY(uMTG[i].cSTART_MINUTE)-1) uMTG[i].cEND_HOUR = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cEND_HOUR,LENGTH_ARRAY(uMTG[i].cEND_HOUR)-1) uMTG[i].cEND_MINUTE = REMOVE_STRING(cBUFF,',',1) SET_LENGTH_ARRAY(uMTG[i].cEND_MINUTE,LENGTH_ARRAY(uMTG[i].cEND_MINUTE)-1) uMTG[i].cSUBJECT = cBUFF IF((LENGTH_ARRAY(cBUFF) > 0) && LEFT_STRING(cBUFF, 1) == $2C || LEFT_STRING(cBUFF, 1) == $00) { SET_LENGTH_ARRAY(uMTG[i].cHOST_NAME, 0) SET_LENGTH_ARRAY(uMTG[i].cSTART_DATE, 0) SET_LENGTH_ARRAY(uMTG[i].cSTART_HOUR, 0) SET_LENGTH_ARRAY(uMTG[i].cSTART_MINUTE, 0) SET_LENGTH_ARRAY(uMTG[i].cEND_HOUR, 0) SET_LENGTH_ARRAY(uMTG[i].cEND_MINUTE, 0) SET_LENGTH_ARRAY(uMTG[i].cSUBJECT, 0) } } FILE_CLOSE(slFILE_VAL) } } DEFINE_FUNCTION fnWRITE_FILE_MTG(CHAR cFILENAME[]) { LOCAL_VAR CHAR cBUFF[120] LOCAL_VAR SLONG slFILE_VAR LOCAL_VAR INTEGER i LOCAL_VAR SLONG slRESULT slFILE_VAR = FILE_OPEN(cFILENAME, FILE_RW_NEW) IF(slFILE_VAR > 0) { FOR(i=1; i<=MAX_LENGTH_ARRAY(uMTG); i++) { cBUFF = "uMTG[i].cHOST_NAME,',',uMTG[i].cSTART_DATE,',',uMTG[i].cSTART_HOUR,',',uMTG[i].cSTART_MINUTE,',',uMTG[i].cEND_HOUR,',',uMTG[i].cEND_MINUTE,',',uMTG[i].cSUBJECT" slRESULT = FILE_WRITE_LINE(slFILE_VAR,cBUFF,LENGTH_ARRAY(cBUFF)) SEND_STRING 0, "slRESULT" } FILE_CLOSE(slFILE_VAR) SEND_STRING 0,"'GOOD FILE CLOSED ',ITOA(FILE_CLOSE(slFILE_VAR))" } ELSE { SEND_STRING 0,"'BAD FILE OPEN ',ITOA(FILE_CLOSE(slFILE_VAR))" } } (***********************************************************) (* STARTUP CODE GOES BELOW *) (***********************************************************) DEFINE_START TIMELINE_CREATE(TL_FEEDBACK, nTIMER, 1, TIMELINE_ABSOLUTE, TIMELINE_REPEAT) //Half Second TIMELINE_CREATE(TL_FEEDBACK2, nTIMER2, 1, TIMELINE_ABSOLUTE, TIMELINE_REPEAT) // 5 Seconds TIMELINE_CREATE(TL_FEEDBACK3, nTIMER3, 1, TIMELINE_ABSOLUTE, TIMELINE_REPEAT) //60 Seconds (***********************************************************) (* THE EVENTS GO BELOW *) (***********************************************************) DEFINE_EVENT DATA_EVENT[dvMaster] //Master Data Event { ONLINE: { SEND_COMMAND dvTP, "'ADBEEP'" //When online, BEEP! } } DATA_EVENT[dvTP] { STRING: { STACK_VAR INTEGER x SEND_STRING 0, "'FROM TP: ', DATA.TEXT" SELECT { ACTIVE(FIND_STRING(DATA.TEXT, 'Button 16-', 1)): { REMOVE_STRING(DATA.TEXT, 'Button 16-', 1) cBADGE = DATA.TEXT IF(LENGTH_ARRAY(cBADGE == 11)) { fnRead_BadgeID('Badge_IDs.csv') FOR(x=1; x<=MAX_LENGTH_ARRAY(uUSERS); x++) { IF(cBADGE == uUSERS[x].cID) { SEND_COMMAND dvTP, "'@PPN-New Mtg'" SEND_COMMAND dvTP, "'@PPK-Authorization'" SEND_COMMAND dvTP, "'@PPK-Invalid Entry'" SEND_COMMAND dvTP_Mtg, "'^TXT-1,0,',uUSERS[x].cFIRST_NAME,',',uUSERS[x].cLAST_NAME" ON[nVALID_ENTRY] cHOST_NAME = "uUSERS[x].cFIRST_NAME,' ',uUSERS[x].cLAST_NAME" SEND_COMMAND dvTP_Mtg, "'^TXT-2,0,',DATE" BREAK; } ELSE { SEND_COMMAND dvTP, "'@PPN-Invalid Entry'" SEND_COMMAND dvTP, "'@PPK-Authorization'" OFF[nVALID_ENTRY] WAIT 40 { SEND_COMMAND dvTP, "'@PPK-Invalid Entry'" SEND_COMMAND dvTP, "'PAGE-Locked'" } } } } } ACTIVE(FIND_STRING(DATA.TEXT, '@PPF-Authorization;Locked', 1)): { SEND_COMMAND dvTP_Mtg, "'^TXT-3,0,'" SEND_COMMAND dvTP_Mtg, "'^TXT-4,0,'" SEND_COMMAND dvTP_Mtg, "'^TXT-5,0,'" SEND_COMMAND dvTP_Mtg, "'^TXT-6,0,'" SEND_COMMAND dvTP_Mtg, "'^TXT-11,0,'" SEND_COMMAND dvTP_Mtg, "'^TXT-12,0,'" SEND_COMMAND dvTP_Mtg, "'^TXT-13,0,'" } ACTIVE(FIND_STRING(DATA.TEXT,'KEYB-',1)): { REMOVE_STRING(DATA.TEXT,'KEYB-',1) cDATA = DATA.TEXT } ACTIVE(FIND_STRING(DATA.TEXT,'KEYP-',1)): { REMOVE_STRING(DATA.TEXT,'KEYP-',1) IF(nTIME == 1) { nHOUR = ATOI(DATA.TEXT) } ELSE IF(nTIME == 2) { nMINUTE = ATOI(DATA.TEXT) } ELSE IF(nTIME == 3) { nHOUR2 = ATOI(DATA.TEXT) } ELSE IF(nTIME == 4) { nMINUTE2 = ATOI(DATA.TEXT) } } } PULSE[nDONE] } } BUTTON_EVENT[dvTP_Mtg, 3] { PUSH: { TO[BUTTON.INPUT] nTIME = 1; } RELEASE: { WAIT_UNTIL(nDONE) { cSTART_HOUR = ITOA(nHOUR) SEND_COMMAND dvTP_Mtg, "'^TXT-3,0,',cSTART_HOUR" IF(nHOUR >= 8 && nHOUR < 12) { SEND_COMMAND dvTP_Mtg, "'^TXT-5,0,AM'" } ELSE { SEND_COMMAND dvTP_Mtg, "'^TXT-5,0,PM'" } } } } BUTTON_EVENT[dvTP_Mtg, 4] { PUSH: { nTIME = 2; } RELEASE: { WAIT_UNTIL(nDONE) { cSTART_MINUTE = ITOA(nMINUTE) IF(nMINUTE == 0) { cSTART_MINUTE = "'00'" } SEND_COMMAND dvTP_Mtg, "'^TXT-4,0,',cSTART_MINUTE" } } } BUTTON_EVENT[dvTP_Mtg, 11] { PUSH: { nTIME = 3; } RELEASE: { WAIT_UNTIL(nDONE) { cEND_HOUR = ITOA(nHOUR2) SEND_COMMAND dvTP_Mtg, "'^TXT-11,0,',cEND_HOUR" IF(nHOUR2 >= 8 && nHOUR2 < 12) { SEND_COMMAND dvTP_Mtg, "'^TXT-13,0,AM'" } ELSE { SEND_COMMAND dvTP_Mtg, "'^TXT-13,0,PM'" } } } } BUTTON_EVENT[dvTP_Mtg, 12] { PUSH: { nTIME = 4; } RELEASE: { WAIT_UNTIL(nDONE) { cEND_MINUTE = ITOA(nMINUTE2) IF(nMINUTE2 == 0) { cEND_MINUTE = "'00'" } SEND_COMMAND dvTP_Mtg, "'^TXT-12,0,',cEND_MINUTE" } } } BUTTON_EVENT[dvTP_Mtg, 6] { PUSH: { } RELEASE: { WAIT_UNTIL(nDONE) { cSUBJECT = cDATA SEND_COMMAND dvTP_Mtg, "'^TXT-6,0,',cSUBJECT" } } } BUTTON_EVENT[dvTP_Mtg, 10] //reserve a new meeting { PUSH: { LOCAL_VAR INTEGER x LOCAL_VAR INTEGER INDEX LOCAL_VAR INTEGER nDURATION LOCAL_VAR INTEGER nNUM_MTGS LOCAL_VAR INTEGER ACCESS INDEX = fnPROCESS_TIME(nHOUR, nMINUTE) nDURATION = fnCONVERT_REQUEST(nHOUR, nMINUTE, nHOUR2, nMINUTE2) nNUM_MTGS = (nDURATION/15) ACCESS = fnPROCESS_REQUEST(INDEX, nNUM_MTGS) IF(LENGTH_ARRAY(cSUBJECT) > 0) { OFF[dvTP_Mtg, 16] IF(ACCESS == 1) { FOR(x=1; x<=nNUM_MTGS; x++) { uMTG[INDEX].cHOST_NAME = cHOST_NAME uMTG[INDEX].cSTART_DATE = DATE uMTG[INDEX].cSTART_HOUR = cSTART_HOUR uMTG[INDEX].cSTART_MINUTE = cSTART_MINUTE uMTG[INDEX].cEND_HOUR = cEND_HOUR uMTG[INDEX].cEND_MINUTE = cEND_MINUTE uMTG[INDEX].cSUBJECT = cSUBJECT INDEX++ IF(x > nNUM_MTGS) { BREAK; } } fnWRITE_FILE_MTG('New_Mtg.csv') SEND_COMMAND dvTP, "'@PPX-New Mtg'" } ELSE IF(ACCESS == 0) { SEND_COMMAND dvTP, "'@PPN-Error'" } } ELSE { ON[dvTP_Mtg, 16] } } } BUTTON_EVENT[dvTP, 4] //Cancel the current meeting { PUSH: { LOCAL_VAR INTEGER i LOCAL_VAR INTEGER nDURATION2 LOCAL_VAR INTEGER nNUM_MTGs2 LOCAL_VAR INTEGER nLINE nLINE = fnPROCESS_TIME(nCURRENT_HOUR, nCURRENT_MIN) nDURATION2 = fnCONVERT_REQUEST(ATOI(uMTG[nLINE].cSTART_HOUR), ATOI(uMTG[nLINE].cSTART_MINUTE), ATOI(uMTG[nLINE].cEND_HOUR), ATOI(uMTG[nLINE].cEND_MINUTE)) nNUM_MTGs2 = (nDURATION2/15) FOR(i=1; i<=nNUM_MTGS2; i++) { SET_LENGTH_ARRAY(uMTG[nLINE].cHOST_NAME, 0) SET_LENGTH_ARRAY(uMTG[nLINE].cSTART_DATE, 0) SET_LENGTH_ARRAY(uMTG[nLINE].cSTART_HOUR, 0) SET_LENGTH_ARRAY(uMTG[nLINE].cSTART_MINUTE, 0) SET_LENGTH_ARRAY(uMTG[nLINE].cEND_HOUR, 0) SET_LENGTH_ARRAY(uMTG[nLINE].cEND_MINUTE, 0) SET_LENGTH_ARRAY(uMTG[nLINE].cSUBJECT, 0) nLINE++ IF(i > nNUM_MTGs2) { BREAK; } } fnWRITE_FILE_MTG('New_Mtg.csv') } } BUTTON_EVENT[dcMTGs] { PUSH: { STACK_VAR INTEGER BIC BIC = BUTTON.INPUT.CHANNEL ON[dvTP, 6] ON[dvTP, 7] IF(LENGTH_ARRAY(uMTG[BIC].cSUBJECT) > 0) { SEND_COMMAND dvTP, "'^TXT-2,0,',uMTG[BIC].cSUBJECT,' ',uMTG[BIC].cSTART_HOUR,':',uMTG[BIC].cSTART_MINUTE,' - ',uMTG[BIC].cEND_HOUR,':',uMTG[BIC].cEND_MINUTE" } ELSE { SEND_COMMAND dvTP, "'^TXT-2,0,Meeting room vacant'" } WAIT 50 { OFF[dvTP, 6] OFF[dvTP, 7] } } } BUTTON_EVENT[dvTP, 3] { PUSH: { STACK_VAR INTEGER y y = fnPROCESS_TIME(nCURRENT_HOUR, nCURRENT_MIN) IF(LENGTH_ARRAY(uMTG[y].cSUBJECT) > 0) { SEND_COMMAND dvTP, "'^TXT-3,0,',uMTG[y].cSUBJECT" } ELSE { SEND_COMMAND dvTP, "'^TXT-3,0,There is no meeting to cancel...'" } } } TIMELINE_EVENT[TL_FEEDBACK] { nCURRENT_HOUR = TIME_TO_HOUR(TIME) nCURRENT_MIN = TIME_TO_MINUTE(TIME) } TIMELINE_EVENT[TL_FEEDBACK2] { nCURRENT_MEETING = fnFILL_CALENDAR(nCURRENT_HOUR, nCURRENT_MIN) } TIMELINE_EVENT[TL_FEEDBACK3] { fnREAD_FILE_MTG('New_Mtg.csv') } (***********************************************************) (* THE ACTUAL PROGRAM GOES BELOW *) (***********************************************************) DEFINE_PROGRAM [dvTP, 1] = (nCURRENT_MEETING == 1) (***********************************************************) (* END OF PROGRAM *) (* DO NOT PUT ANY CODE BELOW THIS COMMENT *) (***********************************************************) -
Ah, completely missed this the other day. Your last call to remove string where you are populating subject is likely returning nothing. When remove_string(..) is called, if the sequence is not found it will return an empty string.
Unless you have trailing comma's you may have more luck replacing:uMTG[i].cSUBJECT = REMOVE_STRING(cBUFF, ',',1)
withuMTG[i].cSUBJECT = cBUFF
Alternatively, the explode(..) function I pointed to earlier in the thread may be able to neaten things up a bit. -
Ah, completely missed this the other day. Your last call to remove string where you are populating subject is likely returning nothing. When remove_string(..) is called, if the sequence is not found it will return an empty string.
Unless you have trailing comma's you may have more luck replacing:uMTG[i].cSUBJECT = REMOVE_STRING(cBUFF, ',',1)
withuMTG[i].cSUBJECT = cBUFF
Alternatively, the explode(..) function I pointed to earlier in the thread may be able to neaten things up a bit.
Kim,
thank you... that is exactly what is happening. I was able to write my own function to clean up the commas and blank spaces before I saw the explode function! -
Have you got you uMTG definition handy for reference?
One thing I see as a possibility from the code below is your cBUFF variable is only 120 characters. This will result in a max line length of 120. As your subject is the last column of that CSV format it will be the item that is truncated during the read line.
sorry for the delay:DEFINE_TYPE STRUCTURE _sMTG //Mtg Structure { CHAR cHOST_NAME[25] CHAR cSTART_DATE[25] CHAR cSTART_HOUR[4] CHAR cSTART_MINUTE[4] CHAR cEND_HOUR[4] CHAR cEND_MINUTE[4] CHAR cSUBJECT[50] } DEFINE_VARIABLE VOLATILE _sMTG uMTG[MAX_MEETINGS] DEFINE_CONSTANT MAX_MEETINGS = 36
Categories
- All Categories
- 2.5K AMX General Discussion
- 922 AMX Technical Discussion
- 514 AMX Hardware
- 502 AMX Control Products
- 3 AMX Video Distribution Products
- 9 AMX Networked AV (SVSI) Products
- AMX Workspace & Collaboration Products
- 3.4K AMX Software
- 151 AMX Resource Management Suite Software
- 386 AMX Design Tools
- 2.4K NetLinx Studio
- 135 Duet/Cafe Duet
- 248 NetLinx Modules & Duet Modules
- 57 AMX RPM Forum
- 228 MODPEDIA - The Public Repository of Modules for Everyone
- 943 AMX Specialty Forums
- 2.6K AMXForums Archive
- 2.6K AMXForums Archive Threads
- 1.5K AMX Hardware
- 432 AMX Applications and Solutions
- 249 Residential Forum
- 182 Tips and Tricks
- 146 AMX Website/Forums