From f5cc86a06f8ba20d35c212c81c298625c530dbd8 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Mon, 11 Mar 2013 15:15:22 +0300 Subject: [PATCH] Initial jSSC-0.9.0 Repo --- src/cpp/_nix_based/jssc.cpp | 819 ++++++++++++ src/cpp/jssc_SerialNativeInterface.h | 181 +++ src/cpp/windows/jssc.c++ | 658 ++++++++++ src/java/jssc/SerialNativeInterface.java | 403 ++++++ src/java/jssc/SerialPort.java | 1118 +++++++++++++++++ src/java/jssc/SerialPortEvent.java | 192 +++ src/java/jssc/SerialPortEventListener.java | 34 + src/java/jssc/SerialPortException.java | 87 ++ src/java/jssc/SerialPortList.java | 194 +++ src/java/libs/linux/libjSSC-0.9_x86.so | Bin 0 -> 13808 bytes src/java/libs/linux/libjSSC-0.9_x86_64.so | Bin 0 -> 14488 bytes src/java/libs/mac_os_x/libjSSC-0.9_ppc.jnilib | Bin 0 -> 14400 bytes .../libs/mac_os_x/libjSSC-0.9_ppc64.jnilib | Bin 0 -> 14496 bytes src/java/libs/mac_os_x/libjSSC-0.9_x86.jnilib | Bin 0 -> 18204 bytes .../libs/mac_os_x/libjSSC-0.9_x86_64.jnilib | Bin 0 -> 14432 bytes src/java/libs/solaris/libjSSC-0.9_x86.so | Bin 0 -> 15220 bytes src/java/libs/solaris/libjSSC-0.9_x86_64.so | Bin 0 -> 20336 bytes src/java/libs/windows/jSSC-0.9_x86.dll | Bin 0 -> 62976 bytes src/java/libs/windows/jSSC-0.9_x86_64.dll | Bin 0 -> 93696 bytes 19 files changed, 3686 insertions(+) create mode 100644 src/cpp/_nix_based/jssc.cpp create mode 100644 src/cpp/jssc_SerialNativeInterface.h create mode 100644 src/cpp/windows/jssc.c++ create mode 100644 src/java/jssc/SerialNativeInterface.java create mode 100644 src/java/jssc/SerialPort.java create mode 100644 src/java/jssc/SerialPortEvent.java create mode 100644 src/java/jssc/SerialPortEventListener.java create mode 100644 src/java/jssc/SerialPortException.java create mode 100644 src/java/jssc/SerialPortList.java create mode 100644 src/java/libs/linux/libjSSC-0.9_x86.so create mode 100644 src/java/libs/linux/libjSSC-0.9_x86_64.so create mode 100644 src/java/libs/mac_os_x/libjSSC-0.9_ppc.jnilib create mode 100644 src/java/libs/mac_os_x/libjSSC-0.9_ppc64.jnilib create mode 100644 src/java/libs/mac_os_x/libjSSC-0.9_x86.jnilib create mode 100644 src/java/libs/mac_os_x/libjSSC-0.9_x86_64.jnilib create mode 100644 src/java/libs/solaris/libjSSC-0.9_x86.so create mode 100644 src/java/libs/solaris/libjSSC-0.9_x86_64.so create mode 100644 src/java/libs/windows/jSSC-0.9_x86.dll create mode 100644 src/java/libs/windows/jSSC-0.9_x86_64.dll diff --git a/src/cpp/_nix_based/jssc.cpp b/src/cpp/_nix_based/jssc.cpp new file mode 100644 index 0000000..149a6e1 --- /dev/null +++ b/src/cpp/_nix_based/jssc.cpp @@ -0,0 +1,819 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +#include +#include +#include +#include +#include +#include +#include //-D_TS_ERRNO use for Solaris C++ compiler + +#ifdef __linux__ + #include +#endif +#ifdef __SunOS + #include //Needed for FIONREAD in Solaris +#endif +#ifdef __APPLE__ + #include //Needed for IOSSIOSPEED in Mac OS X (Non standard baudrate) +#endif + +#include +#include "jssc_SerialNativeInterface.h" + +//#include //-lCstd use for Solaris linker + + +/* OK */ +/* Port opening */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_openPort(JNIEnv *env, jobject object, jstring portName){ + const char* port = env->GetStringUTFChars(portName, JNI_FALSE); + jint hComm; + hComm = open(port, O_RDWR | O_NOCTTY | O_NDELAY); + if(hComm != -1){ + #if defined TIOCEXCL && !defined __SunOS + ioctl(hComm, TIOCEXCL);//since 0.9 + #endif + int flags = fcntl(hComm, F_GETFL, 0); + flags &= ~O_NDELAY; + fcntl(hComm, F_SETFL, flags); + } + else {//since 0.9 -> + if(errno == EBUSY){//Port busy + hComm = -1; + } + else if(errno == ENOENT){//Port not found + hComm = -2; + } + }//<- since 0.9 + env->ReleaseStringUTFChars(portName, port); + return hComm; +} + +/* OK */ +/* + * Choose baudrate + */ +speed_t getBaudRateByNum(jint baudRate) { + switch(baudRate){ + case 0: + return B0; + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + #ifdef B57600 + case 57600: + return B57600; + #endif + #ifdef B115200 + case 115200: + return B115200; + #endif + #ifdef B230400 + case 230400: + return B230400; + #endif + #ifdef B460800 + case 460800: + return B460800; + #endif + + #ifdef B500000 + case 500000: + return B500000; + #endif + #ifdef B576000 + case 576000: + return B576000; + #endif + #ifdef B921600 + case 921600: + return B921600; + #endif + #ifdef B1000000 + case 1000000: + return B1000000; + #endif + + #ifdef B1152000 + case 1152000: + return B1152000; + #endif + #ifdef B1500000 + case 1500000: + return B1500000; + #endif + #ifdef B2000000 + case 2000000: + return B2000000; + #endif + #ifdef B2500000 + case 2500000: + return B2500000; + #endif + + #ifdef B3000000 + case 3000000: + return B3000000; + #endif + #ifdef B3500000 + case 3500000: + return B3500000; + #endif + #ifdef B4000000 + case 4000000: + return B4000000; + #endif + default: + return -1; + } +} + +/* OK */ +/* + * Choose data bits + */ +int getDataBitsByNum(jint byteSize) { + switch(byteSize){ + case 5: + return CS5; + case 6: + return CS6; + case 7: + return CS7; + case 8: + return CS8; + default: + return -1; + } +} + +/* OK */ +/* + * Set serial port settings + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setParams + (JNIEnv *env, jobject object, jint portHandle, jint baudRate, jint byteSize, jint stopBits, jint parity, jboolean setRTS, jboolean setDTR){ + jboolean returnValue = JNI_FALSE; + + speed_t baudRateValue = getBaudRateByNum(baudRate); + int dataBits = getDataBitsByNum(byteSize); + + termios *settings = new termios(); + if(tcgetattr(portHandle, settings) == 0){ + if(baudRateValue != -1){ + //Set standart baudrate from "termios.h" + if(cfsetispeed(settings, baudRateValue) < 0 || cfsetospeed(settings, baudRateValue) < 0){ + goto methodEnd; + } + } + else { + #ifdef __SunOS + goto methodEnd;//Solaris don't support non standart baudrates + #elif defined __linux__ + //Try to calculate a divisor for setting non standart baudrate + serial_struct *serial_info = new serial_struct(); + if(ioctl(portHandle, TIOCGSERIAL, serial_info) < 0){ //Getting serial_info structure + delete serial_info; + goto methodEnd; + } + else { + serial_info->flags |= ASYNC_SPD_CUST; + serial_info->custom_divisor = (serial_info->baud_base/baudRate); //Calculate divisor + if(serial_info->custom_divisor == 0){ //If divisor == 0 go to method end to prevent "division by zero" error + delete serial_info; + goto methodEnd; + } + settings->c_cflag |= B38400; + if(cfsetispeed(settings, B38400) < 0 || cfsetospeed(settings, B38400) < 0){ + delete serial_info; + goto methodEnd; + } + if(ioctl(portHandle, TIOCSSERIAL, serial_info) < 0){//Try to set new settings with non standart baudrate + delete serial_info; + goto methodEnd; + } + delete serial_info; + } + #endif + } + } + + /* + * Setting data bits + */ + if(dataBits != -1){ + settings->c_cflag &= ~CSIZE; + settings->c_cflag |= dataBits; + } + else { + goto methodEnd; + } + + /* + * Setting stop bits + */ + if(stopBits == 0){ //1 stop bit (for info see ->> MSDN) + settings->c_cflag &= ~CSTOPB; + } + else if((stopBits == 1) || (stopBits == 2)){ //1 == 1.5 stop bits; 2 == 2 stop bits (for info see ->> MSDN) + settings->c_cflag |= CSTOPB; + } + else { + goto methodEnd; + } + + settings->c_cflag |= (CREAD | CLOCAL); + settings->c_cflag &= ~CRTSCTS; + settings->c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOCTL | ECHOPRT | ECHOKE | ISIG | IEXTEN); + + settings->c_iflag &= ~(IXON | IXOFF | IXANY | INPCK | PARMRK | ISTRIP | IGNBRK | BRKINT | INLCR | IGNCR| ICRNL); +#ifdef IUCLC + settings->c_iflag &= ~IUCLC; +#endif + settings->c_oflag &= ~OPOST; + + //since 0.9 -> + settings->c_cc[VMIN] = 0; + settings->c_cc[VTIME] = 0; + //<- since 0.9 + + /* + * Parity bits + */ +#ifdef PAREXT + settings->c_cflag &= ~(PARENB | PARODD | PAREXT);//Clear parity settings +#elif defined CMSPAR + settings->c_cflag &= ~(PARENB | PARODD | CMSPAR);//Clear parity settings +#else + settings->c_cflag &= ~(PARENB | PARODD);//Clear parity settings +#endif + if(parity == 1){//Parity ODD + settings->c_cflag |= (PARENB | PARODD); + settings->c_iflag |= INPCK; + } + else if(parity == 2){//Parity EVEN + settings->c_cflag |= PARENB; + settings->c_iflag |= INPCK; + } + else if(parity == 3){//Parity MARK + #ifdef PAREXT + settings->c_cflag |= (PARENB | PARODD | PAREXT); + settings->c_iflag |= INPCK; + #elif defined CMSPAR + settings->c_cflag |= (PARENB | PARODD | CMSPAR); + settings->c_iflag |= INPCK; + #endif + } + else if(parity == 4){//Parity SPACE + #ifdef PAREXT + settings->c_cflag |= (PARENB | PAREXT); + settings->c_iflag |= INPCK; + #elif defined CMSPAR + settings->c_cflag |= (PARENB | CMSPAR); + settings->c_iflag |= INPCK; + #endif + } + else if(parity == 0){ + //Do nothing (Parity NONE) + } + else { + goto methodEnd; + } + + if(tcsetattr(portHandle, TCSANOW, settings) == 0){//Try to set all settings + #ifdef __APPLE__ + //Try to set non-standard baud rate in Mac OS X + if(baudRateValue == -1){ + speed_t speed = (speed_t)baudRate; + if(ioctl(portHandle, IOSSIOSPEED, &speed) < 0){//IOSSIOSPEED must be used only after tcsetattr + goto methodEnd; + } + } + #endif + int lineStatus; + if(ioctl(portHandle, TIOCMGET, &lineStatus) >= 0){ + if(setRTS == JNI_TRUE){ + lineStatus |= TIOCM_RTS; + } + else { + lineStatus &= ~TIOCM_RTS; + } + if(setDTR == JNI_TRUE){ + lineStatus |= TIOCM_DTR; + } + else { + lineStatus &= ~TIOCM_DTR; + } + if(ioctl(portHandle, TIOCMSET, &lineStatus) >= 0){ + returnValue = JNI_TRUE; + } + } + } + methodEnd: { + delete settings; + return returnValue; + } +} + +const jint PURGE_RXABORT = 0x0002; //ignored +const jint PURGE_RXCLEAR = 0x0008; +const jint PURGE_TXABORT = 0x0001; //ignored +const jint PURGE_TXCLEAR = 0x0004; + +/* OK */ +/* + * PurgeComm + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_purgePort + (JNIEnv *env, jobject object, jint portHandle, jint flags){ + int clearValue = -1; + if((flags & PURGE_RXCLEAR) && (flags & PURGE_TXCLEAR)){ + clearValue = TCIOFLUSH; + } + else if(flags & PURGE_RXCLEAR) { + clearValue = TCIFLUSH; + } + else if(flags & PURGE_TXCLEAR) { + clearValue = TCOFLUSH; + } + else if((flags & PURGE_RXABORT) || (flags & PURGE_TXABORT)){ + return JNI_TRUE; + } + else { + return JNI_FALSE; + } + return tcflush(portHandle, clearValue) == 0 ? JNI_TRUE : JNI_FALSE; +} + +/* OK */ +/* Closing the port */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_closePort + (JNIEnv *env, jobject object, jint portHandle){ + return close(portHandle) == 0 ? JNI_TRUE : JNI_FALSE; +} + +/* OK */ +/* + * Setting events mask + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setEventsMask + (JNIEnv *env, jobject object, jint portHandle, jint mask){ + //Don't needed in linux, implemented in java code + return JNI_TRUE; +} + +/* OK */ +/* + * Getting events mask + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getEventsMask + (JNIEnv *env, jobject object, jint portHandle){ + //Don't needed in linux, implemented in java code + return -1; +} + +/* OK */ +/* + * RTS line status changing (ON || OFF) + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setRTS + (JNIEnv *env, jobject object, jint portHandle, jboolean enabled){ + int returnValue = 0; + int lineStatus; + ioctl(portHandle, TIOCMGET, &lineStatus); + if(enabled == JNI_TRUE){ + lineStatus |= TIOCM_RTS; + } + else { + lineStatus &= ~TIOCM_RTS; + } + returnValue = ioctl(portHandle, TIOCMSET, &lineStatus); + return (returnValue >= 0 ? JNI_TRUE : JNI_FALSE); +} + +/* OK */ +/* + * DTR line status changing (ON || OFF) + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setDTR + (JNIEnv *env, jobject object, jint portHandle, jboolean enabled){ + int returnValue = 0; + int lineStatus; + ioctl(portHandle, TIOCMGET, &lineStatus); + if(enabled == JNI_TRUE){ + lineStatus |= TIOCM_DTR; + } + else { + lineStatus &= ~TIOCM_DTR; + } + returnValue = ioctl(portHandle, TIOCMSET, &lineStatus); + return (returnValue >= 0 ? JNI_TRUE : JNI_FALSE); +} + +/* OK */ +/* + * Writing data to the port + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes + (JNIEnv *env, jobject object, jint portHandle, jbyteArray buffer){ + jbyte* jBuffer = env->GetByteArrayElements(buffer, JNI_FALSE); + jint bufferSize = env->GetArrayLength(buffer); + jint result = write(portHandle, jBuffer, (size_t)bufferSize); + env->ReleaseByteArrayElements(buffer, jBuffer, 0); + return result == bufferSize ? JNI_TRUE : JNI_FALSE; +} + +/* OK */ +/* + * Reading data from the port + */ +JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes + (JNIEnv *env, jobject object, jint portHandle, jint byteCount){ +#ifdef __SunOS + jbyte *lpBuffer = new jbyte[byteCount];//Need for CC compiler + read(portHandle, lpBuffer, byteCount); +#else + jbyte lpBuffer[byteCount]; + read(portHandle, &lpBuffer, byteCount); +#endif + jbyteArray returnArray = env->NewByteArray(byteCount); + env->SetByteArrayRegion(returnArray, 0, byteCount, lpBuffer); +#ifdef __SunOS + delete(lpBuffer); +#endif + return returnArray; +} + +/* OK */ +/* + * Get bytes count in serial port buffers (Input and Output) + */ +JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getBuffersBytesCount + (JNIEnv *env, jobject object, jint portHandle){ + jint returnValues[2]; + returnValues[0] = -1; //Input buffer + returnValues[1] = -1; //Output buffer + jintArray returnArray = env->NewIntArray(2); + ioctl(portHandle, FIONREAD, &returnValues[0]); + ioctl(portHandle, TIOCOUTQ, &returnValues[1]); + env->SetIntArrayRegion(returnArray, 0, 2, returnValues); + return returnArray; +} + +const jint FLOWCONTROL_NONE = 0; +const jint FLOWCONTROL_RTSCTS_IN = 1; +const jint FLOWCONTROL_RTSCTS_OUT = 2; +const jint FLOWCONTROL_XONXOFF_IN = 4; +const jint FLOWCONTROL_XONXOFF_OUT = 8; + +/* OK */ +/* + * Setting flow control mode + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setFlowControlMode + (JNIEnv *env, jobject object, jint portHandle, jint mask){ + jboolean returnValue = JNI_FALSE; + termios *settings = new termios(); + if(tcgetattr(portHandle, settings) == 0){ + settings->c_cflag &= ~CRTSCTS; + settings->c_iflag &= ~(IXON | IXOFF); + if(mask != FLOWCONTROL_NONE){ + if(((mask & FLOWCONTROL_RTSCTS_IN) == FLOWCONTROL_RTSCTS_IN) || ((mask & FLOWCONTROL_RTSCTS_OUT) == FLOWCONTROL_RTSCTS_OUT)){ + settings->c_cflag |= CRTSCTS; + } + if((mask & FLOWCONTROL_XONXOFF_IN) == FLOWCONTROL_XONXOFF_IN){ + settings->c_iflag |= IXOFF; + } + if((mask & FLOWCONTROL_XONXOFF_OUT) == FLOWCONTROL_XONXOFF_OUT){ + settings->c_iflag |= IXON; + } + } + if(tcsetattr(portHandle, TCSANOW, settings) == 0){ + returnValue = JNI_TRUE; + } + } + delete settings; + return returnValue; +} + +/* OK */ +/* + * Getting flow control mode + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getFlowControlMode + (JNIEnv *env, jobject object, jint portHandle){ + jint returnValue = 0; + termios *settings = new termios(); + if(tcgetattr(portHandle, settings) == 0){ + if(settings->c_cflag & CRTSCTS){ + returnValue |= (FLOWCONTROL_RTSCTS_IN | FLOWCONTROL_RTSCTS_OUT); + } + if(settings->c_iflag & IXOFF){ + returnValue |= FLOWCONTROL_XONXOFF_IN; + } + if(settings->c_iflag & IXON){ + returnValue |= FLOWCONTROL_XONXOFF_OUT; + } + } + return returnValue; +} + +/* OK */ +/* + * Send break for setted duration + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_sendBreak + (JNIEnv *env, jobject object, jint portHandle, jint duration){ + jboolean returnValue = JNI_FALSE; + if(duration > 0){ + if(ioctl(portHandle, TIOCSBRK, 0) >= 0){ + int sec = (duration >= 1000 ? duration/1000 : 0); + int nanoSec = (sec > 0 ? duration - sec*1000 : duration)*1000000; + struct timespec *timeStruct = new timespec(); + timeStruct->tv_sec = sec; + timeStruct->tv_nsec = nanoSec; + nanosleep(timeStruct, NULL); + delete(timeStruct); + if(ioctl(portHandle, TIOCCBRK, 0) >= 0){ + returnValue = JNI_TRUE; + } + } + } + return returnValue; +} + +/* OK */ +/* + * Return "statusLines" from ioctl(portHandle, TIOCMGET, &statusLines) + * Need for "_waitEvents" and "_getLinesStatus" + */ +int getLinesStatus(jint portHandle) { + int statusLines; + ioctl(portHandle, TIOCMGET, &statusLines); + return statusLines; +} + +/* OK */ +/* + * Not supported in Solaris and Mac OS X + * + * Get interrupts count for: + * 0 - Break(for BREAK event) + * 1 - TX(for TXEMPTY event) + * --ERRORS(for ERR event)-- + * 2 - Frame + * 3 - Overrun + * 4 - Parity + */ +void getInterruptsCount(jint portHandle, int intArray[]) { +#ifdef TIOCGICOUNT + struct serial_icounter_struct *icount = new serial_icounter_struct(); + if(ioctl(portHandle, TIOCGICOUNT, icount) >= 0){ + intArray[0] = icount->brk; + intArray[1] = icount->tx; + intArray[2] = icount->frame; + intArray[3] = icount->overrun; + intArray[4] = icount->parity; + } + delete icount; +#endif +} + +const jint INTERRUPT_BREAK = 512; +const jint INTERRUPT_TX = 1024; +const jint INTERRUPT_FRAME = 2048; +const jint INTERRUPT_OVERRUN = 4096; +const jint INTERRUPT_PARITY = 8192; + +const jint EV_CTS = 8; +const jint EV_DSR = 16; +const jint EV_RING = 256; +const jint EV_RLSD = 32; +const jint EV_RXCHAR = 1; +//const jint EV_RXFLAG = 2; //Not supported +const jint EV_TXEMPTY = 4; +const jint events[] = {INTERRUPT_BREAK, + INTERRUPT_TX, + INTERRUPT_FRAME, + INTERRUPT_OVERRUN, + INTERRUPT_PARITY, + EV_CTS, + EV_DSR, + EV_RING, + EV_RLSD, + EV_RXCHAR, + //EV_RXFLAG, //Not supported + EV_TXEMPTY}; + +/* OK */ +/* + * Collecting data for EventListener class (Linux have no implementation of "WaitCommEvent" function from Windows) + * + */ +JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_waitEvents + (JNIEnv *env, jobject object, jint portHandle) { + + jclass intClass = env->FindClass("[I"); + jobjectArray returnArray = env->NewObjectArray(sizeof(events)/sizeof(jint), intClass, NULL); + + /*Input buffer*/ + jint bytesCountIn = 0; + ioctl(portHandle, FIONREAD, &bytesCountIn); + + /*Output buffer*/ + jint bytesCountOut = 0; + ioctl(portHandle, TIOCOUTQ, &bytesCountOut); + + /*Lines status*/ + int statusLines = getLinesStatus(portHandle); + + jint statusCTS = 0; + jint statusDSR = 0; + jint statusRING = 0; + jint statusRLSD = 0; + + /*CTS status*/ + if(statusLines & TIOCM_CTS){ + statusCTS = 1; + } + + /*DSR status*/ + if(statusLines & TIOCM_DSR){ + statusDSR = 1; + } + + /*RING status*/ + if(statusLines & TIOCM_RNG){ + statusRING = 1; + } + + /*RLSD(DCD) status*/ + if(statusLines & TIOCM_CAR){ + statusRLSD = 1; + } + + /*Interrupts*/ + int interrupts[] = {-1, -1, -1, -1, -1}; + getInterruptsCount(portHandle, interrupts); + + jint interruptBreak = interrupts[0]; + jint interruptTX = interrupts[1]; + jint interruptFrame = interrupts[2]; + jint interruptOverrun = interrupts[3]; + jint interruptParity = interrupts[4]; + + for(int i = 0; i < sizeof(events)/sizeof(jint); i++){ + jint returnValues[2]; + switch(events[i]) { + + case INTERRUPT_BREAK: //Interrupt Break - for BREAK event + returnValues[1] = interruptBreak; + goto forEnd; + case INTERRUPT_TX: //Interrupt TX - for TXEMPTY event + returnValues[1] = interruptTX; + goto forEnd; + case INTERRUPT_FRAME: //Interrupt Frame - for ERR event + returnValues[1] = interruptFrame; + goto forEnd; + case INTERRUPT_OVERRUN: //Interrupt Overrun - for ERR event + returnValues[1] = interruptOverrun; + goto forEnd; + case INTERRUPT_PARITY: //Interrupt Parity - for ERR event + returnValues[1] = interruptParity; + goto forEnd; + case EV_CTS: + returnValues[1] = statusCTS; + goto forEnd; + case EV_DSR: + returnValues[1] = statusDSR; + goto forEnd; + case EV_RING: + returnValues[1] = statusRING; + goto forEnd; + case EV_RLSD: /*DCD*/ + returnValues[1] = statusRLSD; + goto forEnd; + case EV_RXCHAR: + returnValues[1] = bytesCountIn; + goto forEnd; + /*case EV_RXFLAG: // Event RXFLAG - Not supported + returnValues[0] = EV_RXFLAG; + returnValues[1] = 0; + goto forEnd;*/ + case EV_TXEMPTY: + returnValues[1] = bytesCountOut; + goto forEnd; + } + forEnd: { + returnValues[0] = events[i]; + jintArray singleResultArray = env->NewIntArray(2); + env->SetIntArrayRegion(singleResultArray, 0, 2, returnValues); + env->SetObjectArrayElement(returnArray, i, singleResultArray); + }; + } + return returnArray; +} + +/* OK */ +/* + * Getting serial ports names like an a String array (String[]) + */ +JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_getSerialPortNames + (JNIEnv *env, jobject object){ + //Don't needed in linux, implemented in java code (Note: null will be returned) + return NULL; +} + +/* OK */ +/* + * Getting lines status + * + * returnValues[0] - CTS + * returnValues[1] - DSR + * returnValues[2] - RING + * returnValues[3] - RLSD(DCD) + */ +JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getLinesStatus + (JNIEnv *env, jobject object, jint portHandle){ + jint returnValues[4]; + for(jint i = 0; i < 4; i++){ + returnValues[i] = 0; + } + jintArray returnArray = env->NewIntArray(4); + + /*Lines status*/ + int statusLines = getLinesStatus(portHandle); + + /*CTS status*/ + if(statusLines & TIOCM_CTS){ + returnValues[0] = 1; + } + + /*DSR status*/ + if(statusLines & TIOCM_DSR){ + returnValues[1] = 1; + } + + /*RING status*/ + if(statusLines & TIOCM_RNG){ + returnValues[2] = 1; + } + + /*RLSD(DCD) status*/ + if(statusLines & TIOCM_CAR){ + returnValues[3] = 1; + } + + env->SetIntArrayRegion(returnArray, 0, 4, returnValues); + return returnArray; +} diff --git a/src/cpp/jssc_SerialNativeInterface.h b/src/cpp/jssc_SerialNativeInterface.h new file mode 100644 index 0000000..259da67 --- /dev/null +++ b/src/cpp/jssc_SerialNativeInterface.h @@ -0,0 +1,181 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class jssc_SerialNativeInterface */ + +#ifndef _Included_jssc_SerialNativeInterface +#define _Included_jssc_SerialNativeInterface +#ifdef __cplusplus +extern "C" { +#endif +#undef jssc_SerialNativeInterface_OS_LINUX +#define jssc_SerialNativeInterface_OS_LINUX 0L +#undef jssc_SerialNativeInterface_OS_WINDOWS +#define jssc_SerialNativeInterface_OS_WINDOWS 1L +#undef jssc_SerialNativeInterface_OS_SOLARIS +#define jssc_SerialNativeInterface_OS_SOLARIS 2L +#undef jssc_SerialNativeInterface_OS_MAC_OS_X +#define jssc_SerialNativeInterface_OS_MAC_OS_X 3L +/* + * Class: jssc_SerialNativeInterface + * Method: openPort + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_openPort + (JNIEnv *, jobject, jstring); + +/* + * Class: jssc_SerialNativeInterface + * Method: setParams + * Signature: (IIIIIZZ)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setParams + (JNIEnv *, jobject, jint, jint, jint, jint, jint, jboolean, jboolean); + +/* + * Class: jssc_SerialNativeInterface + * Method: purgePort + * Signature: (II)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_purgePort + (JNIEnv *, jobject, jint, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: closePort + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_closePort + (JNIEnv *, jobject, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: setEventsMask + * Signature: (II)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setEventsMask + (JNIEnv *, jobject, jint, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: getEventsMask + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getEventsMask + (JNIEnv *, jobject, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: waitEvents + * Signature: (I)[[I + */ +JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_waitEvents + (JNIEnv *, jobject, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: setRTS + * Signature: (IZ)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setRTS + (JNIEnv *, jobject, jint, jboolean); + +/* + * Class: jssc_SerialNativeInterface + * Method: setDTR + * Signature: (IZ)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setDTR + (JNIEnv *, jobject, jint, jboolean); + +/* + * Class: jssc_SerialNativeInterface + * Method: readBytes + * Signature: (II)[B + */ +JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes + (JNIEnv *, jobject, jint, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: writeBytes + * Signature: (I[B)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes + (JNIEnv *, jobject, jint, jbyteArray); + +/* + * Class: jssc_SerialNativeInterface + * Method: getBuffersBytesCount + * Signature: (I)[I + */ +JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getBuffersBytesCount + (JNIEnv *, jobject, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: setFlowControlMode + * Signature: (II)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setFlowControlMode + (JNIEnv *, jobject, jint, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: getFlowControlMode + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getFlowControlMode + (JNIEnv *, jobject, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: getSerialPortNames + * Signature: ()[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_getSerialPortNames + (JNIEnv *, jobject); + +/* + * Class: jssc_SerialNativeInterface + * Method: getLinesStatus + * Signature: (I)[I + */ +JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getLinesStatus + (JNIEnv *, jobject, jint); + +/* + * Class: jssc_SerialNativeInterface + * Method: sendBreak + * Signature: (II)Z + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_sendBreak + (JNIEnv *, jobject, jint, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/cpp/windows/jssc.c++ b/src/cpp/windows/jssc.c++ new file mode 100644 index 0000000..ba1c2cf --- /dev/null +++ b/src/cpp/windows/jssc.c++ @@ -0,0 +1,658 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +#include +#include +#include +#include "jssc_SerialNativeInterface.h" + +/* + * Port opening. + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_openPort(JNIEnv *env, jobject object, jstring portName){ + char prefix[] = "\\\\.\\"; + const char* port = env->GetStringUTFChars(portName, JNI_FALSE); + strcat(prefix, port); + HANDLE hComm; + hComm = CreateFile(prefix, + GENERIC_READ | GENERIC_WRITE, + 0, + 0, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + 0); + env->ReleaseStringUTFChars(portName, port); + //since 0.9 -> + if(hComm == INVALID_HANDLE_VALUE){ + DWORD errorValue = GetLastError(); + if(errorValue == ERROR_ACCESS_DENIED){ + hComm = (HANDLE)-1;//Port busy + } + else if(errorValue == ERROR_FILE_NOT_FOUND){ + hComm = (HANDLE)-2;//Port not found + } + } + //<- since 0.9 +#if defined(_X86_) + return (jint)hComm; +#elif defined(__x86_64) + return (intptr_t)hComm; +#endif +}; + +/* + * Setting serial port params. + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setParams + (JNIEnv *env, jobject object, jint portHandle, jint baudRate, jint byteSize, jint stopBits, jint parity, jboolean setRTS, jboolean setDTR){ + HANDLE hComm = (HANDLE)portHandle; + DCB *dcb = new DCB(); + jboolean returnValue = JNI_FALSE; + if(GetCommState(hComm, dcb)){ + dcb->BaudRate = baudRate; + dcb->ByteSize = byteSize; + dcb->StopBits = stopBits; + dcb->Parity = parity; + + //since 0.8 -> + if(setRTS == JNI_TRUE){ + dcb->fRtsControl = RTS_CONTROL_ENABLE; + } + else { + dcb->fRtsControl = RTS_CONTROL_DISABLE; + } + if(setDTR == JNI_TRUE){ + dcb->fDtrControl = DTR_CONTROL_ENABLE; + } + else { + dcb->fDtrControl = DTR_CONTROL_DISABLE; + } + dcb->fOutxCtsFlow = FALSE; + dcb->fOutxDsrFlow = FALSE; + dcb->fDsrSensitivity = FALSE; + dcb->fTXContinueOnXoff = TRUE; + dcb->fOutX = FALSE; + dcb->fInX = FALSE; + dcb->fErrorChar = FALSE; + dcb->fNull = FALSE; + dcb->fAbortOnError = FALSE; + dcb->XonLim = 2048; + dcb->XoffLim = 512; + dcb->XonChar = (char)17; //DC1 + dcb->XoffChar = (char)19; //DC3 + //<- since 0.8 + + if(SetCommState(hComm, dcb)){ + returnValue = JNI_TRUE; + } + } + delete dcb; + return returnValue; +} + +/* + * PurgeComm + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_purgePort + (JNIEnv *env, jobject object, jint portHandle, jint flags){ + HANDLE hComm = (HANDLE)portHandle; + DWORD dwFlags = (DWORD)flags; + return (PurgeComm(hComm, dwFlags) ? JNI_TRUE : JNI_FALSE); +} + +/* + * Port closing + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_closePort + (JNIEnv *env, jobject object, jint portHandle){ + HANDLE hComm = (HANDLE)portHandle; + return (CloseHandle(hComm) ? JNI_TRUE : JNI_FALSE); +} + +/* + * Set events mask + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setEventsMask + (JNIEnv *env, jobject object, jint portHandle, jint mask){ + HANDLE hComm = (HANDLE)portHandle; + DWORD dwEvtMask = (DWORD)mask; + return (SetCommMask(hComm, dwEvtMask) ? JNI_TRUE : JNI_FALSE); +} + +/* + * Get events mask + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getEventsMask + (JNIEnv *env, jobject object, jint portHandle){ + HANDLE hComm = (HANDLE)portHandle; + DWORD lpEvtMask; + if(GetCommMask(hComm, &lpEvtMask)){ + return (jint)lpEvtMask; + } + else { + return -1; + } +} + +/* + * Change RTS line state (ON || OFF) + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setRTS + (JNIEnv *env, jobject object, jint portHandle, jboolean enabled){ + HANDLE hComm = (HANDLE)portHandle; + if(enabled == JNI_TRUE){ + return (EscapeCommFunction(hComm, SETRTS) ? JNI_TRUE : JNI_FALSE); + } + else { + return (EscapeCommFunction(hComm, CLRRTS) ? JNI_TRUE : JNI_FALSE); + } +} + +/* + * Change DTR line state (ON || OFF) + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setDTR + (JNIEnv *env, jobject object, jint portHandle, jboolean enabled){ + HANDLE hComm = (HANDLE)portHandle; + if(enabled == JNI_TRUE){ + return (EscapeCommFunction(hComm, SETDTR) ? JNI_TRUE : JNI_FALSE); + } + else { + return (EscapeCommFunction(hComm, CLRDTR) ? JNI_TRUE : JNI_FALSE); + } +} + +/* + * Write data to port + * portHandle - port handle + * buffer - byte array for sending + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes + (JNIEnv *env, jobject object, jint portHandle, jbyteArray buffer){ + HANDLE hComm = (HANDLE)portHandle; + DWORD lpNumberOfBytesTransferred; + DWORD lpNumberOfBytesWritten; + OVERLAPPED *overlapped = new OVERLAPPED(); + jboolean returnValue = JNI_FALSE; + jbyte* jBuffer = env->GetByteArrayElements(buffer, JNI_FALSE); + overlapped->hEvent = CreateEventA(NULL, true, false, NULL); + if(WriteFile(hComm, jBuffer, (DWORD)env->GetArrayLength(buffer), &lpNumberOfBytesWritten, overlapped)){ + returnValue = JNI_TRUE; + } + else if(GetLastError() == ERROR_IO_PENDING){ + if(WaitForSingleObject(overlapped->hEvent, INFINITE) == WAIT_OBJECT_0){ + if(GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false)){ + returnValue = JNI_TRUE; + } + } + } + env->ReleaseByteArrayElements(buffer, jBuffer, 0); + CloseHandle(overlapped->hEvent); + delete overlapped; + return returnValue; +} + +/* + * Read data from port + * portHandle - port handle + * byteCount - count of bytes for reading + */ +JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes + (JNIEnv *env, jobject object, jint portHandle, jint byteCount){ + HANDLE hComm = (HANDLE)portHandle; + DWORD lpNumberOfBytesTransferred; + DWORD lpNumberOfBytesRead; + OVERLAPPED *overlapped = new OVERLAPPED(); + jbyte lpBuffer[byteCount]; + jbyteArray returnArray = env->NewByteArray(byteCount); + overlapped->hEvent = CreateEventA(NULL, true, false, NULL); + if(ReadFile(hComm, lpBuffer, (DWORD)byteCount, &lpNumberOfBytesRead, overlapped)){ + env->SetByteArrayRegion(returnArray, 0, byteCount, lpBuffer); + } + else if(GetLastError() == ERROR_IO_PENDING){ + if(WaitForSingleObject(overlapped->hEvent, INFINITE) == WAIT_OBJECT_0){ + if(GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false)){ + env->SetByteArrayRegion(returnArray, 0, byteCount, lpBuffer); + } + } + } + CloseHandle(overlapped->hEvent); + delete overlapped; + return returnArray; +} + +/* + * Get bytes count in serial port buffers (Input and Output) + */ +JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getBuffersBytesCount + (JNIEnv *env, jobject object, jint portHandle){ + HANDLE hComm = (HANDLE)portHandle; + jint returnValues[2]; + returnValues[0] = -1; + returnValues[1] = -1; + jintArray returnArray = env->NewIntArray(2); + DWORD lpErrors; + COMSTAT *comstat = new COMSTAT(); + if(ClearCommError(hComm, &lpErrors, comstat)){ + returnValues[0] = (jint)comstat->cbInQue; + returnValues[1] = (jint)comstat->cbOutQue; + } + else { + returnValues[0] = -1; + returnValues[1] = -1; + } + delete comstat; + env->SetIntArrayRegion(returnArray, 0, 2, returnValues); + return returnArray; +} + +//since 0.8 -> +const jint FLOWCONTROL_NONE = 0; +const jint FLOWCONTROL_RTSCTS_IN = 1; +const jint FLOWCONTROL_RTSCTS_OUT = 2; +const jint FLOWCONTROL_XONXOFF_IN = 4; +const jint FLOWCONTROL_XONXOFF_OUT = 8; +//<- since 0.8 + +/* + * Setting flow control mode + * + * since 0.8 + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setFlowControlMode + (JNIEnv *env, jobject object, jint portHandle, jint mask){ + HANDLE hComm = (HANDLE)portHandle; + jboolean returnValue = JNI_FALSE; + DCB *dcb = new DCB(); + if(GetCommState(hComm, dcb)){ + dcb->fRtsControl = RTS_CONTROL_ENABLE; + dcb->fOutxCtsFlow = FALSE; + dcb->fOutX = FALSE; + dcb->fInX = FALSE; + if(mask != FLOWCONTROL_NONE){ + if((mask & FLOWCONTROL_RTSCTS_IN) == FLOWCONTROL_RTSCTS_IN){ + dcb->fRtsControl = RTS_CONTROL_HANDSHAKE; + } + if((mask & FLOWCONTROL_RTSCTS_OUT) == FLOWCONTROL_RTSCTS_OUT){ + dcb->fOutxCtsFlow = TRUE; + } + if((mask & FLOWCONTROL_XONXOFF_IN) == FLOWCONTROL_XONXOFF_IN){ + dcb->fInX = TRUE; + } + if((mask & FLOWCONTROL_XONXOFF_OUT) == FLOWCONTROL_XONXOFF_OUT){ + dcb->fOutX = TRUE; + } + } + if(SetCommState(hComm, dcb)){ + returnValue = JNI_TRUE; + } + } + delete dcb; + return returnValue; +} + +/* + * Getting flow control mode + * + * since 0.8 + */ +JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getFlowControlMode + (JNIEnv *env, jobject object, jint portHandle){ + HANDLE hComm = (HANDLE)portHandle; + jint returnValue = 0; + DCB *dcb = new DCB(); + if(GetCommState(hComm, dcb)){ + if(dcb->fRtsControl == RTS_CONTROL_HANDSHAKE){ + returnValue |= FLOWCONTROL_RTSCTS_IN; + } + if(dcb->fOutxCtsFlow == TRUE){ + returnValue |= FLOWCONTROL_RTSCTS_OUT; + } + if(dcb->fInX == TRUE){ + returnValue |= FLOWCONTROL_XONXOFF_IN; + } + if(dcb->fOutX == TRUE){ + returnValue |= FLOWCONTROL_XONXOFF_OUT; + } + } + delete dcb; + return returnValue; +} + +/* + * Send break for setted duration + * + * since 0.8 + */ +JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_sendBreak + (JNIEnv *env, jobject object, jint portHandle, jint duration){ + HANDLE hComm = (HANDLE)portHandle; + jboolean returnValue = JNI_FALSE; + if(duration > 0){ + if(SetCommBreak(hComm) > 0){ + Sleep(duration); + if(ClearCommBreak(hComm) > 0){ + returnValue = JNI_TRUE; + } + } + } + return returnValue; +} + +/* + * Wait event + * portHandle - port handle + */ +JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_waitEvents + (JNIEnv *env, jobject object, jint portHandle) { + HANDLE hComm = (HANDLE)portHandle; + DWORD lpEvtMask = 0; + DWORD lpNumberOfBytesTransferred = 0; + OVERLAPPED *overlapped = new OVERLAPPED(); + jclass intClass = env->FindClass("[I"); + jobjectArray returnArray; + boolean functionSuccessful = false; + overlapped->hEvent = CreateEventA(NULL, true, false, NULL); + if(WaitCommEvent(hComm, &lpEvtMask, overlapped)){ + functionSuccessful = true; + } + else if(GetLastError() == ERROR_IO_PENDING){ + if(WaitForSingleObject(overlapped->hEvent, INFINITE) == WAIT_OBJECT_0){ + if(GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false)){ + functionSuccessful = true; + } + } + } + if(functionSuccessful){ + boolean executeGetCommModemStatus = false; + boolean executeClearCommError = false; + DWORD events[9];//fixed since 0.8 (old value is 8) + jint eventsCount = 0; + if((EV_BREAK & lpEvtMask) == EV_BREAK){ + events[eventsCount] = EV_BREAK; + eventsCount++; + } + if((EV_CTS & lpEvtMask) == EV_CTS){ + events[eventsCount] = EV_CTS; + eventsCount++; + executeGetCommModemStatus = true; + } + if((EV_DSR & lpEvtMask) == EV_DSR){ + events[eventsCount] = EV_DSR; + eventsCount++; + executeGetCommModemStatus = true; + } + if((EV_ERR & lpEvtMask) == EV_ERR){ + events[eventsCount] = EV_ERR; + eventsCount++; + executeClearCommError = true; + } + if((EV_RING & lpEvtMask) == EV_RING){ + events[eventsCount] = EV_RING; + eventsCount++; + executeGetCommModemStatus = true; + } + if((EV_RLSD & lpEvtMask) == EV_RLSD){ + events[eventsCount] = EV_RLSD; + eventsCount++; + executeGetCommModemStatus = true; + } + if((EV_RXCHAR & lpEvtMask) == EV_RXCHAR){ + events[eventsCount] = EV_RXCHAR; + eventsCount++; + executeClearCommError = true; + } + if((EV_RXFLAG & lpEvtMask) == EV_RXFLAG){ + events[eventsCount] = EV_RXFLAG; + eventsCount++; + executeClearCommError = true; + } + if((EV_TXEMPTY & lpEvtMask) == EV_TXEMPTY){ + events[eventsCount] = EV_TXEMPTY; + eventsCount++; + executeClearCommError = true; + } + /* + * Execute GetCommModemStatus function if it's needed (get lines status) + */ + jint statusCTS = 0; + jint statusDSR = 0; + jint statusRING = 0; + jint statusRLSD = 0; + boolean successGetCommModemStatus = false; + if(executeGetCommModemStatus){ + DWORD lpModemStat; + if(GetCommModemStatus(hComm, &lpModemStat)){ + successGetCommModemStatus = true; + if((MS_CTS_ON & lpModemStat) == MS_CTS_ON){ + statusCTS = 1; + } + if((MS_DSR_ON & lpModemStat) == MS_DSR_ON){ + statusDSR = 1; + } + if((MS_RING_ON & lpModemStat) == MS_RING_ON){ + statusRING = 1; + } + if((MS_RLSD_ON & lpModemStat) == MS_RLSD_ON){ + statusRLSD = 1; + } + } + else { + jint lastError = (jint)GetLastError(); + statusCTS = lastError; + statusDSR = lastError; + statusRING = lastError; + statusRLSD = lastError; + } + } + /* + * Execute ClearCommError function if it's needed (get count of bytes in buffers and errors) + */ + jint bytesCountIn = 0; + jint bytesCountOut = 0; + jint communicationsErrors = 0; + boolean successClearCommError = false; + if(executeClearCommError){ + DWORD lpErrors; + COMSTAT *comstat = new COMSTAT(); + if(ClearCommError(hComm, &lpErrors, comstat)){ + successClearCommError = true; + bytesCountIn = (jint)comstat->cbInQue; + bytesCountOut = (jint)comstat->cbOutQue; + communicationsErrors = (jint)lpErrors; + } + else { + jint lastError = (jint)GetLastError(); + bytesCountIn = lastError; + bytesCountOut = lastError; + communicationsErrors = lastError; + } + delete comstat; + } + /* + * Create int[][] for events values + */ + returnArray = env->NewObjectArray(eventsCount, intClass, NULL); + /* + * Set events values + */ + for(jint i = 0; i < eventsCount; i++){ + jint returnValues[2]; + switch(events[i]){ + case EV_BREAK: + returnValues[0] = (jint)events[i]; + returnValues[1] = 0; + goto forEnd; + case EV_CTS: + returnValues[1] = statusCTS; + goto modemStatus; + case EV_DSR: + returnValues[1] = statusDSR; + goto modemStatus; + case EV_ERR: + returnValues[1] = communicationsErrors; + goto bytesAndErrors; + case EV_RING: + returnValues[1] = statusRING; + goto modemStatus; + case EV_RLSD: + returnValues[1] = statusRLSD; + goto modemStatus; + case EV_RXCHAR: + returnValues[1] = bytesCountIn; + goto bytesAndErrors; + case EV_RXFLAG: + returnValues[1] = bytesCountIn; + goto bytesAndErrors; + case EV_TXEMPTY: + returnValues[1] = bytesCountOut; + goto bytesAndErrors; + default: + returnValues[0] = (jint)events[i]; + returnValues[1] = 0; + goto forEnd; + }; + modemStatus: { + if(successGetCommModemStatus){ + returnValues[0] = (jint)events[i]; + } + else { + returnValues[0] = -1; + } + goto forEnd; + } + bytesAndErrors: { + if(successClearCommError){ + returnValues[0] = (jint)events[i]; + } + else { + returnValues[0] = -1; + } + goto forEnd; + } + forEnd: { + jintArray singleResultArray = env->NewIntArray(2); + env->SetIntArrayRegion(singleResultArray, 0, 2, returnValues); + env->SetObjectArrayElement(returnArray, i, singleResultArray); + }; + } + } + else { + returnArray = env->NewObjectArray(1, intClass, NULL); + jint returnValues[2]; + returnValues[0] = -1; + returnValues[1] = (jint)GetLastError(); + jintArray singleResultArray = env->NewIntArray(2); + env->SetIntArrayRegion(singleResultArray, 0, 2, returnValues); + env->SetObjectArrayElement(returnArray, 0, singleResultArray); + }; + CloseHandle(overlapped->hEvent); + delete overlapped; + return returnArray; +} + +/* + * Get serial port names + */ +JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_getSerialPortNames + (JNIEnv *env, jobject object){ + HKEY phkResult; + LPCSTR lpSubKey = "HARDWARE\\DEVICEMAP\\SERIALCOMM\\"; + jobjectArray returnArray = NULL; + if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &phkResult) == ERROR_SUCCESS){ + boolean hasMoreElements = true; + DWORD keysCount = 0; + char valueName[256]; + DWORD valueNameSize; + DWORD enumResult; + while(hasMoreElements){ + valueNameSize = 256; + enumResult = RegEnumValueA(phkResult, keysCount, valueName, &valueNameSize, NULL, NULL, NULL, NULL); + if(enumResult == ERROR_SUCCESS){ + keysCount++; + } + else if(enumResult == ERROR_NO_MORE_ITEMS){ + hasMoreElements = false; + } + else { + hasMoreElements = false; + } + } + if(keysCount > 0){ + jclass stringClass = env->FindClass("java/lang/String"); + returnArray = env->NewObjectArray((jsize)keysCount, stringClass, NULL); + char lpValueName[256]; + DWORD lpcchValueName; + byte lpData[256]; + DWORD lpcbData; + DWORD result; + for(DWORD i = 0; i < keysCount; i++){ + lpcchValueName = 256; + lpcbData = 256; + result = RegEnumValueA(phkResult, i, lpValueName, &lpcchValueName, NULL, NULL, lpData, &lpcbData); + if(result == ERROR_SUCCESS){ + env->SetObjectArrayElement(returnArray, i, env->NewStringUTF((char*)lpData)); + } + } + } + CloseHandle(phkResult); + } + return returnArray; +} + +/* + * Get lines status + * + * returnValues[0] - CTS + * returnValues[1] - DSR + * returnValues[2] - RING + * returnValues[3] - RLSD + * + */ +JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getLinesStatus + (JNIEnv *env, jobject object, jint portHandle){ + HANDLE hComm = (HANDLE)portHandle; + DWORD lpModemStat; + jint returnValues[4]; + for(jint i = 0; i < 4; i++){ + returnValues[i] = 0; + } + jintArray returnArray = env->NewIntArray(4); + if(GetCommModemStatus(hComm, &lpModemStat)){ + if((MS_CTS_ON & lpModemStat) == MS_CTS_ON){ + returnValues[0] = 1; + } + if((MS_DSR_ON & lpModemStat) == MS_DSR_ON){ + returnValues[1] = 1; + } + if((MS_RING_ON & lpModemStat) == MS_RING_ON){ + returnValues[2] = 1; + } + if((MS_RLSD_ON & lpModemStat) == MS_RLSD_ON){ + returnValues[3] = 1; + } + } + env->SetIntArrayRegion(returnArray, 0, 4, returnValues); + return returnArray; +} diff --git a/src/java/jssc/SerialNativeInterface.java b/src/java/jssc/SerialNativeInterface.java new file mode 100644 index 0000000..2dcfbce --- /dev/null +++ b/src/java/jssc/SerialNativeInterface.java @@ -0,0 +1,403 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +package jssc; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +/** + * + * @author scream3r + */ +public class SerialNativeInterface { + + private static final String libVersion = "0.9"; //jSSC-0.9.0 Release from 21.12.2011 + private static final String libMinorSuffix = "0"; //since 0.9.0 + + public static final int OS_LINUX = 0; + public static final int OS_WINDOWS = 1; + public static final int OS_SOLARIS = 2;//since 0.9.0 + public static final int OS_MAC_OS_X = 3;//since 0.9.0 + + private static int osType = -1; + + static { + String libFolderPath; + String libName; + + String osName = System.getProperty("os.name"); + String architecture = System.getProperty("os.arch"); + String userHome = System.getProperty("user.home"); + String fileSeparator = System.getProperty("file.separator"); + + if(osName.equals("Linux")){ + osName = "linux"; + osType = OS_LINUX; + } + else if(osName.startsWith("Win")){ + osName = "windows"; + osType = OS_WINDOWS; + }//since 0.9.0 -> + else if(osName.equals("SunOS")){ + osName = "solaris"; + osType = OS_SOLARIS; + } + else if(osName.equals("Mac OS X")){ + osName = "mac_os_x"; + osType = OS_MAC_OS_X; + }//<- since 0.9.0 + + if(architecture.equals("i386") || architecture.equals("i686")){ + architecture = "x86"; + } + else if(architecture.equals("amd64")){ + architecture = "x86_64"; + } + + libFolderPath = userHome + fileSeparator + ".jssc" + fileSeparator + osName; + libName = "jSSC-" + libVersion + "_" + architecture; + libName = System.mapLibraryName(libName); + + boolean loadLib = false; + + if(isLibFolderExist(libFolderPath)){ + if(isLibFileExist(libFolderPath + fileSeparator + libName)){ + loadLib = true; + } + else { + if(extractLib((libFolderPath + fileSeparator + libName), osName, libName)){ + loadLib = true; + } + } + } + else { + if(new File(libFolderPath).mkdirs()){ + if(extractLib((libFolderPath + fileSeparator + libName), osName, libName)){ + loadLib = true; + } + } + } + + if(loadLib){ + System.load(libFolderPath + fileSeparator + libName); + } + } + + /** + * Is library folder exists + * + * @param libFolderPath + * + * @since 0.8 + */ + private static boolean isLibFolderExist(String libFolderPath) { + boolean returnValue = false; + File folder = new File(libFolderPath); + if(folder.exists() && folder.isDirectory()){ + returnValue = true; + } + return returnValue; + } + + /** + * Is library file exists + * + * @param libFilePath + * + * @since 0.8 + */ + private static boolean isLibFileExist(String libFilePath) { + boolean returnValue = false; + File folder = new File(libFilePath); + if(folder.exists() && folder.isFile()){ + returnValue = true; + } + return returnValue; + } + + /** + * Extract lib to lib folder + * + * @param libFilePath + * @param osName + * @param libName + * + * @since 0.8 + */ + private static boolean extractLib(String libFilePath, String osName, String libName) { + boolean returnValue = false; + File libFile = new File(libFilePath); + InputStream input = null; + FileOutputStream output = null; + input = SerialNativeInterface.class.getResourceAsStream("/libs/" + osName + "/" + libName); + if(input != null){ + int read; + byte[] buffer = new byte[4096]; + try { + output = new FileOutputStream(libFilePath); + while((read = input.read(buffer)) != -1){ + output.write(buffer, 0, read); + } + output.close(); + input.close(); + returnValue = true; + } + catch (Exception ex) { + try { + output.close(); + if(libFile.exists()){ + libFile.delete(); + } + } + catch (Exception ex_out) { + //Do nothing + } + try { + input.close(); + } + catch (Exception ex_in) { + //Do nothing + } + } + } + return returnValue; + } + + /** + * Get OS type (OS_LINUX || OS_WINDOWS || OS_SOLARIS) + * + * @since 0.8 + */ + public static int getOsType() { + return osType; + } + + /** + * Get jSSC version. The version of library is Base Version + Minor Suffix + * + * @since 0.8 + */ + public static String getLibraryVersion() { + return libVersion + "." + libMinorSuffix; + } + + /** + * Get jSSC Base Version + * + * @since 0.9.0 + */ + public static String getLibraryBaseVersion() { + return libVersion; + } + + /** + * Get jSSC minor suffix. For example in version 0.8.1 - 1 is a minor suffix + * + * @since 0.9.0 + */ + public static String getLibraryMinorSuffix() { + return libMinorSuffix; + } + + /** + * Open port + * + * @param portName name of port for opening + * + * @return handle of opened port or -1 if opening of the port was unsuccessful + */ + public native int openPort(String portName); + + /** + * Setting the parameters of opened port + * + * @param handle handle of opened port + * @param baudRate data transfer rate + * @param dataBits number of data bits + * @param stopBits number of stop bits + * @param parity parity + * @param setRTS initial state of RTS line (ON/OFF) + * @param setDTR initial state of DTR line (ON/OFF) + * + * @return If the operation is successfully completed, the method returns true, otherwise false + */ + public native boolean setParams(int handle, int baudRate, int dataBits, int stopBits, int parity, boolean setRTS, boolean setDTR); + + /** + * Purge of input and output buffer + * + * @param handle handle of opened port + * @param flags flags specifying required actions for purgePort method + * + * @return If the operation is successfully completed, the method returns true, otherwise false + */ + public native boolean purgePort(int handle, int flags); + + /** + * Close port + * + * @param handle handle of opened port + * + * @return If the operation is successfully completed, the method returns true, otherwise false + */ + public native boolean closePort(int handle); + + /** + * Set events mask + * + * @param handle handle of opened port + * @param mask events mask + * + * @return If the operation is successfully completed, the method returns true, otherwise false + */ + public native boolean setEventsMask(int handle, int mask); + + /** + * Get events mask + * + * @param handle handle of opened port + * + * @return Method returns event mask as a variable of int type + */ + public native int getEventsMask(int handle); + + /** + * Wait events + * + * @param handle handle of opened port + * + * @return Method returns two-dimensional array containing event types and their values + * (events[i][0] - event type, events[i][1] - event value). + */ + public native int[][] waitEvents(int handle); + + /** + * Change RTS line state + * + * @param handle handle of opened port + * @param value true - ON, false - OFF + * + * @return If the operation is successfully completed, the method returns true, otherwise false + */ + public native boolean setRTS(int handle, boolean value); + + /** + * Change DTR line state + * + * @param handle handle of opened port + * @param value true - ON, false - OFF + * + * @return If the operation is successfully completed, the method returns true, otherwise false + */ + public native boolean setDTR(int handle, boolean value); + + /** + * Read data from port + * + * @param handle handle of opened port + * @param byteCount count of bytes required to read + * + * @return Method returns the array of read bytes + */ + public native byte[] readBytes(int handle, int byteCount); + + /** + * Write data to port + * + * @param handle handle of opened port + * @param buffer array of bytes to write + * + * @return If the operation is successfully completed, the method returns true, otherwise false + */ + public native boolean writeBytes(int handle, byte[] buffer); + + /** + * Get bytes count in buffers of port + * + * @param handle handle of opened port + * + * @return Method returns the array that contains info about bytes count in buffers: + *
element 0 - input buffer
+ *
element 1 - output buffer
+ * + * @since 0.8 + */ + public native int[] getBuffersBytesCount(int handle); + + /** + * Set flow control mode + * + * @param handle handle of opened port + * @param mask mask of flow control mode + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @since 0.8 + */ + public native boolean setFlowControlMode(int handle, int mask); + + /** + * Get flow control mode + * + * @param handle handle of opened port + * + * @return Mask of setted flow control mode + * + * @since 0.8 + */ + public native int getFlowControlMode(int handle); + + /** + * Get serial port names like an array of String + * + * @return unsorted array of String with port names + */ + public native String[] getSerialPortNames(); + + /** + * Getting lines states + * + * @param handle handle of opened port + * + * @return Method returns the array containing information about lines in following order: + *
element 0 - CTS line state
+ *
element 1 - DSR line state
+ *
element 2 - RING line state
+ *
element 3 - RLSD line state
+ */ + public native int[] getLinesStatus(int handle); + + /** + * Send Break singnal for setted duration + * + * @param handle handle of opened port + * @param duration duration of Break signal + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @since 0.8 + */ + public native boolean sendBreak(int handle, int duration); +} diff --git a/src/java/jssc/SerialPort.java b/src/java/jssc/SerialPort.java new file mode 100644 index 0000000..6985f76 --- /dev/null +++ b/src/java/jssc/SerialPort.java @@ -0,0 +1,1118 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +package jssc; + +/** + * + * @author scream3r + */ +public class SerialPort { + + private SerialNativeInterface serialInterface; + private SerialPortEventListener eventListener; + private int portHandle; + private String portName; + private boolean portOpened = false; + private boolean maskAssigned = false; + private boolean eventListenerAdded = false; + + + public static final int BAUDRATE_110 = 110; + public static final int BAUDRATE_300 = 300; + public static final int BAUDRATE_600 = 600; + public static final int BAUDRATE_1200 = 1200; + public static final int BAUDRATE_4800 = 4800; + public static final int BAUDRATE_9600 = 9600; + public static final int BAUDRATE_14400 = 14400; + public static final int BAUDRATE_19200 = 19200; + public static final int BAUDRATE_38400 = 38400; + public static final int BAUDRATE_57600 = 57600; + public static final int BAUDRATE_115200 = 115200; + public static final int BAUDRATE_128000 = 128000; + public static final int BAUDRATE_256000 = 256000; + + + public static final int DATABITS_5 = 5; + public static final int DATABITS_6 = 6; + public static final int DATABITS_7 = 7; + public static final int DATABITS_8 = 8; + + + public static final int STOPBITS_1 = 1; + public static final int STOPBITS_2 = 2; + public static final int STOPBITS_1_5 = 3; + + + public static final int PARITY_NONE = 0; + public static final int PARITY_ODD = 1; + public static final int PARITY_EVEN = 2; + public static final int PARITY_MARK = 3; + public static final int PARITY_SPACE = 4; + + + public static final int PURGE_RXABORT = 0x0002; + public static final int PURGE_RXCLEAR = 0x0008; + public static final int PURGE_TXABORT = 0x0001; + public static final int PURGE_TXCLEAR = 0x0004; + + + public static final int MASK_RXCHAR = 1; + public static final int MASK_RXFLAG = 2; + public static final int MASK_TXEMPTY = 4; + public static final int MASK_CTS = 8; + public static final int MASK_DSR = 16; + public static final int MASK_RLSD = 32; + public static final int MASK_BREAK = 64; + public static final int MASK_ERR = 128; + public static final int MASK_RING = 256; + + + //since 0.8 -> + public static final int FLOWCONTROL_NONE = 0; + public static final int FLOWCONTROL_RTSCTS_IN = 1; + public static final int FLOWCONTROL_RTSCTS_OUT = 2; + public static final int FLOWCONTROL_XONXOFF_IN = 4; + public static final int FLOWCONTROL_XONXOFF_OUT = 8; + //<- since 0.8 + + //since 0.8 -> + public static final int ERROR_FRAME = 0x0008; + public static final int ERROR_OVERRUN = 0x0002; + public static final int ERROR_PARITY = 0x0004; + //<- since 0.8 + + public SerialPort(String portName) { + this.portName = portName; + serialInterface = new SerialNativeInterface(); + } + + /** + * Getting port name under operation + * + * @return Method returns port name under operation as a String + */ + public String getPortName(){ + return portName; + } + + /** + * Getting port state + * + * @return Method returns true if port is open, otherwise false + */ + public boolean isOpened() { + return portOpened; + } + + /** + * Port opening + *

+ * Note: If port busy TYPE_PORT_BUSY exception will be thrown. + * If port not found TYPE_PORT_NOT_FOUND exception will be thrown. + * + * @return If the operation is successfully completed, the method returns true + * + * @throws SerialPortException + */ + public boolean openPort() throws SerialPortException { + if(portOpened){ + throw new SerialPortException(portName, "openPort()", SerialPortException.TYPE_PORT_ALREADY_OPENED); + } + portHandle = serialInterface.openPort(portName); + //since 0.9.0 -> + if(portHandle == -1){ + throw new SerialPortException(portName, "openPort()", SerialPortException.TYPE_PORT_BUSY); + } + else if(portHandle == -2){ + throw new SerialPortException(portName, "openPort()", SerialPortException.TYPE_PORT_NOT_FOUND); + } + //<- since 0.9.0 + portOpened = true; + return true; + } + + /** + * Setting the parameters of port. RTS and DTR lines are enabled by default + * + * @param baudRate data transfer rate + * @param dataBits number of data bits + * @param stopBits number of stop bits + * @param parity parity + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean setParams(int baudRate, int dataBits, int stopBits, int parity) throws SerialPortException { + checkPortOpened("setParams()"); + if(stopBits == 1){ + stopBits = 0; + } + else if(stopBits == 3){ + stopBits = 1; + } + return serialInterface.setParams(portHandle, baudRate, dataBits, stopBits, parity, true, true); + } + + /** + * Setting the parameters of port + * + * @param baudRate data transfer rate + * @param dataBits number of data bits + * @param stopBits number of stop bits + * @param parity parity + * @param setRTS initial state of RTS line(ON/OFF) + * @param setDTR initial state of DTR line(ON/OFF) + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + * + * @since 0.8 + */ + public boolean setParams(int baudRate, int dataBits, int stopBits, int parity, boolean setRTS, boolean setDTR) throws SerialPortException { + checkPortOpened("setParams()"); + if(stopBits == 1){ + stopBits = 0; + } + else if(stopBits == 3){ + stopBits = 1; + } + return serialInterface.setParams(portHandle, baudRate, dataBits, stopBits, parity, setRTS, setDTR); + } + + /** + * Purge of input and output buffer. Required flags shall be sent to the input. Variables with prefix + * "PURGE_", for example "PURGE_RXCLEAR". Sent parameter "flags" is additive value, + * so addition of flags is allowed. For example, if input or output buffer shall be purged, + * parameter "PURGE_RXCLEAR | PURGE_TXCLEAR". + *
Note: some devices or drivers may not support this function + * + * @return If the operation is successfully completed, the method returns true, otherwise false. + * + * @throws SerialPortException + */ + public boolean purgePort(int flags) throws SerialPortException { + checkPortOpened("purgePort()"); + return serialInterface.purgePort(portHandle, flags); + } + + /** + * Events mask for Linux OS + * + * @since 0.8 + */ + private int linuxMask; + + /** + * Set events mask. Required flags shall be sent to the input. Variables with prefix + * "MASK_", shall be used as flags, for example "MASK_RXCHAR". + * Sent parameter "mask" is additive value, so addition of flags is allowed. + * For example if messages about data receipt and CTS and DSR status changing + * shall be received, it is required to set the mask - "MASK_RXCHAR | MASK_CTS | MASK_DSR" + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean setEventsMask(int mask) throws SerialPortException { + checkPortOpened("setEventsMask()"); + if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_LINUX || + SerialNativeInterface.getOsType() == SerialNativeInterface.OS_SOLARIS || + SerialNativeInterface.getOsType() == SerialNativeInterface.OS_MAC_OS_X){//since 0.9.0 + linuxMask = mask; + if(mask > 0){ + maskAssigned = true; + } + else { + maskAssigned = false; + } + return true; + } + boolean returnValue = serialInterface.setEventsMask(portHandle, mask); + if(!returnValue){ + throw new SerialPortException(portName, "setEventsMask()", SerialPortException.TYPE_CANT_SET_MASK); + } + if(mask > 0){ + maskAssigned = true; + } + else { + maskAssigned = false; + } + return returnValue; + } + + /** + * Getting events mask for the port + * + * @return Method returns events mask as int type variable. This variable is an additive value + * + * @throws SerialPortException + */ + public int getEventsMask() throws SerialPortException { + checkPortOpened("getEventsMask()"); + if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_LINUX || + SerialNativeInterface.getOsType() == SerialNativeInterface.OS_SOLARIS || + SerialNativeInterface.getOsType() == SerialNativeInterface.OS_MAC_OS_X){//since 0.9.0 + return linuxMask; + } + return serialInterface.getEventsMask(portHandle); + } + + /** + * Getting events mask for the port is Linux OS (for internal use) + * + * @since 0.8 + */ + private int getLinuxMask() { + return linuxMask; + } + + /** + * Change RTS line state. Set "true" for switching ON and "false" for switching OFF RTS line + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean setRTS(boolean enabled) throws SerialPortException { + checkPortOpened("setRTS()"); + return serialInterface.setRTS(portHandle, enabled); + } + + /** + * Change DTR line state. Set "true" for switching ON and "false" for switching OFF DTR line + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean setDTR(boolean enabled) throws SerialPortException { + checkPortOpened("setDTR()"); + return serialInterface.setDTR(portHandle, enabled); + } + + /** + * Write byte array to port + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean writeBytes(byte[] buffer) throws SerialPortException { + checkPortOpened("writeBytes()"); + return serialInterface.writeBytes(portHandle, buffer); + } + + /** + * Write single byte to port + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + * + * @since 0.8 + */ + public boolean writeByte(byte singleByte) throws SerialPortException { + checkPortOpened("writeByte()"); + return writeBytes(new byte[]{singleByte}); + } + + /** + * Write String to port + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + * + * @since 0.8 + */ + public boolean writeString(String string) throws SerialPortException { + checkPortOpened("writeString()"); + return writeBytes(string.getBytes()); + } + + /** + * Write int value (in range from 0 to 255 (0x00 - 0xFF)) to port + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + * + * @since 0.8 + */ + public boolean writeInt(int singleInt) throws SerialPortException { + checkPortOpened("writeInt()"); + return writeBytes(new byte[]{(byte)singleInt}); + } + + /** + * Write int array (in range from 0 to 255 (0x00 - 0xFF)) to port + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + * + * @since 0.8 + */ + public boolean writeIntArray(int[] buffer) throws SerialPortException { + checkPortOpened("writeIntArray()"); + byte[] byteArray = new byte[buffer.length]; + for(int i = 0; i < buffer.length; i++){ + byteArray[i] = (byte)buffer[i]; + } + return writeBytes(byteArray); + } + + /** + * Read byte array from port + * + * @param byteCount count of bytes for reading + * + * @return byte array with "byteCount" length + * + * @throws SerialPortException + */ + public byte[] readBytes(int byteCount) throws SerialPortException { + checkPortOpened("readBytes()"); + return serialInterface.readBytes(portHandle, byteCount); + } + + /** + * Read string from port + * + * @param byteCount count of bytes for reading + * + * @return byte array with "byteCount" length converted to String + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String readString(int byteCount) throws SerialPortException { + checkPortOpened("readString()"); + return new String(readBytes(byteCount)); + } + + /** + * Read Hex string from port (example: FF OA FF). Separator by default is a space + * + * @param byteCount count of bytes for reading + * + * @return byte array with "byteCount" length converted to Hexadecimal String + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String readHexString(int byteCount) throws SerialPortException { + checkPortOpened("readHexString()"); + return readHexString(byteCount, " "); + } + + /** + * Read Hex string from port with setted separator (example if separator is "::": FF::OA::FF) + * + * @param byteCount count of bytes for reading + * + * @return byte array with "byteCount" length converted to Hexadecimal String + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String readHexString(int byteCount, String separator) throws SerialPortException { + checkPortOpened("readHexString()"); + String[] strBuffer = readHexStringArray(byteCount); + String returnString = ""; + boolean insertSeparator = false; + for(String value : strBuffer){ + if(insertSeparator){ + returnString += separator; + } + returnString += value; + insertSeparator = true; + } + return returnString; + } + + /** + * Read Hex String array from port + * + * @param byteCount count of bytes for reading + * + * @return String array with "byteCount" length and Hexadecimal String values + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String[] readHexStringArray(int byteCount) throws SerialPortException { + checkPortOpened("readHexStringArray()"); + int[] intBuffer = readIntArray(byteCount); + String[] strBuffer = new String[intBuffer.length]; + for(int i = 0; i < intBuffer.length; i++){ + String value = Integer.toHexString(intBuffer[i]).toUpperCase(); + if(value.length() == 1) { + value = "0" + value; + } + strBuffer[i] = value; + } + return strBuffer; + } + + /** + * Read int array from port + * + * @param byteCount count of bytes for reading + * + * @return int array with values in range from 0 to 255 + * + * @throws SerialPortException + * + * @since 0.8 + */ + public int[] readIntArray(int byteCount) throws SerialPortException { + checkPortOpened("readIntArray()"); + byte[] buffer = readBytes(byteCount); + int[] intBuffer = new int[buffer.length]; + for(int i = 0; i < buffer.length; i++){ + if(buffer[i] < 0){ + intBuffer[i] = 256 + buffer[i]; + } + else { + intBuffer[i] = buffer[i]; + } + } + return intBuffer; + } + + /** + * Read all available bytes from port like a byte array + * + * @return If input buffer is empty null will be returned, else byte array with all data from port + * + * @throws SerialPortException + * + * @since 0.8 + */ + public byte[] readBytes() throws SerialPortException { + checkPortOpened("readBytes()"); + int byteCount = getInputBufferBytesCount(); + if(byteCount <= 0){ + return null; + } + return readBytes(byteCount); + } + + /** + * Read all available bytes from port like a String + * + * @return If input buffer is empty null will be returned, else byte array with all data from port converted to String + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String readString() throws SerialPortException { + checkPortOpened("readString()"); + int byteCount = getInputBufferBytesCount(); + if(byteCount <= 0){ + return null; + } + return readString(byteCount); + } + + /** + * Read all available bytes from port like a Hex String + * + * @return If input buffer is empty null will be returned, else byte array with all data from port converted to Hex String + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String readHexString() throws SerialPortException { + checkPortOpened("readHexString()"); + int byteCount = getInputBufferBytesCount(); + if(byteCount <= 0){ + return null; + } + return readHexString(byteCount); + } + + /** + * Read all available bytes from port like a Hex String with setted separator + * + * @return If input buffer is empty null will be returned, else byte array with all data from port converted to Hex String + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String readHexString(String separator) throws SerialPortException { + checkPortOpened("readHexString()"); + int byteCount = getInputBufferBytesCount(); + if(byteCount <= 0){ + return null; + } + return readHexString(byteCount, separator); + } + + /** + * Read all available bytes from port like a Hex String array + * + * @return If input buffer is empty null will be returned, else byte array with all data from port converted to Hex String array + * + * @throws SerialPortException + * + * @since 0.8 + */ + public String[] readHexStringArray() throws SerialPortException { + checkPortOpened("readHexStringArray()"); + int byteCount = getInputBufferBytesCount(); + if(byteCount <= 0){ + return null; + } + return readHexStringArray(byteCount); + } + + /** + * Read all available bytes from port like a int array (values in range from 0 to 255) + * + * @return If input buffer is empty null will be returned, else byte array with all data from port converted to int array + * + * @throws SerialPortException + * + * @since 0.8 + */ + public int[] readIntArray() throws SerialPortException { + checkPortOpened("readHex()"); + int byteCount = getInputBufferBytesCount(); + if(byteCount <= 0){ + return null; + } + return readIntArray(byteCount); + } + + /** + * Get count of bytes in input buffer + * + * @return Count of bytes in input buffer or -1 if error occured + * + * @throws SerialPortException + * + * @since 0.8 + */ + public int getInputBufferBytesCount() throws SerialPortException { + checkPortOpened("getInputBufferBytesCount()"); + return serialInterface.getBuffersBytesCount(portHandle)[0]; + } + + /** + * Get count of bytes in output buffer + * + * @return Count of bytes in output buffer or -1 if error occured + * + * @throws SerialPortException + * + * @since 0.8 + */ + public int getOutputBufferBytesCount() throws SerialPortException { + checkPortOpened("getOutputBufferBytesCount()"); + return serialInterface.getBuffersBytesCount(portHandle)[1]; + } + + /** + * Set flow control mode. For required mode use variables with prefix "FLOWCONTROL_". + * Example of hardware flow control mode(RTS/CTS): setFlowControlMode(FLOWCONTROL_RTSCTS_IN | FLOWCONTROL_RTSCTS_OUT); + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + * + * @since 0.8 + */ + public boolean setFlowControlMode(int mask) throws SerialPortException { + checkPortOpened("setFlowControlMode()"); + return serialInterface.setFlowControlMode(portHandle, mask); + } + + /** + * Get flow control mode + * + * @return Mask of setted flow control mode + * + * @throws SerialPortException + * + * @since 0.8 + */ + public int getFlowControlMode() throws SerialPortException { + checkPortOpened("getFlowControlMode()"); + return serialInterface.getFlowControlMode(portHandle); + } + + /** + * Send Break singnal for setted duration + * + * @param duration duration of Break signal + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + * + * @since 0.8 + */ + public boolean sendBreak(int duration)throws SerialPortException { + checkPortOpened("sendBreak()"); + return serialInterface.sendBreak(portHandle, duration); + } + + private int[][] waitEvents() { + return serialInterface.waitEvents(portHandle); + } + + /** + * Check port opened (since jSSC-0.8 String "EMPTY" was replaced with "portName" variable) + * + * @param methodName method name + * + * @throws SerialPortException + */ + private void checkPortOpened(String methodName) throws SerialPortException { + if(!portOpened){ + throw new SerialPortException(portName, methodName, SerialPortException.TYPE_PORT_NOT_OPENED); + } + } + + /** + * Getting lines status. Lines status is sent as 0 – OFF and 1 - ON + * + * @return Method returns the array containing information about lines in following order: + *
element 0 - CTS line state
+ *
element 1 - DSR line state
+ *
element 2 - RING line state
+ *
element 3 - RLSD line state
+ * + * @throws SerialPortException + */ + public int[] getLinesStatus() throws SerialPortException { + checkPortOpened("getLinesStatus()"); + return serialInterface.getLinesStatus(portHandle); + } + + /** + * Get state of CTS line + * + * @return If line is active, method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean isCTS() throws SerialPortException { + checkPortOpened("isCTS()"); + if(serialInterface.getLinesStatus(portHandle)[0] == 1){ + return true; + } + else { + return false; + } + } + + /** + * Get state of DSR line + * + * @return If line is active, method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean isDSR() throws SerialPortException { + checkPortOpened("isDSR()"); + if(serialInterface.getLinesStatus(portHandle)[1] == 1){ + return true; + } + else { + return false; + } + } + + /** + * Get state of RING line + * + * @return If line is active, method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean isRING() throws SerialPortException { + checkPortOpened("isRING()"); + if(serialInterface.getLinesStatus(portHandle)[2] == 1){ + return true; + } + else { + return false; + } + } + + /** + * Get state of RLSD line + * + * @return If line is active, method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean isRLSD() throws SerialPortException { + checkPortOpened("isRLSD()"); + if(serialInterface.getLinesStatus(portHandle)[3] == 1){ + return true; + } + else { + return false; + } + } + + /** + * Add event listener. Object of "SerialPortEventListener" type shall + * be sent to the method. This object shall be properly described, as it will + * be in charge for handling of occurred events. This method will independently + * set the mask in "MASK_RXCHAR" state if it was not set beforehand + * + * @throws SerialPortException + */ + public void addEventListener(SerialPortEventListener listener) throws SerialPortException { + checkPortOpened("addEventListener()"); + if(!eventListenerAdded){ + if(maskAssigned){ + eventListener = listener; + eventThread = getNewEventThread(); + eventThread.setName("EventThread " + portName); + eventThread.start(); + eventListenerAdded = true; + } + else { + setEventsMask(MASK_RXCHAR); + eventListener = listener; + eventThread = getNewEventThread(); + eventThread.setName("EventThread " + portName); + eventThread.start(); + eventListenerAdded = true; + } + } + else { + throw new SerialPortException(portName, "addEventListener()", SerialPortException.TYPE_LISTENER_ALREADY_ADDED); + } + } + + /** + * Add event listener. Object of "SerialPortEventListener" type shall be sent + * to the method. This object shall be properly described, as it will be in + * charge for handling of occurred events. Also events mask shall be sent to + * this method, to do it use variables with prefix "MASK_" for example "MASK_RXCHAR" + * + * @see #setEventsMask(int) setEventsMask(int mask) + * + * @throws SerialPortException + */ + public void addEventListener(SerialPortEventListener listener, int mask) throws SerialPortException { + checkPortOpened("addEventListener()"); + if(!eventListenerAdded){ + setEventsMask(mask); + eventListener = listener; + eventThread = getNewEventThread(); + eventThread.setName("EventThread " + portName); + eventThread.start(); + eventListenerAdded = true; + } + else { + throw new SerialPortException(portName, "addEventListener()", SerialPortException.TYPE_LISTENER_ALREADY_ADDED); + } + } + + /** + * Create new EventListener Thread depending on the type of operating system + * + * @since 0.8 + */ + private EventThread getNewEventThread() { + if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_LINUX || + SerialNativeInterface.getOsType() == SerialNativeInterface.OS_SOLARIS || + SerialNativeInterface.getOsType() == SerialNativeInterface.OS_MAC_OS_X){//since 0.9.0 + return new LinuxEventThread(); + } + return new EventThread(); + } + + /** + * Delete event listener. Mask is set to 0. So at the next addition of event + * handler you shall set required event mask again + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean removeEventListener() throws SerialPortException { + checkPortOpened("removeEventListener()"); + if(!eventListenerAdded){ + throw new SerialPortException(portName, "removeEventListener()", SerialPortException.TYPE_CANT_REMOVE_LISTENER); + } + eventThread.terminateThread(); + setEventsMask(0); + if(Thread.currentThread().getId() != eventThread.getId()){ + if(eventThread.isAlive()){ + try { + eventThread.join(5000); + } + catch (InterruptedException ex) { + throw new SerialPortException(portName, "removeEventListener()", SerialPortException.TYPE_LISTENER_THREAD_INTERRUPTED); + } + } + } + eventListenerAdded = false; + return true; + } + + /** + * Close port. This method deletes event listener first, then closes the port + * + * @return If the operation is successfully completed, the method returns true, otherwise false + * + * @throws SerialPortException + */ + public boolean closePort() throws SerialPortException { + checkPortOpened("closePort()"); + if(eventListenerAdded){ + removeEventListener(); + } + boolean returnValue = serialInterface.closePort(portHandle); + if(returnValue){ + maskAssigned = false; + portOpened = false; + } + return returnValue; + } + + private EventThread eventThread; + + private class EventThread extends Thread { + + private boolean threadTerminated = false; + + @Override + public void run() { + while(!threadTerminated){ + int[][] eventArray = waitEvents(); + for(int i = 0; i < eventArray.length; i++){ + if(eventArray[i][0] > 0 && !threadTerminated){ + eventListener.serialEvent(new SerialPortEvent(portName, eventArray[i][0], eventArray[i][1])); + } + } + } + } + + private void terminateThread(){ + threadTerminated = true; + } + } + + /** + * EventListener for Linux OS + * + * @since 0.8 + */ + private class LinuxEventThread extends EventThread { + + //Essential interruptions for events: BREAK, ERR, TXEMPTY + private final int INTERRUPT_BREAK = 512; + private final int INTERRUPT_TX = 1024; + private final int INTERRUPT_FRAME = 2048; + private final int INTERRUPT_OVERRUN = 4096; + private final int INTERRUPT_PARITY = 8192; + + //Count of interruptions + private int interruptBreak; + private int interruptTX; + private int interruptFrame; + private int interruptOverrun; + private int interruptParity; + + //Previous states if lines (then state change event will be generated) + private int preCTS; + private int preDSR; + private int preRLSD; + private int preRING; + + //Need to get initial states + public LinuxEventThread(){ + int[][] eventArray = waitEvents(); + for(int i = 0; i < eventArray.length; i++){ + int eventType = eventArray[i][0]; + int eventValue = eventArray[i][1]; + switch(eventType){ + case INTERRUPT_BREAK: + interruptBreak = eventValue; + break; + case INTERRUPT_TX: + interruptTX = eventValue; + break; + case INTERRUPT_FRAME: + interruptFrame = eventValue; + break; + case INTERRUPT_OVERRUN: + interruptOverrun = eventValue; + break; + case INTERRUPT_PARITY: + interruptParity = eventValue; + break; + case MASK_CTS: + preCTS = eventValue; + break; + case MASK_DSR: + preDSR = eventValue; + break; + case MASK_RING: + preRING = eventValue; + break; + case MASK_RLSD: + preRLSD = eventValue; + break; + } + } + } + + @Override + public void run() { + while(!super.threadTerminated){ + int[][] eventArray = waitEvents(); + int mask = getLinuxMask(); + boolean interruptTxChanged = false; + int errorMask = 0; + for(int i = 0; i < eventArray.length; i++){ + boolean sendEvent = false; + int eventType = eventArray[i][0]; + int eventValue = eventArray[i][1]; + if(eventType > 0 && !super.threadTerminated){ + switch(eventType){ + case INTERRUPT_BREAK: + if(eventValue != interruptBreak){ + interruptBreak = eventValue; + if((mask & MASK_BREAK) == MASK_BREAK){ + eventType = MASK_BREAK; + eventValue = 0; + sendEvent = true; + } + } + break; + case INTERRUPT_TX: + if(eventValue != interruptTX){ + interruptTX = eventValue; + interruptTxChanged = true; + } + break; + case INTERRUPT_FRAME: + if(eventValue != interruptFrame){ + interruptFrame = eventValue; + errorMask |= ERROR_FRAME; + } + break; + case INTERRUPT_OVERRUN: + if(eventValue != interruptOverrun){ + interruptOverrun = eventValue; + errorMask |= ERROR_OVERRUN; + } + break; + case INTERRUPT_PARITY: + if(eventValue != interruptParity){ + interruptParity = eventValue; + errorMask |= ERROR_PARITY; + } + if((mask & MASK_ERR) == MASK_ERR && errorMask != 0){ + eventType = MASK_ERR; + eventValue = errorMask; + sendEvent = true; + } + break; + case MASK_CTS: + if(eventValue != preCTS){ + preCTS = eventValue; + if((mask & MASK_CTS) == MASK_CTS){ + sendEvent = true; + } + } + break; + case MASK_DSR: + if(eventValue != preDSR){ + preDSR = eventValue; + if((mask & MASK_DSR) == MASK_DSR){ + sendEvent = true; + } + } + break; + case MASK_RING: + if(eventValue != preRING){ + preRING = eventValue; + if((mask & MASK_RING) == MASK_RING){ + sendEvent = true; + } + } + break; + case MASK_RLSD: /*DCD*/ + if(eventValue != preRLSD){ + preRLSD = eventValue; + if((mask & MASK_RLSD) == MASK_RLSD){ + sendEvent = true; + } + } + break; + case MASK_RXCHAR: + if(((mask & MASK_RXCHAR) == MASK_RXCHAR) && (eventValue > 0)){ + sendEvent = true; + } + break; + /*case MASK_RXFLAG: + //Do nothing at this moment + if(((mask & MASK_RXFLAG) == MASK_RXFLAG) && (eventValue > 0)){ + sendEvent = true; + } + break;*/ + case MASK_TXEMPTY: + if(((mask & MASK_TXEMPTY) == MASK_TXEMPTY) && (eventValue == 0) && interruptTxChanged){ + sendEvent = true; + } + break; + } + if(sendEvent){ + eventListener.serialEvent(new SerialPortEvent(portName, eventType, eventValue)); + } + } + } + //Need to sleep some time + try { + Thread.sleep(0, 100); + } + catch (Exception ex) { + //Do nothing + } + } + } + } +} diff --git a/src/java/jssc/SerialPortEvent.java b/src/java/jssc/SerialPortEvent.java new file mode 100644 index 0000000..19f69ae --- /dev/null +++ b/src/java/jssc/SerialPortEvent.java @@ -0,0 +1,192 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +package jssc; + +/** + * + * @author scream3r + */ +public class SerialPortEvent { + + private String portName; + private int eventType; + private int eventValue; + + public static final int RXCHAR = 1; + public static final int RXFLAG = 2; + public static final int TXEMPTY = 4; + public static final int CTS = 8; + public static final int DSR = 16; + public static final int RLSD = 32; + public static final int BREAK = 64; + public static final int ERR = 128; + public static final int RING = 256; + + public SerialPortEvent(String portName, int eventType, int eventValue){ + this.portName = portName; + this.eventType = eventType; + this.eventValue = eventValue; + } + + /** + * Getting port name which sent the event + */ + public String getPortName() { + return portName; + } + + /** + * Getting event type + */ + public int getEventType() { + return eventType; + } + + /** + * Getting event value + *

+ *
Event values depending on their types:
+ *
RXCHAR - bytes count in input buffer
+ *
RXFLAG - bytes count in input buffer (Not supported in Linux)
+ *
TXEMPTY - bytes count in output buffer
+ *
CTS - state of CTS line (0 - OFF, 1 - ON)
+ *
DSR - state of DSR line (0 - OFF, 1 - ON)
+ *
RLSD - state of RLSD line (0 - OFF, 1 - ON)
+ *
BREAK - 0
+ *
RING - state of RING line (0 - OFF, 1 - ON)
+ *
ERR - mask of errors
+ */ + public int getEventValue() { + return eventValue; + } + + /** + * Method returns true if event of type "RXCHAR" is received and otherwise false + */ + public boolean isRXCHAR() { + if(eventType == RXCHAR){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "RXFLAG" is received and otherwise false + */ + public boolean isRXFLAG() { + if(eventType == RXFLAG){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "TXEMPTY" is received and otherwise false + */ + public boolean isTXEMPTY() { + if(eventType == TXEMPTY){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "CTS" is received and otherwise false + */ + public boolean isCTS() { + if(eventType == CTS){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "DSR" is received and otherwise false + */ + public boolean isDSR() { + if(eventType == DSR){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "RLSD" is received and otherwise false + */ + public boolean isRLSD() { + if(eventType == RLSD){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "BREAK" is received and otherwise false + */ + public boolean isBREAK() { + if(eventType == BREAK){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "ERR" is received and otherwise false + */ + public boolean isERR() { + if(eventType == ERR){ + return true; + } + else { + return false; + } + } + + /** + * Method returns true if event of type "RING" is received and otherwise false + */ + public boolean isRING() { + if(eventType == RING){ + return true; + } + else { + return false; + } + } +} diff --git a/src/java/jssc/SerialPortEventListener.java b/src/java/jssc/SerialPortEventListener.java new file mode 100644 index 0000000..d7214e7 --- /dev/null +++ b/src/java/jssc/SerialPortEventListener.java @@ -0,0 +1,34 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +package jssc; + +/** + * + * @author scream3r + */ +public interface SerialPortEventListener { + + public abstract void serialEvent(SerialPortEvent serialPortEvent); +} diff --git a/src/java/jssc/SerialPortException.java b/src/java/jssc/SerialPortException.java new file mode 100644 index 0000000..0d1c100 --- /dev/null +++ b/src/java/jssc/SerialPortException.java @@ -0,0 +1,87 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +package jssc; + +/** + * + * @author scream3r + */ +public class SerialPortException extends Exception { + + final public static String TYPE_PORT_ALREADY_OPENED = "Port already opened"; + final public static String TYPE_PORT_NOT_OPENED = "Port not opened"; + final public static String TYPE_CANT_SET_MASK = "Can't set mask"; + final public static String TYPE_LISTENER_ALREADY_ADDED = "Event listener already added"; + final public static String TYPE_LISTENER_THREAD_INTERRUPTED = "Event listener thread interrupted"; + final public static String TYPE_CANT_REMOVE_LISTENER = "Can't remove event listener, because listener not added"; + /** + * @since 0.8 + */ + final public static String TYPE_PARAMETER_IS_NOT_CORRECT = "Parameter is not correct"; + /** + * @since 0.8 + */ + final public static String TYPE_NULL_NOT_PERMITTED = "Null not permitted"; + /** + * @since 0.9.0 + */ + final public static String TYPE_PORT_BUSY = "Port busy"; + /** + * @since 0.9.0 + */ + final public static String TYPE_PORT_NOT_FOUND = "Port not found"; + + private String portName; + private String methodName; + private String exceptionType; + + public SerialPortException(String portName, String methodName, String exceptionType){ + super("Port name - " + portName + "; Method name - " + methodName + "; Exception type - " + exceptionType + "."); + this.portName = portName; + this.methodName = methodName; + this.exceptionType = exceptionType; + } + + /** + * Getting port name during operation with which the exception was called + */ + public String getPortName(){ + return portName; + } + + /** + * Getting method name during execution of which the exception was called + */ + public String getMethodName(){ + return methodName; + } + + /** + * Getting exception type + */ + public String getExceptionType(){ + return exceptionType; + } +} diff --git a/src/java/jssc/SerialPortList.java b/src/java/jssc/SerialPortList.java new file mode 100644 index 0000000..513181c --- /dev/null +++ b/src/java/jssc/SerialPortList.java @@ -0,0 +1,194 @@ +/* jSSC (Java Simple Serial Connector) - serial port communication library. + * © Alexey Sokolov (scream3r), 2010-2011. + * + * This file is part of jSSC. + * + * jSSC is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * jSSC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with jSSC. If not, see . + * + * If you use jSSC in public project you can inform me about this by e-mail, + * of course if you want it. + * + * e-mail: scream3r.org@gmail.com + * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ + */ +package jssc; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.TreeSet; + +/** + * + * @author scream3r + */ +public class SerialPortList { + + private static SerialNativeInterface serialInterface; + private static Comparator comparator = new Comparator() { + @Override + public int compare(String valueA, String valueB) { + int result = 0; + if(valueA.toLowerCase().contains("com") && valueB.toLowerCase().contains("com")){ + try { + int index1 = Integer.valueOf(valueA.toLowerCase().replace("com", "")); + int index2 = Integer.valueOf(valueB.toLowerCase().replace("com", "")); + result = index1 - index2; + } + catch (Exception ex) { + result = valueA.compareToIgnoreCase(valueB); + } + } + else { + result = valueA.compareToIgnoreCase(valueB); + } + return result; + } + }; + + static { + serialInterface = new SerialNativeInterface(); + } + + /** + * Get sorted array of serial ports in the system + * + * @return String array. If there is no ports in the system String[] + * with zero length will be returned (since jSSC-0.8 in previous versions null will be returned) + */ + public static String[] getPortNames() { + if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_LINUX){ + return getLinuxPortNames(); + } + else if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_SOLARIS){//since 0.9.0 -> + return getSolarisPortNames(); + } + else if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_MAC_OS_X){ + return getMacOSXPortNames(); + }//<-since 0.9.0 + String[] portNames = serialInterface.getSerialPortNames(); + if(portNames == null){ + return new String[]{}; + } + TreeSet ports = new TreeSet(comparator); + ports.addAll(Arrays.asList(portNames)); + return ports.toArray(new String[ports.size()]); + } + + /** + * Get serial port names in Linux OS (This method was completely rewrited in 0.8-tb4) + * + * @return + */ + private static String[] getLinuxPortNames() { + String[] returnArray = new String[]{}; + try { + Process dmesgProcess = Runtime.getRuntime().exec("dmesg"); + BufferedReader reader = new BufferedReader(new InputStreamReader(dmesgProcess.getInputStream())); + TreeSet portsTree = new TreeSet(); + ArrayList portsList = new ArrayList(); + String buffer = ""; + while((buffer = reader.readLine()) != null && !buffer.isEmpty()){ + if(buffer.matches(".*(ttyS|ttyUSB)[0-9]{1,3}.*")){ + String[] tmp = buffer.split(" "); + for(String value : tmp){ + if(value.matches("(ttyS|ttyUSB)[0-9]{1,3}")){ + portsTree.add("/dev/" + value); + } + } + } + } + for(String portName : portsTree){ + SerialPort serialPort = new SerialPort(portName); + try { + if(serialPort.openPort()){ + portsList.add(portName); + serialPort.closePort(); + } + } + catch (SerialPortException ex) { + //since 0.9.0 -> + if(ex.getExceptionType().equals(SerialPortException.TYPE_PORT_BUSY)){ + portsList.add(portName); + } + //<- since 0.9.0 + } + } + returnArray = portsList.toArray(returnArray); + reader.close(); + } + catch (IOException ex) { + //Do nothing + } + return returnArray; + } + + /** + * Get serial port names in Solaris OS + * + * @since 0.9.0 + */ + private static String[] getSolarisPortNames() { + String[] returnArray = new String[]{}; + File dir = new File("/dev/term"); + if(dir.exists() && dir.isDirectory()){ + File[] files = dir.listFiles(); + if(files.length > 0){ + TreeSet portsTree = new TreeSet(); + ArrayList portsList = new ArrayList(); + for(File file : files){ + if(!file.isDirectory() && !file.isFile() && file.getName().matches("[0-9]*|[a-z]*")){ + portsTree.add("/dev/term/" + file.getName()); + } + } + for(String portName : portsTree){ + portsList.add(portName); + } + returnArray = portsList.toArray(returnArray); + } + } + return returnArray; + } + + /** + * Get serial port names in Mac OS X + * + * @since 0.9.0 + */ + private static String[] getMacOSXPortNames() { + String[] returnArray = new String[]{}; + File dir = new File("/dev"); + if(dir.exists() && dir.isDirectory()){ + File[] files = dir.listFiles(); + if(files.length > 0){ + TreeSet portsTree = new TreeSet(); + ArrayList portsList = new ArrayList(); + for(File file : files){ + if(!file.isDirectory() && !file.isFile() && file.getName().matches("tty.(serial.*|usbserial.*)")){ + portsTree.add("/dev/" + file.getName()); + } + } + for(String portName : portsTree){ + portsList.add(portName); + } + returnArray = portsList.toArray(returnArray); + } + } + return returnArray; + } +} diff --git a/src/java/libs/linux/libjSSC-0.9_x86.so b/src/java/libs/linux/libjSSC-0.9_x86.so new file mode 100644 index 0000000000000000000000000000000000000000..fa08c409f1493f0294e8248b09b4db787a8a08d3 GIT binary patch literal 13808 zcmeHOad=eKmA{ir!azc1#F$c{Ft!5)(U@rJhE+5KCO}PW3Ji?`PKIPcBFRjec>}?M zX7(k>a}3Q2`mtNPA6D1JO1J4p*NwIP3`rzNQBtKXUq4!A+h`|(8dnTyL}!1$`{qrC z)P8CE*Z#rfJ305QnM}ecsUlU7s+=xF0dQBo-cum#Vv1NG zW{GQMzrjyj;?N3O}heh&! z4!qX_<^Zk(u+XmgvL@hNy(|Q}9&iHyZcLP`^D5rGLwV<&ciwzID|B-0&V2Bq~j_%Ew zyUh8^^RIpF9`~9vuJfgXx24}Rv}yMfk3aBk-PgYLY_4POJk#N zfu_%lVn=0>K>(AUk;K69MDL(H#FM^ld>PXsFK+8R7+C8v?2r?W7r3 zqYM(186s0W!irUhQ_ze3zXrBlBg9tOpCO*3zM!YY=>xzK2w`hF1m7UT^h0StM|!C(1#AFXD7?!u(fZd}O8&561h~LC;P&<#3#S4s;`u=}?^h z9cVlFUyjqC$9$ET&mE_K0eJ&o5ZJ%;`OTOw0{W#mJrCn;kpG=HE!^(S4St_Hpm>@T zw_CXDe07R&*8ribs@cP;r@rn1uW)~Lt9!L~b6r63HZ8071OncG_^M~C$9;bwP~~3Z zZL0Is-|bQAwt83jz*6I>^1A(vUSGMtNfE4wI)9Z?FKViM`0I9io0@!ncfG#~WBfkh zzBm8Y&0eM06RckCQM|?5?+!LV2fcN$FWYY$`O1Y=W(-6RT-?saOQU%E# zMQIXMHR!7gGtp}gMr#fM^x4O1NsPA zo0K04Ik$R!N}$XW*fJ?M4E!6oA$#@OHIoVj+w!%mCuQ5#RL2EV5D>CD>3}A$r&{M{ zO}JgYZFRnCL{}i#fIxF^ZfwMn88U$W2AY;7{ zZ8CeBf{jW*8c|nXH>pzFJazHapOh67=&8!sz1!39S0wS}dW!nGO#!94YSE(HfIs(E zk`0O4=Bg@pfK)!)tMqm^cbBRy?yA}??ix=Wa^G8xe8xI&3Iz0>2Ti_ev0~XW_bs`3 zVp&DS(&AO_{M=i_iqciZAkkc)*WEYgCfZRSZ_UGwmV$pQsj@~&|3}&^>uI=oO+n4{ zcm6BIe#$e(4k(|$9Vbn?Xh$21336|V_fwlUf4BwL#5D1EyiHm77_LyM;y{8wRoolz z*{bbHH)q|ZIg70=!uWtP^90!2c&}-XU76!SbhU>rVx3=4&0%M zp>ZKG&b-@+ah4Ym<4j#n4Ck#R#vP!P7#E0g;;V&NOANg$h;in+w71r%=gm=pvOd&X zSRh1r?}b<_*7k;y7QF+l(Wx*eItUvp*1y^tJ50yKo`&kO#GZz7`VDRhgu}fuG&JZeTgyw-P6RiG)k{#Qty{?ot296}JEDis5>^#Z?T52bZ{JI+ z7XIqUrzz@trp3bnd;1F9fOoXM!K$a!ptrWvyGuXqRnTwj(y6_GPc%*c+bh=w8*+=3AT>VSY-28L{}D@W@7NvC8E=+WQE4M zkKQOcrw2>hmHA?0ufATPu^TX?bBglbgGy+u60Yguja)fv{r0~|@trWXbEYCfB{Wt6yE-k(OYK`l7xa&9?Croj= z|A7)FYum3t86Izc89ED>bevq@=<~25bi|A|59&K<>R9TwDdElKLg6ezSvn zljaAPHuxk&_wDfwnia5{tF%V>GLgm%};J zTac3TVwYlfhH5ffzlrszF@GBRy*Uz#9WD8sI9ie;)SY&dmcwDKeUJlhiXi6{K0e_{q7 zYwK>lvt{Q=#L0K1;6XL=am&uv9NrS$c}QuJ#B zA3o1{=U`q8hBeX@Eb|>;z$vAGv_W`nn_TPvQaJf_}lu~LEz z0ciSmeB6U@RC4A{BT%3LO+#bY+NR^qRHPoa zVezz_As@`t^Qz@UYNE9Qtt}@^33?@+;XH+PgQPD}(hG@k({WQ9mp3QT9(E3@$IL_b zp+VnAj-_C6#%b6Lc~JJvi;!2taX+5Vco@$oWD{JI9X$wRL%nT_VBmln$y7%(mBsnp zBRv-7TE;%z48491IWT%(w|)?f^q4zM!LuVhDZ%$JtS?)Qm~cLIbYW{0=lXd*t%=?( zB`<K}*WHgQv;o(Q#a>XMGoq)GB3(GA5(BOPfCGZLF91g>>tBGMClMN-K@?=9C*59CyE zda{3eqfVURoZ>Z1p(keY`c@?SH`;;K3H3HCpmY13*q(8)Ab6aEtTBcd>amU!K<%!G z+=^VM*glJUMSncrpT2BhYv=M9Mt?dohx$88RlQ)-mGDPF1Fcj>C=?2m)zV(ppZ~v~*N0 z%J*ANn6>nCPwV$rssBQ$fBK*4{}V{ROn-S+>zl|N&fJra+e`4vJZrP$RBTi7StMyz}ONrq5j zDY&ve&;bAozT$$(LuZQ@t*uwNBT_G$B$^Pj{G+cwxC5TdouskgGFe|$4q^iYoAuO zAG4VIou@b~`XD9=JNs}Y3zwbHoSk9kfaW^Ht)lFp=6ptT9T?gR#c({Sqqrolz}ad2 z_6KNDM{!we2C0skgBf9LORh6=f7YC*v5R2}xx?@<;Jy{kX)imC{hdK5_a0zw3JXD6N&J_e1vi`L+NypQtVHSSH}vL`v> zw+Y(S!T#6AX?Xk0(9aTdStt2_FisyN{oMqOlm~x1=*!O+Ja6D~ll(lx`vmu&_<0_+ zW6?vspIiqg;=DNxokhR%jE*8YN`x%@#fa8dF>%&xdygTfOu=ZlUm)yQKg0UL7FHv& zTEyxgtKsx^*Xj826F+t`DcT5w&pdv@nRo{w?{~iUsqtOcAuVpe)W)+rG5Ya-rA#th z4~DR6!J^<@gYxq>FM}5y68wjVPBY44hFOu{gZP*Uk<&b?3-kCwML|MNoQT^-+ zGmfVSCy zWADadzaYcXEK6})%Jj5V52vo$WmeNto|-n@+<&xp=_^_0r5OTSohSt~(|(L6{|fS_ z+%xSJ$Xt<;QZfzrmEw#hiAyrJNxU?}7jO4SydqiOmwL$RnH}HgPti3aqe#FhkpJBo{*RD>whHX=9YFIyaDVF zNSj27#Z1%gy7PIH_)imGBVH+2JaEMWS3GdV16Mq7#RFG7aK!^xJn;YC19+5;QAq={ zj;9j&y%KRTF1*D2zKNLMC1nEhTU!1g&Tn+_1EaqXoxB8R<4-yqJPJWGrXD|StpRWS&oA6OEvpxWfU(@BY2|oRiuN3gu?Jq(*bo5gO9#{R5sY2j7 z?x#+FPvLj>Mj3&E=`bn>FduL$paifEPy^Ts*adh3@GRg3z)`@j0B-|E0O@c>4q!gu zRzL}09biL|J_|l!`$zi%*Cu>@7`zEz_j#K9_JZ86@^Fp2G$8Mj&S&&EzcdjrEGil^+D>D&A() z`;BVP@Gx-zes z_XOHz6k}cw8VtS&cnB?qk?12Ip+1R^|Hp=~pUEFD^c_nRz>vp%hG4v7VZSkNbDWQU zvCq|E0~zAQ9>*=flt;eg|2G3t7DFE2q#JyNcB~H|FG3_dgVCw`^#S$te0gD(?&ncy>IP^MA;8R}d!&MW_)5%^9g z^^=eCj{*#N{AM%?K5S`;LO#OJ0R|uU@eAOqO>pTv2nmFJz_w literal 0 HcmV?d00001 diff --git a/src/java/libs/linux/libjSSC-0.9_x86_64.so b/src/java/libs/linux/libjSSC-0.9_x86_64.so new file mode 100644 index 0000000000000000000000000000000000000000..2639593aa119f278ae74f8f097d3a5cad4a7cfc5 GIT binary patch literal 14488 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 literal 0 HcmV?d00001 diff --git a/src/java/libs/mac_os_x/libjSSC-0.9_ppc.jnilib b/src/java/libs/mac_os_x/libjSSC-0.9_ppc.jnilib new file mode 100644 index 0000000000000000000000000000000000000000..27b869497d4acf50ea8dc1d2ef4b92b4d8e7817e GIT binary patch literal 14400 zcmeHOeQ;CPl|N5%p>~i%7)FWIR{o%4*T8r|beJ^llaOU>uw{+) z3?h-EXNQ=kp{)TgA&DWT?Rqk^{lhzx&eG5{PC}=i3A;-tyIFRVPGPb$OSWv6ZpbW~ zN&WVB?t4#u{s6nPf9yYc!}U4$eBE=;J@?$JCp7&ZpZ*;Ym7*1)<)bYll7B)g=^5Y> z{vA`4(AGU6^kzLCvnOByI(cRzMNwlZ)no(A*DnNh

c&lKBrm+6)SvL}xU4xO;C; zS5ocU`_OzF_d(y`A!0}l*5?N{Tn0uSMTz#rlQWLYw^8vbWP?NkW^I`I0>DO6?d^>3 zGl>h>xCNFIHW=D%WN}4_btvt#rMHFK_ zm4w<0zvlT-4BbZ9U}(3&^I3@ooqf@$5=%v62~|-edl#_L3b8e?!EnJxQQ}>T_>q7j z=HX#z_k;JwTGOZm=u?z!%{yAQHa3S$D%ApUqcL;^@rQk9S?gI2gqh`NS$bFJ-u=6F zH9T0m@ew7Fh;G~;?*vJxtY+Dw@1K$LfBSUlzc+sM_rKmWaTzrB3pQ#e03RfJcn@5@ z4jg-zTG9Anf4SX*#&&p*-{IMG35*A3nCzmEM=W_lwZniOG+vv{Xzag(3J@mD(A$^n z^}=IrNwqCnUA=M3Mx=~6huJzjYc#tKuWEARLpGH;yOn#=1Aw+m#@ddvX!T zMIaY}B}X9ba*Wr%OnLp^ag3*3&hdUNn)>oFqJd-ge%Al2b39$_wEDMqXtJmOxoE0I z*Dq!DIt;xpAArt*d#Cz4o#QMQh6mhO?u|PAdiuxJsq|~rDc=}q>D8%WiCiO<`WVY_ z(;Cnm`Rmh##+a~C8PLd)esXmxe1!6wsC2vcX-seN#7%pgDD(7(sZ8U$sZ8ws6nw0)`{=8raOOw9OKF(T1)e8jxI-h4rJvrrI^_$? z6W&32V*6S0)SaPW?;s8N!Zc*|gXnKUf0NlSN1xCq?)$>J&h|iS0j-5;z~jNQ`gP3d zI`Apbr%YWp`lrCV1l~V*2c;TcSgJ8}9D6{274(aiZw$SIEf}jsj0N8sy@Q2eU$`(l zW3Rvky)^Vjyo05azHsTJVQ-xVdK>6R?Dl-HnQr)8DoocXgL#;C%9$RfQ-TJ)8t8=r zI0+5ex3aRJIm2|sl9PGvm|xt4rwr)WW55EJ0oQ2^*%rgF7|U2c@6(j;BZ;uqO_*O5 z`g!aN=a7$d9e&i#kuUJ!Xa~O>vMwJv3I1NOWYSLOtT8yh(?6;f`J9*(IXzJ~NEN_k z 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 literal 0 HcmV?d00001 diff --git a/src/java/libs/mac_os_x/libjSSC-0.9_ppc64.jnilib b/src/java/libs/mac_os_x/libjSSC-0.9_ppc64.jnilib new file mode 100644 index 0000000000000000000000000000000000000000..96319f6d4d82fb1f272278d095ff1d80bddab7db GIT binary patch literal 14496 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 literal 0 HcmV?d00001 diff --git a/src/java/libs/mac_os_x/libjSSC-0.9_x86.jnilib b/src/java/libs/mac_os_x/libjSSC-0.9_x86.jnilib new file mode 100644 index 0000000000000000000000000000000000000000..025becf362d7b6b24b14849e7f78b887acffd092 GIT binary patch literal 18204 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 literal 0 HcmV?d00001 diff --git a/src/java/libs/windows/jSSC-0.9_x86.dll b/src/java/libs/windows/jSSC-0.9_x86.dll new file mode 100644 index 0000000000000000000000000000000000000000..9f0fcec9ea87efa6f6d069e9f1e0218adc5b2114 GIT binary patch literal 62976 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 gp|M(~N-X z_q^NLMqSw8$agp#1^AyzIUEb{u=CXetl)h=f;CI7&pY%*$6%Nam3Vsdqa5Lqs$<9g})7bU%NMPDia>)qYZR8hq$|PRD}(k#C50 z<#Y`>hkl43g18C)*(o`H6CAY@hFo#Eb~(Zxy#zno@c&Z${{;VY{(O!JjYB5Ogxd~8 zL@jV~5T2*wne&Hqz9GM!GKE1m7V@WKd^%}8%ezvRCvXH@3E0l-nBM0o_YJvPrV}^> zE*%G;l=1tdYaCK@?s+VKJbxZVJaIE|?{vP#A=P_aj*?5g_P<267MWpq9=y!y@HWQV zW=@QL;q*4@o;U&)vAk`n^1SCYbf_9{)Q(e)>Tjb?#}dYPHz-H=91BgUs-)G8U z56F}e({SB^*Jyj566=A~3y(k!^YedzfibTD?b6FG@n75=-R%@Xk>r)CQ81vKvBo5~ zw~^%y4x$imigj12mCJoEJ2fw0m;xAr-cOP}CpI8di49~85|FUINo4G01M;CaWw^e* z%ISzVMy9I?&zpCnDT&@M;nC{afN+9ijsswbd`L#l#lx&YP^)VpQjlWfsYnlkG{^M< zLR7=m*J2MeYQ01Hwna>#>g!Xhqpp#cqDVGRH0mPRM%jAn8HXb|Up0D#zn<1dHM+5muNaV8ji9MpQH}W=3Q}8L9%SJkUi0kNR+rm; zlfZVv*WSPwE)(PUHRg>sDlv0UDrH@iN~I<$M+8tH-ufu9db}v}gN4ye zZ`Xh4oEt1uVz-N+vF_;Rx99Trh3KZ|Cbk9-aJZwd*CPeqiyV1~Y6*G{RP`@fg8ljc zyp-o3!U>gOMHD|^%<5Qm0ZF@yZd61j``_A)M zAitV8ue2oPIS48Ar3(N;D&D39K`*GP0y3rpJpTG!PUO)%iwc0flxJ~=suxKT`+J8# z0Gov;o4=dLe_My7i$I1b^r{Ylx%n|(G$2!7wXg&!Nna{SYkXLe76#E3rIY-NIXp>a zH;$?^&0$NJvUz_bWfK$!4_LCkTh+Ix8X5tJ98untAV(oCQ=UKH%Ar$JoRe@o2M&3u zUvCU2FvLM8Vckl3@&&@RQAi68gK|-vDB6LBtH#LUlt&fCtv9t-NkE%OKq?8SmIQQ8~`$X{8`BKBCyV+fo@8c z0|EvkA-?p}i&Ze_64ekzr#w#~8%9(d>Q)-|l&2bj0=L@9sv+B8sKs{EQl1BIMpZ_F z<(f-gA?8Nao1O;e_SV z3%G4Sf5W@gJcA(UyQpjZxDXjE+M=DP%s-=0nLo}A=@8xg9T@c7ac;dS`qB6C4Rg;f zj0_WUa#zapsz~n>oPWFd%0j3YoictjMQz^M?9?D{-i)p&nR`az&gML=TlDn;l)SUK zKr5&}yD%^Mv0Hl_FV5tj(EsB^;{sIR!THS@C|#{v^*>lwMjCo|raZr%CI|^lsVVgC z)R#s#m)Gwu@hSJW=sTjX=k2U<7bw9Ud@q=nXTQ1UIq^olbK3$`2GOE#yXaErX7%Vc z(i7Mjc$;@aFb?J=6hvrp0e#iXV+8tvkw1uPXZ8cv^t_CBpg!ffbgCFze{R;_w-rF4 z3Q`NCOtH)NpYI>%A1kbuP~A3KEt0Fx+5%u4tudh#5D>gR!YBUBs}L8`m+h}_ZEg;h z_NNm$R`aRe)Rs$@;a$0iEDDsJ@~n*@I&r_KBLq?_9t94}Mhq7XQeuaL!$N_06dC6E zl=xpj$^O@f&fg^Micf&llcC1>r)&B}BZVfzB z%M$c=_16P>Q&3+K)Yk;{9YOuep#HEQ`0H49wRwaJ1OnnklRoSs&`#F?PbT2v1*^>2j^waKl-PHO)nldgc!27p z-jYs_>eA+*8f`6*j1$^{0HaazI*5OO62@35v3Z@5@7K3QTbv=?3RAm)wtXYu^jChP z+}#66GrC9Hi6 z*SZ90fT>y^VE4a9pOXJoL9a^$y)F{;x)4=^QrRa9Nv@(nf{d)QVMU>iq1if4+NX|P zuv@bFm@&Bb6sIHgu8^2YEQr=*y2Ku7Xf1mMxU5HQfDp!6YwBIVOx)&RG>y#`gmGLl zd5-v4FJ)jxg3x-|4#AR((hy862;A*AIcY_hhrNgjW-TKx3G40BT=!q%ztn%3tv^TG z3$$CK?QSLB1IF>MoJqpxW0SsmIWuSrfi(pH)qE)o%|Be9LuJN{UEIH>DDGw}# zY7_9b%`1_wDqbO^mJX;#YS1-eef=AwjZTa!@VW{m_Bf1PA@j|CFQ5jyrclQ6vSmI5 zzNq~HxF?~djPe^}3;agVjfziVsY4A3A_@)TvkEGw`L};TKe-&`r#xRGn;7q`6#sqr zui}5eIz+^;lJO@w5LSi%G@OSf7m1Fv_1YI?yW@Wa4uuLOWkFcqEC72%0H(xZh_*)U z?qEOp&j*3{7xssj2lC*-K~9I1HzEB^wY3SvT0hpi_55*Tp!H72SWpwO((!x(2-rh+ z+`uMBW~#=p8}OD?15LggCs(*EF zr#yk{1>p__U_J;~H;X_^9FTl`A1dB`;=Mw=*NgWmz6(wytPv=(glm6_EDScn3af2` z&|w?oAKK%N?)FdZ4t+kl%-sxAY9NVFZ#%oLZ$l_ikeAxzZ5$O}sci}vqszRFff1tz z&3GqNx6~h9>Qoa2&}c`OsZE>m)qH;my@d<(qOk6Bhjq0`l*qh)83j6ejsBP9LoBUP zyEmt_*~#a!{2K5Cj8Iu%L}<{ALql~<{%Dh3O(?gTNCx0|oAyl*C4OaV(t52sp!;Oq z6C2J)myA>Ob!mUIYWRzl`Hp13rsr5&Bv>^va)}zPDRxFv&i-xDn%ZvB%|Aq!9x4?w zNJDcSI3L=k>fPY~P2A{KjT)9xHCefTY1KI8{-(&wqJUVNc6hpwLQ%-U$%WpXz(Wb_ z35<^B*h=l;jwudFKGe~EaEAdSm_mKU7|kV67)tn@07ljQ#eQYJ&lv-c1xIv*qK$cB z1O^hm)&BbSLS@c8(1f?G>as|c1qZQXVnZNN(55VDJR=dPUmd7is?2!4=o^DLu4(v)9s%JeVDbEm(^4O<`XeytcC7lE7EUyN`dG-}pV!NvL@8ox`^ zP6Xp95VpVT9{@Wr+^ro0Tf9Q8Tpihw=0~y$WVa%$KH)kGNyA2M*(i93VuTuG3*>=p z1IGCB83V(0uY+>WN%RVVa6?TW<%jb_B_vYC-|Fm#^rQ6A?x21Fs5qpOu;3h1ivxzQ zB;fSddK)W84^r;Fk_1t=)E)vIM}QJL6S1uhmW5FusL>jVAd~~z_7x@P1El2RRhKET zS_EftycS>HHfshRY596SovLq-Qxn5}k68v#@mh>njI%cF*ysrV)N<7r^K(;`qqZn5a1giHk5A2?;rU)Y(O?!p0`6Jz!Wr2Il+zEOj$8~N~^K0BK_;5~fCm&RuTUql<@;Pl= z@@Zr8vJhEhbIUg+oqRUvXLFU6vqR9yP`0uMm+uK+<1gWU<-ZPUzjqq)#!a zZ$47j6es^E(9;p#EIm;48l%CA^Qtb>N~^A4f`U?!<0V*j-$&^n&d?K?G31s$rZ~A? z%m*Yw#pWCgI1HrX`r(&FniDrVlN&Pp3jG0UOHUCXH?V?Q>nqOF`r0m{+E4({^;Tbq!+-a9e`dH&UM-*peWIr(Zy=Csn~oC3g)+~nV`u$0v?P2<8A*4QH8c< zJtXEmNDYs$w-HQk=47@Xu39&|9O}R|YUTFZF4|dBQ~+R4RP`yvTEF^JOTk@WB=R$Z zAAhvHaOOZYQJsH`)3K05jU=$88WXk)*?z()T94+__7YR&cEf_j$N{2%Qb&;Efs z7D}J3iuuSNRQlPW^@Knk&Z=?Rz^cjGfU1$&k%`*X^}ElS>hf+)p3nfsdNrz1=C`SJ zeII0NQ=ic@C>)&ndid%Vp*rw0C~J|UROv|r*JWkUrF`^&LqM=5&Ym=|fx;~;wS`qG z45~7+>t#kw#fX}!8H<7Ba5%Kp_?}Zv)*Nyh_t$RwZ$k z7$;+peTB$A==F4V>(_{q^IK4t^pWW2_2wp!H6$Mt>qSS3j#+Q^-bvicqLY86KR4lb zdNHd@ea0^oi(*!i7@Ze2&!_@c#}q4b+BiNE!>$#GY7_z_F|<7O8S_&;v+KJtTi;Q3 zeSiFKwmwVngPiofZ2A&A{dKwNX^Q8TUzAOMxShT_H~ngYnN$8IX^ye|oc`(%1d|hl`PxH?PTOfKbtpoR#}y-EHfTS`DIoM z%CPAv2Orx64DeE55*Lt|jbbu+JMnXvji0Sw6U^nw1!?_5vM%MWWw0$&tdHOb2fd(F zh+Zd#Df-^}6a{6*W0oH3QJ+nJrFQuVS-z;>Dq%}Of22OJhYOUL6I?Lulrg>NgACGx zO6)y=;N5B+464ZGkEjn0v=S1rBzp_h`USrfGi7YA`Crx>Z-i4;iLJN0q>1QYSV5^0 zTZX_;<@fM~R}4_%&8R92PfZC|R~&1@zGjEOjrA~+X7QaE4frugKu1*Q?m*6{SzZNQ zycb@9s64_278KG~5N#rIYSKS&HM$k-rKf}pKq|4*2pRaWcPlu(5-;bc;O}U|m}0XC z00SV>+A%gv)iz8nB(1+W4*+lhmQhpY-8!OXkdhe5HrA~sw@3^a%Jj<@|#+(YT z`JWXIkQE--vBKp2X>bSs&)lCgHsP9r8QL6htuY|ObOBo+ocG_}Cu9K!?Nz`*~gr_@ZwbI#}Ho4%1D%vC% zSI})aIV3WS&p%)9l7&~;?8VsgrML#8e8d`5PQ;85r9nr^5i1B_7NZ9HL<|)Tm zO|ex?y0>|TwSynT_2;Y{Do2@Bmt_fLcwh=iEZh_owu~JD3T6n#}RAP z&Z!D&Bh>`pN)AoS3sCZ+^9^Zzp7|O`@=_CA z09dnYX=q~D^~0UJVVrkoM|TrVsG({?6`GPVUv6kf%?Atw6iAPay;*8b) zSEk|xgK{>V=+LVV0V(?z(3R+IgT58e8wak|o0_a65(UmR?atNE1y%Sm!im1HhI_!q zD-2e=5h+vVhn&8ORZ0v~My(#s1tk_iC-{^l>slRyMI9=<3FYwJiB$tx`Hmt*c}n68_FC5E(xFTx#y)Lz z3_?+16hcsi&!fSwueIi5(4^@z#~uRRl0M*1fIcsbNZ8Ocp|44(f3Dxvf9gpI^;Ik- zQfALXu~lJhig#z#SS40TM$x(D3wi-6+Q}0HooMexD+Wc{NWL&>@=q^{Hg<;zlWffD z54s?}GTRukO=v3!xo+EUv{u){@Z?}0LUGTMF?)D3@qyYtfILKy68u)IA;id5>Ag`5 zFP9qL2GFB>`f1auYLu7)$g2WcPq2p($j$zTndYqWDzPgVb)phGm(OysIy3S`9+NB%YLnD@dO#RrQfaP^a8LtiOG(f}rbO}44s*|+qtGuG!N_-=W@6dK7u|ibY znMfs?pJFV@n$Wj)>z;#$HK2yqpn*87e<1EPC<6}buY6-z@^>O2d5_)xYtepE4=PW$ z{}xICyU*i{6Gli-iBA^^>28q9*+*YWL`^_Ahs{obs&kpDQ&d$T1`e-2<3pyv3CGLrdP zF<@+a_f}PkC|Vpg9CLX=BZ2raU+z|`L!U6DN1Y;>Z;#;g?}+3Q%dZ4 zyK2rzOX}7RH!ux+s`e|aEzk}U<(qpTgE^tR1*!be?*Zg4Z74Vr=z==#K*^B8iC#1^ zq%VJD#+QE@2q&mtUKG-ArvEm*EW#|boAwY2g?wM?OdE@W-w5Xq?6PL2vWMMu1QM-N z=9l|o80`lGwTW?muHW5%YH4B|yh(*q4~5_iX#Mb8qZQ#dto6X}SS3#RO9V!#b4rrf0$glMS`v zEhML%@OGXOgTI_%Fw7g}+o*^O;6?_ktz>^R)rXPyfM|{uu+=kkNf1BQYe1;fH`3#a zO`tzUH24Roppq@kI4zfN3xAh&7D~;~zfITc&1N*p`U6WqbI$kzA6%}`04V-Rg*Lub z)~CcS0WeH+6vmzK*;rF#y{dkKwwi`7cy5{>hEPx7)S+*a9!(z= zT6pE_ccTwJMtSwS&s1U`;KiiBMUpJGnt)u@INV#X+14YHW+Mrz3jMVbPgQR>ilVy< zlo;(jCAtn_l3Of-s)yA8WCoN_0M~Z!3vaDZmHh>b74UH-i~DSSr)!#S+FwaaQ3^Ln^NL*Em}e?0H0zZw5@BM(9{1( zy}W)mL=nb&Y&_VM^4s4C-sO+nBQZ1{^a2h9k+O(BX`$(4+Ly!kVqmcsp`z#)HL}o? zM4@xeBqRx!_rSE@8D414vkU(%Ae1ys;<*+zEkXY{)J64t_@K;H44K+Pq_EB=-PS?d z`meh7Q#JlB7WymkQxF*asGGKt6Lg@4$g1R;MYJioZA~%aKgB=1$O{-5SnK zSv?W6Xi_G_2Jk_Ga(^bn1=$R6R6DE>@GXi$@&B+>Pt2tLgB@~SCZvBhKQ|v*zsGlG zJVz}T`dN%mGXodbiCI&SENb)&j!;0a^_BBE+PKlH2V!da-3!tAOm=- z3-khRy9i2$!+Js%XTAkv-3GTW!|z6T{qAu}Oj=-*mH5FdjZT2gWYJjucXtm_*%T0L zKT_CtEDF;yn8K&RrNqBLKw?tLi^1QG!~T7ISHZ<;paR$}+_-jd2H&K50t?DNpni9$ zR_Ko|bNVZ{-1Zrk69>866!RJX0Y|9&20Aua3e9FwPpls01_pxgQg9lfZ0v7{Z@v9a z(VX^0r?5G%3EgJB`5CNyyP0r*IIO=T97~x2Gla_>2m$cgRMd~wB;QJ8aECk(jL(9EDn=&slY$61xj< z($gHn)BJ&&16$Xky%McBT8TY_Q2JV48-^Foi(aq9W-tf%{!@5*x5m(et0O&=hOjT; z#yJJcNStA?J1hu6y2uvkn>KISNKDe*ES7|tYc`;+w4xf+4ufb70HKnL(0`tdjC?Gy z0=ao4nH`iY-^(XUE}LiFh@cF=1iv%XLuon%5sa^aHMQ1oyr!3Q>?0qAehqvIL<;*I zvJuFobi|||H!|6C!eJ%$9N1bWuU z2(>ik7+q{05Alh4;wGEs7pCWk2gy0&jDu49Gdz-*5pn22ck;f>_?7w|rfvas$yaHp zmDu$}yWmKeuDYBbNN@t&2RV9v@>*$+V;NJ7i$JPwRN{Nsk3xq8Y`?{aJ#77`Iavk& zhB4L+aSX@Q={>Q7{;VR^I5BGFfh&upx?ew`#1BJJtnKp6nc6tE5R`SZaqhG;{8-nn zC)_`_OU?QlxPuq?b#lqSjq+G`xL46Q3wL?(k2G+U~Mi>{}%BE?PM(GxS zSmd=KSoB^70xo>C`Vs|^ zskM0>%7JDk{nRl^4dJKOs*pb8<6ewZf>U(E#t87;`r*L{Hnzh+7Lz8>D_LglewAev z^f9FU#rEEk^a@3N4Fph``KKFzBi6PI{tY8aN0~PjPFz!vJeu2S*=j6=_07GuPl2k7 zXv|&u=0077gj1{3>J)IH%pY`WWX&SJwnWLjKnc+jJh|oWKCVBCMt%5oim6g>8{H#5 zy1vWctijHB|0P1+&Dz4Gm_$(ZM@l(rRQ;LKZQ=>u`(o)P@w%A%wGs7hSbw~<9j{@e z;u5bgk`Vl`zPOaoX$KkiRNx6;d|x97M(Uq>ach(z_=ok1s$th+*hL$5(T1!wY}n=F zTa=Gg$m zI#s~uVzFO1x+h?pw}9hkSuWq+&^9xGyA< z3UPs`P@}c2x9B`hwsN z=R}!tUO|N!Iu4|@#~JxFjf%ur_+Q$IrR%}Ttp3QFMsQ--@!hy?y?QVng21sv$R_X> zXJGCX83I=;_XOttl>jO+v0h6MIn=acB5!qp$meBie%>W&egO|r^YM}&|7h(`{t7ty z*|kId42SaYufy24>&-!!^GzeV1PjD1q0Sq@qG*LziEjokB!NbUL(SNA6q90dL$LNA z3UFY9Y)&^OIppLc?Q6`~L81;7h4|d{6{jlkWBD=b6%HH0nPNQ!t&1uo z90NJD1`5U|`I+@KO$6xM_F(BO*8h>=9~sd#j0JUj)*N8GY}$%%Vl z@t1F|mJiV;ESbgaE#)-D*ABFYL%`tFk7@&;Sg2WxE zJ$v?i+jivY0rmLPEPZLy7e!Cj*MqeLma2*K^He3U+tH{-Kgfc#zl`D?b%5QCPtjr$B?1Epwz#23(@@6OGus@mncXl%t+YX0mcljag>6~#X(Qy{g z!O>G@qXRuI#}ur|v2Jq&mLGJID`4juXpg_>33p>R0kD?@%YK|;5e4>vpI5_{yzY3Le2li zqf7MvBv)Yk;@T(6+UKTgA8gC}EPd@y8V(}5e{uaqvi<>Gum2f1cYb;O|JWn2|12H? z`#-(?y7GU4|GT~y^*7+r#rmJ&vXEbl|6*Bx|E||R=>Met$G#Ko{}&!zY`^t?g8vD! z{;RuQ{~)eS`NjC(zFV|Eg+~|L{|r}W{o?xP$olW;di{fbLH)FN;%+doaNU*A9j-i-j&mchxYmh82t|`W&F;z?~!}^K9EHZEXqLDQX_fBd@sQ zN14-n2#dU37I_DX#3m*zrol_t5LS@b2rsM|{k8&_Z3RBo0L!TWmRh(#9hK;X{qSLF zGNEXcI`vs@kcaK~Tz?2o)Dow!J#3s)z>0&i3?mJY7-%x=zY!g@1|>EKS=6S_T@?N$ zLQ^-+tExy_0coY9#60Uz79IJA^-tgc^wn99j&*AX1&rZA9%~o9rKp<{`vfy-gzzpn zeCh=uRVgt)*GlfI@suGgp}_GkLSe~hJXLI`8OjuKsuFvI!g=2!UV_fbB2Kl7kXq2G zVO>z4H5Emu#tS4gY|l|*7m9BzD}uIaPpchnT?d3Tz!tvy4oqV)SZ;??V)dvKCW`j4 zb(F}LRxmE!iFAqTU*jQI%yG1Dvg2ug@;%H#eldU9`d?9bJ04x+Z*S5W{)_83WaIBl zH~y!~cMB7IzqEb-L9q zXT?)k!K&|W2pE&$aoQQ_ry6Iujl#rDR&-C^wEmSVu!%)LcMx2Kk@ZRNX_-+vz~Cbn zo~|567&wRE^wLpATSYj+%sa85G{BjYAhte@ryCV~S0uGQ5$laD1qV z!H0>cx`JfKPRt5bwerg}5yd`X^`aA%`KLx!b!tL>v_F3WP>Wei;W`+^atNCeyGOL> z1$t%FS$lp!iz#i7O3)$QOWNb}+BF(H5!`!GH?6Ob*0i*k;`sZ{kKl-`+VrM=MvU!l* z6rI8C4)x?cU8Xpjk{hu`A%N*WpCIC3{1cmwm3Tia9uigc#*0l=n1PUVrNr(%h%AaU zg^2{4plogh7g+p#lxr(1DtxQqL{JF#=Z}OFWHI8hD7*}gdZ6$sJOtA(YxoKBW+RIb zr1nd^t8Q6!|8&*+^2`0nyNmx%>VNDj(e$VB=%T#a$ORt1xcw`?6!mY!ql@)l{C`rv zChM>7di@)*MCj+XAM-clRh7Ht0&$A6C=yvsBfVuiz=kj@~Pqt0^ z20L>V@6R`gLk9M72F}5fJt{XdIw&_YtiNHMh&^8}@lnc!Bm3r`3Es{813Tm&XkEEi ze)O-N6a+pS4-&Z9hV@B5tA7^~l%F}?ppOKLa=J5rONZ{f^xU7>ol5M_*;y*POZS#P zo%g3Qn~U~x`?O2`-o424Bw{y9o_CM_+3k6o)9W2b^ryDx5#|*A^E3U|s&R{3nXg2x z%dx9w+VQe1EE~qMKGK>nCNok_lC&_iZgGd}eqAIy5}$IVMF;5jl_$CVA^Z_yI$W9k zB=ZXR;eJ1*tFwi|a5i4?K}#~f!b*}#@v6kfu+&PS{3!7=_!0dQi-SdgbuzwlRWvN% zgB>hp-#aKgVr>YHt`UoerO8noHYQ-*CYF6~M{OP3Jdpc~GV_!Bvo{tpU%J1#Y;PD5 z7(Uk(VTa@p=$}gT8k81JKuh%t{sHaNSD(5}Q>g{o(8myN`Sh~e)hC9n`IKCDz?n><9myXuYrW zgJ3@!E@1CUu1k3C->Fn=r4pZqrBRc$e9ITIy6bEC&coge1%tM(@7oHxg zUgSB~ZA?BC>lc(eeha}A-P28(eIh6`x~IDmpCP2?*dk*%7roxV_**#JP4O6QABN*V zMFQB3SV>u~#J+|&#qPRzkiiMh1audbk?tEY!+8*D<9e44bg|vFU!G`H(T|We5xy1) z`xFe$nh1k!nQi$|n>bX-`XVdD(p-1b|ES?1Snw@@lL`RjjvSVSlY7N_Avk3@todgz z9kN!_CmZFIdA#v_wzC^ z&IRMv*BiRVHqXV(S1z;2fw{x@O|#jSF0$o!f${p8dDz8s1(pVYU*!v4c0NA94f!=x-p@?R=>+N%`MWNl57)OOI^yGGtdnp?0lSim8fq2 z$Uf`TUcFHZJuWk4k2M*tgF)r@{HJTpJp>+)uApZg90FZQKf8%~ayi;dqLYrAZ+sUMD&C0qEByRu{khN?ZL zBwH=ZFz5_t4i;H2B41aUn)ks^OK(dG>kHv8uHBAW!PH`%WZ%E zz1x2++kb8D%9@9NG*5(iBDs04lyyH~cgq>9do;pq&rZ(36Iksq`k>!WE4oj=noC#rr zE_mfb*)6AG@u$W0s47moeJ&@@W60Bq(`#?N0+@L!oASqy_z4zc?2DO>sBW`9?=)4X zz4G6mp0o#8&;II$ej*o=dfTX|)PVH&P1-2{PXhfvF=+rv?SlfXzUWNhJhbAJtJG4L zVGi6DjdK>@9UCO#&v9NnJ2v$9FtO=G$4QBsR!*UWN}Uzve_8`Ot7A@Vlx5$_e3GjS z<_Ll8&Ex@v6j?`uu>NOzI+38~Kx%X~Pt%W$yiV$=#@f`b*7L&8fQ@-DL~ht9hsdF8 zF+`5V;yt@=Tb~!?0K;~_ip4nS19CmGJl_p^wK(??YmUXf56lm!`g_?e9|q2{$&a=l zGZpJ|d4Ty(*lKVjK!g3@dH&zvyWBo~2i6A{aeeSC;S-0812NFD?-Gxz3zB==U)kl? zLcf*vW{k)1rtQrk`*GFpN57>$n67*sF_;+qJ|5x3(too*`!73Hu%1M2+q?VI zuRB^(8T*FypYOI73*^^>=t1>qcxoj)=XM#M&@Y20UH=Cg1U}Z|(KURC?XEu`AF_Ut zUrr$Qw|#6S9eiXZz+91a#}9DqI}0M1x7sJ)O?JNHI^;VpIjEDgpTg%!7IRF8w8yZ2 zer#_HPR~iB?AZ?l%AUkSplks0{}}yff6UTOn;51YM{Z`&?zMa1AL;e!>B@)7%7>*Z z|H<^*ADk)+Lzkwya>viB>qX-m@#q@8@O-zQU4Q+;B9MC-uPWS!03JmQJ9S^ev0dgK z3G#sgEISDpMHOVJXU1oPO~_1u|j={y1Djg_-QnFf;#0>Ze9t@~>fRsewa4^kAN_ zG-PJ%BTaAW5qC?diPN#PVH5_&>6|(QQ!uB%AhjJK$+y#!_c^PuC{oXLn_J!xt0e5L zAlgKmJZ*Xt`{hX=L$V$*2xRHvpnnr*HH%$8r$u^Oe?t4g&#rSbL@rdqSrojQ#GH*< z(q5U<$PVNnfGmE$jl+5HTJgZlbb3FOJt3W0kd~d0)@DoKS;%NX{D6Zn2Iyj9?(^PM}L<7M9e)LzpjNTW3atSIXb=4~1w z{asw(g!@8ykA^tS4rjzPYQutD%B7ny&?9}U#h^y4$1|#m1z|*9@H1>QPn~-xMGOjJaR(Kv68Z<^@r4?MG z591@OuMig?sf%z{!qKyVvJia~jp$_bvg(dj9y{%1wXP6%yM)$$6pF9G-Z4atiZ^LT zDD%Zu#i}yRU3HuiUn8;z%CoprQ*QUu3Io_5h^0~5bE@$)7iNiO;{Ft|nhNPaDmXtG zE`!ciK@Ao(+NYpQ_8zvL^xK=d|FyhR>c+(|C|MG=R4jJe6oC5V$xP-$LJkV3()&}> z`)AXQD^X&{3PG_ifn9CGR$@aWi!YI6Ek`gm9}2YwR;I|$wo5F)noKvTBPQ|*lN3w2 zC1Kqd7C-H#`qPP?Bq%=4YaV_Iou=uIr^Thm>rcs-)_?x?6YEK*4*-E(DgSkpuSSPs z<=n&Ixz;lL{G{=hZr4>J{}wg({C08Rkxw;n0N5MR;RV_e<}J-c^u<`+mpybH`b_$M zSz}fiDacqzn&kytWoqjzzVnYO{~$*Bquc4A!<3;!dyO$yW|1yfhNgAWpI9CuIFZ+q*=cX6Oe`Mu(`n-RP)cv3Tf$g%|u|IApTSqjJw6JToZW0#R zPQXmmR_?bKh8q`sz~z(HFFDK8`Gk9VzdiNKq5ILLI(PRxE%vwms6D^;c`{gY&pdYf z4J-^$LwiS(8{?Un9s@o)jFPX5+7Z$*Ukhvjg>b77ePj<`X%p&^((i5bA^%-V; zrvWP_W_+}>lkGQP@^G^>gRqk!{?yOiE;k~51u)2H4r%vHA{W_j2-T9B{mf8^;lt-*RM3noN!Re8;DqTry` z;O`LT(8oU08srGaNiL75ziSP0;s9V3_|(=Qg*uL&ZsY?&%Zua5LbN9mgvNaD5%wWrS&9?7M}T1y5-}Mo zJllysmS7~e#`!DYZjE!OB8{UZUd}hQHLltrB#+j3t^L&+pJ2a*^}ki%HLU-`hevCC zyd7>Xe3i97z!Ig!9%q7E);VCO8h8^RiKIaHB8qo6`pIR0@p647*fOa+s&4CTS-TVSMQ5;UEGjGRha`ewyW86sihl0|Wkk(9y}LX}IIR!isw*Xa5ekrZ zDK+r;6uMq|EMTeB7IEFvN^QHj7V{D~J`FcaZrYRl+`j#;p}{r?pUWOpzqO`ul<6+4703;eC8klJe zUf6!vlencRU+W{}s7M0j#?ZZ}=y=89$cFQLcjSmxe|?3+k*D=&b$!kkB*gh5+CexR zNjr@Nh#WYz2J-$Sy@GrWD!0Wwllwn)Nz?66NeT2j?SUCn*ae3t&MTQDyR_to|($ZUe;U z;Qj%Q#1gG-+ARP((jbj>X8Job)eC+fFhq6mTClpNQH7iLsj9%P>L%o}$lBl$~8EBQqPYN!R{SorPaD93inkUXV z(hdXPGFaNoRzY zhL(W}Mxt3bjjRCPCh|w`24tCrEXnQyzj||`#z`Mu)jCIH2QD5=^e#lxaUt;GfGf2Q zjeyLV{SIn96zW3}nzAUt^%> zzu$6zo`675KMO0tYx4K|oY&|!JbU1Ggj8~&P44DaR}!Bh4?e9As3v9p&_9#Z1tX|pFj$<(s>J@vK1c*N5g!$J2gimMJ=Wwg`~mRg zL=XZc0Cgncnu~(@jtiu376rSmN5M=uY%8K#T{Z7H9EAmr##UD~-t68z9j_QDBmc?F z!A88+%GVVs&xI@|(fiLAlP==;NO^AmFN81)5#$y^m^rtEB?K|PTt5(mgm}Fduhw)R zRL}q$Ty8k4?}2l3+UeB^JSJcB3PJF;No8y)OM&tOiGcY8#6aB>>WNk;5~SLMegQR# zA!QNXn6)%S`Ia6Cu#eR$QLNNYk!E%|*lI4IKp9Z8fqG>)pl3G3wv=+&4a zYmB@JKzbs=tSrd^(Ma7)lpXw>$Cc*gbIR~G2M%CB|nW?-PHMwh$w zGu`_61(+y9{C6c9{3q+pU922%weT8UMZjPk2Sf{g3q1(eqbGWQ0$!K$9QC!WOk0H_ zfzOfER}H7(kYqeUlCdW2)HE4?hmIr}D>2URpg!#sNxrKEe*vfDsgVsf{X!K0J=Wvb zreU<+reQ3~cbyDqP=peD3&G5ac3gC6YSYAqAE)QWG(BIPfe8AZfaq_Mj-l5ts}N8D zB1y{^N?MMkX?bua@6$3bE+xy*afkf4SY%fYeMx#=VAHb~d2%b8*nlOH+mSq@9||*D z68XYIshA14lo;=k5On?q#;c(7MakwE{z z?}LK&vR=3JC;*il-Wjo@4Jt@H05m3g-;B&D&k5V`VH|cpOM>Lb=^EAH?F<@r9bz)= zGkT`p1LddIq38qXk1WKwtKgUxwG?@?GfsAQ-6TpQK#AV{StJY)XxD@binWjR+vmyS z{#V1bj`V>6T*9GTrs5K5$kB3r72yZ}fV33%vnb_Rf@o~;zPF=(Fh4*h~|jmVk{0tj4o1jxb2huMd2GE`7n-=GWibA+Jm%$`u8P5lN)V&w2W5pnUg&* zsUt2k35JuF8fDE5<_3Hlt_U(IpwWjHCVh+`PJl&5U_RaAs z{1bq{DImbayg*_`pAXl$1_($zo5w?k!-COS!v#R^O`LTL0eTzwJe^2A3rKC$8iCZ6 zKx&U->=@~W{slgF`i?EZxP~F|7d2`*qf2=)+vHCOCEu=XN*+NUl<+UPTb;lb`80eu#8)ec*wN}Q$OLEg z+4IHi_4+!Y*I`;O38!PDb~t1xI8h1i^NGHm7dE;-?@X{xeGqlO*Jqa zdG>z1Z6Rc`Lqo{v<>f2U#1ZEgEB7}yeVhj*J}Zq?dd_ekm3dUZTziV}VN4BsS!m9S zgnbOatrz=1pdR59{f+n|?Dzsy!-iS+=fsC`9tazp=$)Dmr8bl1Mx;UcAa6m#hi!Ws z-$GXcgFhdf{XGWYMwHM6BBhn;-MnpjlUX3i@cu@)0C3?vI50*5#{-#f%5%XN z82ul@ziraD3GE6xvyRe|FPXm)0q_yJ>KX96j{KDE1-w^6f&tzO32&y&jR$pvSIvP} zUc&%SPO=u5LKopKpADE#GF%6XMl@dq z3(fGZOszBefWP4Gbk|#}h>=FE+Q1;3{@7lHvArWQ&3h7QEwUA$FY=rz zt{2f=MWx&U8Ai+Nx%%|n&MtoXVBYt1`s{)4Qh|V>u za1UrkPLj?(Wz*g0*SSatgt+c!2F7e;Oc91t1ff&Yp|H&z2HJdB2^y1HtG7a7^lZVq zzKhzV=Xty${TZU#A)=toELh^~4a5SqH95m^FNvJ#FBG!tbpeVtP4Z`Au_c2q_}BL* z*HfE0zHbG7tmBCop&y%n;4&)R^&$$jdJvy?%7DKzpbrBskO6lxpqK$=41jD9uB~eb zjgEVEHAeN8X~!{b9ReE6Lu5#pA;Lx2kzZ0Sm7q~WS&Y$NU}i2bBZt-WIz;tn6wq@; zz!01f>-Akfz`-oi-X^g40VywzPD8SzC^578tKnKH(`ukMUIsuJY`CU>f&%!F+GI`w zh{*?Sd>Fd~J|0J=dK)1>uVwrc#P3UQpnoT)kBz|U zB2+0G4f@BJ;!9yWjl044(P8AL@5hIXjf!N*)62+O@c` z;zA@$1G9H})Q6+=6?On# zG3O~vFD<|;DBx6SM@xiC1lNO5h}MUVd})+YtD;uaO&dY2=t_AMns8bRmtJJq0&;e= z)knog(hpj3frLi8PBki$cgtL@HAP6u@eUnWK)|cW%uBH^6XGZG3MZOw0vSS|TjpDk zkEBAETJXUKy|f0m-=excqvT(cFceLr>!Y z<8p*k90CJkz6jg8(o;s50|zd}tWlWv+>!xO3Mg|QV+#zI&b(xWkK#LEl;B`3Z92lQ zPKPg$;VDm_kAVE;0nCVbHu2TMg&HZ(-|&hXtcutJC8~aFF;K$l@V>)bj1~hU@_Hrm zLkM9)2!03(=`d#{>C13g%LP2z$v7-9`Gomntktq4wE8I({yWT+}F|A}iB?Fon zP|5*)feiQy0+Nf(Yvk7>j6Ma??3wr4GyV$DyNb5P5#0&?h!t=vTXZ~!H>ACQX&KYR z7>bSy5jr9rD$;{yKl?CEvoCJS(Cp24Rin4!C_D5|Nu_KI^ z9>uf1^~*$k*c%`+8qklCS6h~wcPx-Z!du4XwFBpA`Yiv`$Rz%5MJ>SJ{U}td zmotAO1ELHVLj3iY0l!8-@-Fju`E_wR{l5HA?F4brn{mzf(dL&F-g|YrSZkQ&KZZ-p zucYbEsEDVg--1?u-E3p|55R&^4y_nd!>+f|p$`h&U=~}@oEU$VKOh|hUIZU&ztz83g94~0tlJ5C1F zFyK7mZ#~cB&|OC{-~tAiGN3;LrnBU|_&Ki9!BV+Ktq(0maDA!AZj$3c3YZMOpx?b~ ztKzhj4A&b=Z9J_7Mr`GE?%yTl{4*W*n1~b7b|&)tHXTk?+Q59x@P7jr&HDC~#|>1# zJmP|hw-CsCxjC~T@QgcC8lvC1pm@U}SR%A|KE>Ptm#FmL5dGMddJipG|4Keemi8c2 z-S?^L+WtHAx2&BSrz828xd_-1n3VY6fDBqMtPrvHx(xW30f#bxXA2|6QUqXp z8}e&0qrHgE%zVigu@<7SJC=8~U*Nwmq;FQM9bny)`-f(I!Ix09heI9AHyJVQ^U%Ph z3=He5L(wmA)yq4hv1UY;;J;|!0{op7z(C~mk7>^OufZpR^ilYXLQEyBjCe&i!^j(1 zfV*t9VYKNYhr-Ca5-KSMYYZ5)t-XApbf=KY^kIWUDi**fhe&Cri#F9JjWv2{lNr!FL8yY3<&iMMAi^>xDBKk&+GC@H+?_#(GxPzlo!we8+>SrPRcm0vE z7=CO*2l%CbQ8+1Vq@ov{xK#oeJ%X7Rfetarxdku*E4@&6DupEZzWGj!rw*v>%%3RI zd)u}t&mZ{hYC_Ot9+C0iqWz+Hln4DU-Zt}KvA7dm-E_=bBc0xoc3EK4cuKoq#1uEqw>3r<*{JwP zK&4%tL50L&M^unsWD;NnV@(0{(u_|Nbk{twL7(DoML0oh+zXR}e`MqLiLGbD(x!3B zb1J~5>&UM0`95%<^cykKEg*|@W9{XQvh8+_`beo70~;Oa-xys0XuojA+w{=|ks&q; zGrg4UqawSHPD0fk`Y6k^#s&1ofW9g)a0MWxuca*7e%kHMH{tb!DVXw%hh+-4zVl7B zZs$TD8U!>rzZT^tdx`PU?1T?gE3O~mkL-ZzhiSUCpCPeYxDDYE%+m2fvW$P3{(`iR z=X_3-(|{|}#}~k8&YVGdhr{Qv!)ware(Pwd(J{4-Xd$W%Im{83j6Da=HDdCeFcrtnRI6y006oT`{rs%wE@>*mC9pgUxTzkD)~1 z)S}QU>9nCmnYV?Rw*~1pH4~<0(`D)j>*K+%YjFpc3h;d;s(xb`?^)GVsKvDv-j!j! zJXD7ZxmyalV>-XRrQj3{m{pkW!P{WyvZBkx{JK}5Sp%ABbgV^^iRdtS41HasINFR# zwwur($gmc$AJY1o^4FFr-PO9aEd{^w1NS(oDUuY0oGc5`&QxAr5yZM!9NwnyHhV#r zS1Tc8o3%5kYdnskxFy`veVzT%kJC|!D_3yQ;-#1I9yx9~UD^C*a(;)BmeR?rHz%9C zZNxsx;$#TU-7Rrw|G;NzY)SHO4XFXwqu_=HR3rY#@8npdq9Og!x!uYCtzTeialZmU}AZw)pI4KUdk$>+R=*cDiTn@Bi4}^X=zM`nf*+cvjpo(L_gT!r`pdy+2P07-)X$G*x$45=M(mGqMd$7I^O<1!+xgY zyV>7+#53D}|5+p9SS6l9UW0V4d>of5^#kbAZI}*X;Muq`Rp=!4>R3tN0mC!4l3w|? zh+IJh&;GJG94pB328^dHJBIx({QsUo9E81h!9kJZ|0nm8@$Vil;#V;qx}+@XQO0vE z?iP2@+vZ(K`G&Q4ZvTSYMUqD6s3TW|)^3V6bstr@G#Fbs{iEov)28+DM?dmuy+f^y z)Ef|w$gqA8p=)l)uxP51!&HOQUXk9(!!r69+!4kFC2F*()_jzf31%IVcFD{`@XXhO_$_GA`j`izjEwx_ z;qP0+*hIk&3Vr*2INw*ydEYSBgq4}kLVnO#C_|l`1NUJ?alr$2(&Sq77rqZdwq&c# zKcUPdU&+D$SahHM7A&6k#*N`RVhAU;++~izu}GLjQ0AY)ZP`97$y8$GYCdKDPOO;( z`@0S@%oDBrW?6>bkxj4uzplKIoe)} z{>7U2_Hr#`ZiHKv{SV!xml%;{AP4B%Sos>+oZOnBCzUt0E-SONq)eawzJIT8#uJV) zn3Q-FK$JvxIj2=Dv^8xvzHk?q7AYvvxHdcGPICH3x%{h*uaaXRR>3 zpcl4hQvI%4+9oRP6uhmazhyk<+$ZR(l-O73I%WPYY=37TU}I3PsXfde+6nIeYwv5o zqpGg8_mBjGfQi;vv}%tMG$20#DFy^OfdnU#Kp?2GqREgsA%l~dapp`2iZ+;FGmY`f zRa#Mddj}Ho3%6;#UyEM)8L$e}zkq*e{YkxAPYl#V)kUVH7e*WP>WpK}h%gAL}Je+s9`^!}rQhSa>;9()Ptu$ET)p}#ho>PX3X zJJ0qe3}!<@LBn|kVVn}?-Yk})C`&Ak71QEaDWt1-x*+8#VlZmp_td}PsCyRw<|(zW zUt)a)pR!5M!s9mi^aaF$M+(Y_IB}o}Duq0`JoChXO-B1l`5k8_cj$5a5gij6Jq5z5 zdSh0VH)dbm;pz|ibEyx){5KCXXjzi}r3jNoa07P7i@%R@Z#o)*V#u;e5fr|X?&S;M z=y<3j+>%}+2`U_ob?Fg__(6TCysy@$;q`5F;1X5AqFeYo;Ph?ik;!uHVkH81s2LbA zZn9|72^UMdFl8)1w68z^9n4`6fu_#%wMc0k-_-BD03y|H#FSGzkJC~N*^7Q0wghHq z8TmMnico#Y`83ARkL){oNS`!b20+?__Yxya6T|pwFh-0HBaYKICUuOA7tdvIX841> zCLJIGaZU$3^hdDT7ckKl;_Aud2l`# z-G^)>&yO=@Cgh1LA7A4dmPd?-o6}LI#_`MQ&EsVgjYp@5_amP4(>_w3xmd`H$us{% zc{YqJU;h};$K)Y?TSxNi#|$^d@90<)u_&KAjvt+li}53SuecZtHXZHdR$G@|L$EPv z;Uoh2)MK#h@zw=~wJ8?-b+fp!(_+Yzoy-wITp3(uuy!R58MPuWa9`?E=DKx}#h zw#*lruOo773FSB5pN@S}B!#usWKG#$$5Aq37;`Z}O#Cr~&OD4`5DuHP=?wj~15~B3 zY%FWs_ea4X6J25B_;K)i=qVpQgA|X@=ngD_Hf-Un(P!Oiu#T>x#d~xzIM-jQUk##I zOxjFh+Bp8N_X%#|c?Zr@{{pqzAOuBgT|sG39HyU#?zg`4>3OlN3iVFH*h^g)9w+x z7sq%{i}Ah=%nU;Pa>^+8JP3k}?6KgdKO|%R2we{k*Nakk$HSTH7l3@=_ab5*BKXek z-ss9yN5v^PLNY#dxYPw}v4C=A+q0{sBf zn-MnfK+HbTzR)|;=@+p+!v`;TB;vprHh^-%FX*o)GFI0UDysb|q3U$!EgKT}x2E|% z*YIM@@OE~Cot!(Ijuhh!cN_+~%<>?2Lmqf=`QaD!n~*;}I$L*G`>rE=sbl|=)Krv( zr`^9Kc{C|#`?Wc7`Azr(ATu?_&cz-^r zEFYA`1tio)JaX93!Zn)@O_LJoslEtl97=5m)HBgiAogk;`NDJPEPQuzO8p*0XTHs?ab-aK2nf*+av2dKv`}#ZktHz$3ND6@pioo+m1p`zgME&U0Ow zS`hBf#tyQ1MVud{ViEUL6rnUe(nQ1$hYj=d6wL8|M>FhG#hD{*ha{ zHj@_PQ+{ikKZW+JR$><3Q!{T$Hq0j9wh!;_5sT!uz4{VNCi#LaGRk-w&%jR!y(DKT zvYZA$*~Z*OvYY%rG+HB?{IUK!N5mVN5+vl0)Dyw5|93M=8uLe1o0%aA$ZsCWuYa=8 zjNpg(4RP`GK25Vcap_0O^EK(;K(jmZ^fV#IdF?8c0YhzSG*$l`wi}zz>0`ygg;gLJ zc(196{HF}^YZ}uUzXxDU8;o~F^XUW47vMBSxMFB9Z%kXq?_1JqILsl_UzJ|1fAt1A z07-udiR@9~spNy9O`yN0zO|dvr|Pah5h3gv&kRQpcB?$!f#D(x@-ls~JSPB715Xeo zI%$me*xih3Fo05i;m*6S4L(H+OwASDD$o#WOvZpe>^)7ohdw6!Wj> zI0ikCfku+H9hx(^>mQs0x|13Gk+-Cg%t$iz45H4t{ga2=n05*DbrTvijgVB5Qfh=~ zyRsoSrrm&wldbavMa_N<56$^W7e6J(TIxcZsecJCmj>aCT6?mMUVVTaFM1S>TDkrJ zsTX2XtEOym$^b{Mt0#&h*Nfp+AHBm=${%7m7zTBf(2b3YlQs`#2HiZ)&k;!7ToHgE zhiD$%khTuoW*!Xp4xC5!hsQK(KUY%fwj;Mgdb=(3F&}Cg79@b|<-YL2F}>lWYj95s zzkC0D7Cr!+=$O10R<)=MJ9julj!jLf=9Ie2V4#rjdMP1lis_ed2~+Cme7unI76e1N zub{Rd<*jBZBS@)VMQsr)K%%#zD#V;^98FBlN*SwH^7Y;kUD(;^2_Zn1?o2@WsQ$Ks z1+rLx1rn;i-IOENZhASSt`@qsQ$3Qc@_rZ8Q^>Ny((i#C*YG&u%#+)clGxZk+mG*3{9WAP`<^X?N6N_*xU@V|D&_633KPkKiBQR4BF~10J6Jhj1kcKoH_*N~~|A<|JVgC=Zab^7G$FxS>|0TF` z5o;Shdx`;KS2lXXk6Nj zH#WZd%AP%)zuujXh3teCg?M{~t!e^ZI1*as2*)<)js1{@q9dCR=QLh#tDck-UV5aq zBH5NV_kea*et2;&$nh}XaSQ;%=NrV5c=qH&1HF1C)~t>EY13hX&Y$exZ3y6?cU>H} zRgKk0gHp_Y;d~#^Km2IK`e+t)VU1}k$W7REbPy$@=EZksjfMl8#V}cyE+R!MLXsLz zYb+;i;nO40aRX7)b()A#8=0VDZo05XXUPl=hh=%XR`#AxYvMH4vyGTtm39;)(K!A#`BUHId2P%yWkt1yu8WClN|@nH`Jgqc0DjDI2+W(oWKoVMv2(|!Y6 zMa#XG`%Q8jxD5&QgS?06;a*EtKfP;-a?I3ia308?$l&W4@H{bNfMt*QY*C>NAAQ4% z5AL?*!xCVs3YkNNUh^U;F3G(b39l8S{Q7gj zxOg1`=5FB}0}7ca@zxK%OXNtczy1>LHvNHeq5*sSe#`{uRjDh{#a@fVs3_s&JpeD4 zIwH&t#p|8)e6%!Vu^APE${Hg7&FE(DFsD2p-35);Fm=j2dpFE9I(c8X_sJ{r9!$sc zV#nSKAIg5YC+D)HFmmS)H_#dpuUfM7EPAn4Es|0#+>IDVa56o{Mr0$CvH#)b9Wb(~XudOI)B-Bh5) zv}ciqdM7;Cwv=pTq*Lp7nRk*I`oF-R6J{0rr}{lWNl?FVmg4Id^@e_iY7os))HKy@ zW7=yFIp&*}a?eeVJWs+Tg=t=6uHWh(A;75L=|=s+=4$j0-lil}$Dh1_JEM+=$D@Yv zZ(=Hdy1pK&SzckP>+R_J;hedJT3pAtBK{e5ih90)TwbG|!{$^|v6LtcZ1P@S$$R_9 zyxenHQZusYzXMeeU#r>%s!)p8Ui0jO1&1|R$M*SpaVyRWazKMsAUcE`AI`I4NCmX0@denN@`4$ z`2;y>(>Me{&zYy(S6g5M6>_N7U;QKGX-GN;RtDc%ypOx4I+1DF2t5v&c_>X{(DwL* z7;~;dbfqy*dlNq{9+M* zF~w&L$CK=e5K8o<`|Zh)i1l9r8vPuR=tDHW{s2`ws2$WL*ke_fh2W_E40EK9!_ z%{SKga6|RwKvR#auf1m@=(P1EJa4M6Ne3zOq}!+;K=Ie{y23!3lrKZp8wG`q+R5AI zdZrxo4ZN=7swh#Ui?5;u)Lvy3{Tc0QV2{C9AH}1R{zRcQvGtr-^nYMdh^-e*#VU=;aPG%!6c+ z@c6EXjf77Z(#m^~#uUKku#Tfwfs^pZj@^N*)4RVLSlhjiNbI-0pBS9-! z(Uc#~-b%{O=O-kxBcONy6cltb1=T){elfe%d>^@ZYhIc9FY@Krd zigXW{OHvGSSD4oIM`MeR%!pdT_24=13&THk@OA%ZG3_(rFQWMWWr)n%KUw0Yqkae) zhHFTTx-sn&4B{BINqTbE`lt{Xy}LQAODAFnsCAB~y@cT~pBzU7=OfuxnQV}vL3W(O z7Vb9c?a#ue!0rcVY}jI?S2)rSRX;lcklwN-mm-jB{B<+t~z z(=3c!S>h6NialhtlC`683m2XuGeM+31#uw-6$o4RM`(^y-$c1km$-#zw}p?2!{Ze{ zAAti>XB&sM_MLcW3+Kz}a90hALFcYD9jty%h|Xtsnp_*jQ7M<2&SWEQnwK*n{@Ysv#cyD&U3GFSOVq8C+Oky7_l$_8aC zM!I>jK1g~Jfs2B?ipjGM9}-8JzC-gG5d$LQ7Rydz9+!{Uc1jOvD=3{vZ&yqrM0;tY z<#{To!3&IduIT-%Q6F4g(2O5a)PNg*p0EEw+?6-gY)oH^ZTsoG!@`C>X9Il!HrVlI znUy-2Q6ge4THPwRU=8*`S!#dE=D9qPXq5bU7HVn9NYnoiE#H`G5VypOe!G!~Z*uwx zi<#1A!WyuCvI)TwOHQftft(s*-C?K|WvW3eJly&NKuJqNaM2`Z9yX~*zy29asLG5& z2nw+UpNSaKsNZOC`U40d;n6p-{@380;?p}uVN&XzK>*hm1))QN-hP81GI_2qkwzt^MxO+@E~4^gEHBj-)(#Fn{+W|o7t zesX_#QM5}i*u6!QJus%E^drW_{cWad{A`p>E5K?(H?>ERg8}VLg1nlOqt7#Q;?j)} z8J5*Ql@~@B2=uN{Pe4rR+u>rpKz{?9cX}NhZmBqG7`h7!jWmWrFKmuc!8sl&$7~%!a38c;1;0H zxAdUCmHNer;qSn|$2;_f#wor@#-l0lUp)Rx8BdNR{meMo>UJ3q)8)TkNt}4|1|Y*v26jt& zLw{zR^8CGwH{>(pl;$~8ew0T%{)&u0mm=|t$3^J>-2Y!>{zm=8<40vY)_(Xe9zUIA zU&{1`{>=CoO1xUwFWDmf;_We6#&cT4oApmJpC{w77S4ZWoNV@$V!_|Ak9d4i9RGNH zMqGS6K3B#Y_8X7q$atgtW}Iv=PsY@n4nt4TsoIbS05L?RpPT`{BsgF^d^H(0B=gVwNJ#w*#W^ItR6>J(nZ#Gb zg|CwMO%fXU-YoIear7k;UlSLuO1wfsgU>pNo9l&Ss+8e=2`P-y1SMW2A@#KszER>o zHqld>dnInpm*_Xk@OlX;jIN(byir2Z8-@Rq#J5Oj=gZR~-E=iFZqA)Z@QN{C5%>`gu~~Wk@xcLPKw*Ql7VEIE}NE<^zexhwCzYKtiKke|nN|usY>0e%{gd zvytqWQ`Rv3pH;TE(sU}?b8fTaOT1C|CX4OkkmG+=4K z(txD_O9Pe$EDcy1ury$4z|w%F0ZRjx1}qI&8n85AX~5Eer2$I=mIf>hSQ@Z2U}?b8 zfTaOT1C|CX4OkkmG+=4K(txD_O9Pe$EDcy1ury$4z|w%F0ZRjx1}qI&8n85AX~5Ee zr2$I=mIf>hSQ@Z2U}?b8fTaOT1C|CX4OkkmG+=4K(txFbZ?^_|<@@IBrQ$tw{-yGL zbrLU)dmo)aABm&ylXy+u$oJV9{1m4MkAJ_N5np$lEL94ZKg{C#yZb zEJxpd@jkyE3E2lD-|u&_^oAZX4@i0m?S}+jC}HXI0=K^);Go1K63={5rjN@gBIWZ- zzJ*3UXDDB{Oy48ZHJzb+g;Jhg3GKaKFJG_ZUwBZ&D-!megg&l#Y*40;$aH=Q`_3?Z zs}^22#jdVEV|L@%`c_M#D_R5@_vRCG=&dI$oZ*guxcH!!x+?9FR z`HPnq6s%_JoK?;l9;bKhj3O=I_O6Wy{dg!EEv$-0?YJ^=5&rYIiy{0Igk2`%V~KGS z)LirCsnunwUvvAs0$t;Dl?(@&sTor%O4fROYn+}Sb9>zyD=As)4V9EtSI?VQQc~vg zmW2WV)mx^Pczk8Im#Be&FTfd`lrH2QLCNJI0h}I$Gv*HkRHe!t&_Yg+QmJYcK9^F4 zIMpTI*j-Yhsewwj*Qu!`6;7|qqXtTX6~i)@l!Ux$wO=jMRM*!L#8OMHNLVzAVsSP_|~Hy6sK2ldxM%2MOJ*}%9ZR&A=e4%*1I8lNTUSKG7bEdn2OJ4 zin6}Ksbx%_rzpds^SgsD4@ED>$2`p!g_*Beh^LEajQi6;NtuW=4e_OP@8cpm z3L|LnGUPJkp}3P>28}t5A-54mkixdUHWaOnyLJPo@UXn%0! z)1B`AbbKkkH8#%r#Th=@Ykd%|C+E<@i5|kfOJ8Pm3f#;V}6azo5!xt4g{PV*e$oRD$TjZ zqblXTfWmHEwMyJz0E0q9=%0OF#Y{2H?VZN74Sv<_E%yo97)H%6E~wlq0?xl{}|9=B5_FDY_r3M$-FQwkx*lKV zJw_0?&bNv>1J%&7gH$EEu9Pnn)@4`i}VVt<;BJTU24@Dor_1!Oq zq6=_M1?{!}S3tUV42f$-bSQe|S3}VpT;O%I=xJUY|vEZ@`cc3n8yddP2QJ#d`@y= zIh;8wi-o*qMsBY*9AZW|li?_02`-=*!6OzDtndX)oCI3sbGz7j60bxPLUA@R3Amg* z2yF|M<+*wmwKBS!GuybM{3@KDa$d}tbHIrhUOfvp+?D2TG;i6%chbiWy30z$@WIwN zO$d*c5ypgIIC?(upQ>;oA$!2-)`C2Ep$v~@B|?>jf{hzYRc9rcmzl2&J|$zY5HcD( zc>ca&hNd#f&@0(zfSYyUyR#wTTq7|}x znCnJ1V*ztHQJEf9Q!y&JR5pdVFktZjHl4Yu*bJsFXSXe2s*l~q)H~RM1#GR#7UExH z3z?^g&7Z%3!LI2)2!wewHlJ1Iv1tofWj>pNe>a=PDm|>Sl1-b&yiVp-*CR8p$}U^L zV7ajJ%b0gP1vB4Dws8UT`Pjyd4CBE@=FewWEnt2RyNdade&H16_v4GV%pU~8?g+6T zvOocww15SQ*g{AZsAQ8^Ac%Xk$vag{idgVYhE|4AXswq}=bhXH_+J4gQR}CzpEFwt zguI%YMpCESgGt{!F^^J|8^z>rMM$Ml4UuT&G-^+BDQnQGg9-*Tr^|&tFQ`o7AHO8V zigJVZcCT-}SMdkckjsZY5%Wp(g&5fV0iWh8^Ldmi6*D~qOma;Zxt%)Q8n>oUk<9d! z!K$)AR_1gU21tH?jp<(F!PJD;RZ7XG`&~}WIb8m7YJ(7~jNuL=BMSx95_ub3=UG>R zDYZIGe!|_dnVFb(S64gNxT|K)`~iA0ABJNWX1$o6x^mgb_-jB*u0#oqqv)>6eYq!a z92qb&{anspMLiBL5S*IBi1f1{Yta3TL|BF_|{OjQs_CQa9OHF0LT1 zR$Q;*O1{^|X5zX9*JfP5!u1-i#9AA>2A308J+8-b(bs22;mbFPEQyWA7YLKt7$tfWYVpX*r$i?34iyqL6< zW8Mmj@CDUE8Y53fxmMK{h05XX1{YD|U+hD#aVCj!t5mNREN}*IKNIRD9^d-KKCc$= zc?x{!lg~h^e79E(7NIGJf@eZ5^gu;kMziJtGBS!EEiBsS;zci!1T8nFm#` zYZ1s#ue+eC<*ZtH1`M@tP$6LYO)CUR3Y`IG<>?g*%9X2%PAl+m~=?SUXtd#KO7{HgR8*;0&;XP%$T-lnh zGMllLY_Ugm1{V7&E5!)QiiSgS#rD|^2!Vg(;M-g5KDHP`v8M9T2psO0_!7*Fve~~# ztRSSR$clAKyhyEd`mqWD681XF;nT~-7^vLXOe?~&4{iP(W4Yv!#Rs#?ZMnfRr(Y!@ zmxR26m|bIAf*FP=Mx|{@KvnbIYiQ|_RokvpHHskD2KhA-PKmku^*{`RXH_XZCe~zx zQh>_X^Bm`!iCSU6SBBXgd*Q^eJQq7;3SCt}P7MNC$|&TS?+j{EhZ6~hFBkGiJwh%A zg)PS<(Bt&`Ro6;2h(#rK1BFtSs3e+HG8U$gq76YTuCBriH9JVEraRV5F{|8Vw+la% z9UGO0O;I?wm?g%mF)txsbyl4a%<>cReNLCu9i;l#ghF!RNMG#91U~I8gq!RORMI9R z&qxihR}xmDq$CJFZjXByQBdWT&e%}{k5xLpshPM^^PogV{jH+aZFmQ1u*tW;!W@XsZw~mW5+nY#1|;S0)|IjzGfXI zo0xPHIe)xb+^l3hA&f1<(je9ceAtM_j(HI+6#CYbl%pjvwtk8)B7 zdpDt6N--`G3x}Nc{KQJNvdoVfoj`X%6**@lqUG@DS&6g)ioGQEgT$a8hYhrHc5Nb7 z+OT@U*g}3ss~L8FM_iWBok#WJ>(_MWEkm?Bv8r4q#z)|7{L0R`F}pDD>REhBi1Gb= zg#V*nOLJE)%grAyYX-{7*N4QK5StJDfBG`#XZTbW{FZ1S_3O`>8Q~- zm~k4&?iS}nb0kca-%ZYpj67Ewm&`){Q8duUuy%@(_+Rli$lyBzMp7e6Br{@<6h=xT z{zy%vDH4fvM|vW?k-o@agl$XRrfkdHR=UlbHAkAen|qpjoBNsvo0D56wAfn;TS{B1 zTk2c3wsg1jwDh*Oy(6-teaG${Jv(}L9ND4o7~H{HlUpaW zDy^BVS*?!N!q(E(iq`7Zn%1V)t*z~?-K{;X2U?G`_O%YS9&b%}`c@ z#cdUB{hohsgqqL)o|>xhu6Rqbsv3tIOV1(dF-| z?yBjk?`rDW+7;<)@9OS4(AC>@q^qwhd1va*2|JaYj-7=&i+7gptl!zRbL-B?&I3Dp zcOKcaGd9lwBFSGIwR|BHO?kKEMRnM^*$k-2!KkWLV7v3bB6QA`Sc(Z*|F+ literal 0 HcmV?d00001