MODULE UsbBDI; IMPORT SYS := SYSTEM, BDI := UsbBDIDll, Log := StdLog, ComTools, WinApi, Services, TextModels, Ports, Files, HostFiles; CONST ttDefaultPeriod = 500; (*500 ms*) ttPipeHandDisconnected = -1; nul = 0X; ascLf = 0AX; ascCr = 0DX; CONST FALSE_ = 0; PATH_LENGTH* = 512; CONST JAVA_OUT_FILENAME = "JavaOut.txt"; JAVA_ERR_FILENAME = "JavaErr.txt"; INVALID_HANDLE_VALUE = -1; STD_OUT_COLOR = Ports.green; STD_ERR_COLOR = Ports.grey50; JAVA_OUT_COLOR = Ports.blue; JAVA_ERR_COLOR = Ports.red; CONST MAX_UART_DATA_LENGTH* = BDI.MAX_UART_DATA_LENGTH; CONST PATH_SEPERATOR = "\"; JAR_FILE = "usbpdi.jar"; JAR_LOCATION = "Usb" + PATH_SEPERATOR + "Rsrc"; CLASSPATH = JAR_LOCATION + PATH_SEPERATOR + JAR_FILE; TYPE StdReaderWriter = POINTER TO ABSTRACT RECORD (Services.Action) stdIoText: TextModels.Model; stdIoTextPos: INTEGER; time, endTime: LONGINT; period: INTEGER; hStdOut, hStdErr: WinApi.HANDLE; connected, running: BOOLEAN; prevChar: SHORTCHAR END; JvmReaderWriter = POINTER TO EXTENSIBLE RECORD (StdReaderWriter) errText: TextModels.Model; stdIoWriter, stdErrWriter: TextModels.Writer; hJavaOut, hJavaErr: WinApi.HANDLE; END; VAR bool: BDI.BOOL; Done-, deviceOpen, jvmCreated: BOOLEAN; (** previous operation successfully completed *) readerWriter: JvmReaderWriter; (* ----------- Standard-IO / Java ReaderWriter --------------- *) PROCEDURE (task: StdReaderWriter) Clear, NEW, EXTENSIBLE; BEGIN task.stdIoText := NIL; task.stdIoTextPos := 0; task.hStdOut := ttPipeHandDisconnected; task.hStdErr := ttPipeHandDisconnected; task.time := 0; task.endTime := 0; task.period := 0; task.connected := FALSE; task.running := FALSE; task.prevChar := nul; END Clear; PROCEDURE (task: StdReaderWriter) Stop, NEW, EXTENSIBLE; VAR done: WinApi.BOOL; BEGIN Services.RemoveAction(task); task.running := FALSE; IF task.connected THEN done := WinApi.CloseHandle(task.hStdOut); done := WinApi.CloseHandle(task.hStdErr); END; task.Clear END Stop; PROCEDURE (task: StdReaderWriter) SetTime (startFromNow, period, timeOut: INTEGER), NEW; BEGIN task.time := Services.Ticks() + startFromNow; IF timeOut <= 0 THEN timeOut := MAX(INTEGER) END; task.endTime := task.time + LONG(timeOut) * Services.resolution DIV 1000; task.period := period END SetTime; PROCEDURE (task: StdReaderWriter) ConnectStdOutPipe (hStdOut: WinApi.HANDLE), NEW; VAR done: WinApi.BOOL; BEGIN IF task.connected & (task.hStdOut # hStdOut) THEN done := WinApi.CloseHandle(task.hStdOut) END; task.hStdOut := hStdOut; task.connected := TRUE END ConnectStdOutPipe; PROCEDURE (task: StdReaderWriter) ConnectStdErrPipe (hStdErr: WinApi.HANDLE), NEW; VAR done: WinApi.BOOL; BEGIN IF task.connected & (task.hStdErr # hStdErr) THEN done := WinApi.CloseHandle(task.hStdErr) END; task.hStdErr := hStdErr; task.connected := TRUE END ConnectStdErrPipe; PROCEDURE (task: StdReaderWriter) Start, NEW, ABSTRACT; PROCEDURE NewJvmReaderWriter ( ): JvmReaderWriter; VAR rec: JvmReaderWriter; BEGIN NEW(rec); rec.Clear; RETURN rec END NewJvmReaderWriter; PROCEDURE (rec: JvmReaderWriter) Clear, EXTENSIBLE; VAR done: WinApi.BOOL; BEGIN rec.Clear^; rec.errText := NIL; rec.stdIoWriter := NIL; rec.stdErrWriter := NIL; rec.hJavaOut := ttPipeHandDisconnected; rec.hJavaErr := ttPipeHandDisconnected; END Clear; PROCEDURE (rec: JvmReaderWriter) Stop, EXTENSIBLE; VAR done: WinApi.BOOL; BEGIN IF rec.connected THEN rec.Stop^; done := WinApi.CloseHandle(rec.hJavaErr); done := WinApi.CloseHandle(rec.hJavaOut) END; rec.Clear END Stop; PROCEDURE (rec: JvmReaderWriter) ConnectJavaOutPipe (hJavaOut: WinApi.HANDLE), NEW; VAR done: WinApi.BOOL; BEGIN IF rec.connected & (rec.hJavaOut # hJavaOut) THEN done := WinApi.CloseHandle(rec.hJavaOut) END; rec.hJavaOut := hJavaOut; rec.connected := TRUE END ConnectJavaOutPipe; PROCEDURE (rec: JvmReaderWriter) ConnectJavaErrPipe (hJavaErr: WinApi.HANDLE), NEW; VAR done: WinApi.BOOL; BEGIN IF rec.connected & (rec.hJavaErr # hJavaErr) THEN done := WinApi.CloseHandle(rec.hJavaErr) END; rec.hJavaErr := hJavaErr; rec.connected := TRUE END ConnectJavaErrPipe; PROCEDURE (rec: JvmReaderWriter) Start; VAR handle, stdOutHandle, stdErrHandle: WinApi.HANDLE; BEGIN IF rec.running THEN rec.Stop END; IF rec.stdIoText = NIL THEN rec.stdIoText := TextModels.dir.New(); END; IF rec.stdIoWriter = NIL THEN rec.stdIoWriter := rec.stdIoText.NewWriter(NIL) END; rec.stdIoWriter.SetPos(rec.stdIoTextPos); IF rec.errText = NIL THEN rec.errText := TextModels.dir.New(); END; IF rec.stdErrWriter = NIL THEN rec.stdErrWriter := rec.errText.NewWriter(NIL) END; rec.stdErrWriter.SetPos(rec.errText.Length()); (* Connect Std pipes *) rec.ConnectStdOutPipe(BDI.getOutPipeHandle()); rec.ConnectStdErrPipe(BDI.getErrPipeHandle()); (* Connect Java pipes *) handle := WinApi.CreateFile(JAVA_OUT_FILENAME, WinApi.GENERIC_READ + WinApi.GENERIC_WRITE, WinApi.FILE_SHARE_WRITE, NIL, WinApi.OPEN_EXISTING, {}, 0); IF (handle = INVALID_HANDLE_VALUE) THEN Log.String("Error opening JAVA_OUT_FILENAME"); Log.Ln; ELSE rec.ConnectJavaOutPipe(handle) END; handle := WinApi.CreateFile(JAVA_ERR_FILENAME, WinApi.GENERIC_READ + WinApi.GENERIC_WRITE, WinApi.FILE_SHARE_WRITE, NIL, WinApi.OPEN_EXISTING, {}, 0); IF (handle = INVALID_HANDLE_VALUE) THEN Log.String("Error opening JAVA_ERR_FILENAME"); Log.Ln; ELSE rec.ConnectJavaErrPipe(handle) END; rec.running := TRUE; Services.DoLater(rec, rec.time) END Start; PROCEDURE (rec: JvmReaderWriter) Do; VAR outModel: TextModels.Model; n, ioDone, errDone, nofBytesRead, ioBytesAvail, errBytesAvail, exitCode: INTEGER; ch: SHORTCHAR; strSC: ARRAY 512 OF SHORTCHAR; success: WinApi.BOOL; BEGIN IF rec.running THEN (* STD OUT *) IF rec.hStdOut > 0 THEN nofBytesRead := 0; ioBytesAvail := 0; ioDone := WinApi.PeekNamedPipe( rec.hStdOut, (*//handle to pipe to copy from *) SYS.ADR(strSC), (*//pointer to data buffer*) LEN(strSC), (*//size, in bytes, of data buffer*) nofBytesRead, (*nofBytesRead,*) (*//pointer to number of bytes read*) ioBytesAvail, (*//pointer to total number of bytes available*) NIL (*//pointer to unread bytes in this message*) ); IF (ioDone # 0) & (ioBytesAvail > 0) THEN ioDone := WinApi.ReadFile(rec.hStdOut, SYS.ADR(strSC), LEN(strSC), nofBytesRead, NIL); IF (ioDone # 0) & (nofBytesRead > 0) THEN FOR n := 0 TO nofBytesRead - 1 DO ch := strSC[n]; IF (ch = ascLf) & (rec.prevChar # ascCr) THEN ch := TextModels.line END; rec.stdIoWriter.WriteChar(ch); rec.prevChar := ch; END END; rec.stdIoText.SetAttr(0, rec.stdIoText.Length(), TextModels.NewColor(TextModels.dir.attr, STD_OUT_COLOR)); Log.text.Append(rec.stdIoText) END; END; (* STD ERR *) IF rec.hStdErr > 0 THEN nofBytesRead := 0; errBytesAvail := 0; ioDone := WinApi.PeekNamedPipe( rec.hStdErr, (*//handle to pipe to copy from *) SYS.ADR(strSC), (*//pointer to data buffer*) LEN(strSC), (*//size, in bytes, of data buffer*) nofBytesRead, (*nofBytesRead,*) (*//pointer to number of bytes read*) errBytesAvail, (*//pointer to total number of bytes available*) NIL (*//pointer to unread bytes in this message*) ); IF (ioDone # 0) & (errBytesAvail > 0) THEN errDone := WinApi.ReadFile(rec.hStdErr, SYS.ADR(strSC), LEN(strSC), nofBytesRead, NIL); IF (errDone # 0) & (nofBytesRead > 0) THEN FOR n := 0 TO nofBytesRead - 1 DO ch := strSC[n]; IF (ch = ascLf) & (rec.prevChar # ascCr) THEN ch := TextModels.line END; rec.stdErrWriter.WriteChar(ch); rec.prevChar := ch END END; rec.errText.SetAttr(0, rec.errText.Length(), TextModels.NewColor(TextModels.dir.attr, STD_ERR_COLOR)); Log.text.Append(rec.errText) END END; (* JAVA OUT *) IF rec.hJavaOut > 0 THEN ioDone := WinApi.ReadFile(rec.hJavaOut, SYS.ADR(strSC), LEN(strSC), nofBytesRead, NIL); IF (ioDone # 0) & (nofBytesRead > 0) THEN FOR n := 0 TO nofBytesRead - 1 DO ch := strSC[n]; IF (ch = ascLf) & (rec.prevChar # ascCr) THEN ch := TextModels.line END; rec.stdIoWriter.WriteChar(ch); rec.prevChar := ch; END END; rec.stdIoText.SetAttr(0, rec.stdIoText.Length(), TextModels.NewColor(TextModels.dir.attr, JAVA_OUT_COLOR)); Log.text.Append(rec.stdIoText) END; (* JAVA ERR *) IF rec.hJavaErr > 0 THEN errDone := WinApi.ReadFile(rec.hJavaErr, SYS.ADR(strSC), LEN(strSC), nofBytesRead, NIL); IF (errDone # 0) & (nofBytesRead > 0) THEN FOR n := 0 TO nofBytesRead - 1 DO ch := strSC[n]; IF (ch = ascLf) & (rec.prevChar # ascCr) THEN ch := TextModels.line END; rec.stdErrWriter.WriteChar(ch); rec.prevChar := ch END END; rec.errText.SetAttr(0, rec.errText.Length(), TextModels.NewColor(TextModels.dir.attr, JAVA_ERR_COLOR)); Log.text.Append(rec.errText) END; INC( rec.time, rec.period ); Services.DoLater(rec, rec.time ) END; END Do; (* ----------- JVM --------------- *) PROCEDURE CreateJVM (classpath: ARRAY OF SHORTCHAR); VAR string: WinApi.PtrSTR; BEGIN string := ComTools.NewSString(classpath); bool := BDI.createJVM(string); IF bool = FALSE_ THEN Done := FALSE; Log.String("creating JVM failed"); Log.Ln; HALT(77); ELSE Done := TRUE; Log.String("JVM successfully created"); Log.Ln; jvmCreated := TRUE; END; (* We need to create the jvm before opening the ReaderWriter. The javaout, javaerr can only be opened after creating the jvm! *) IF readerWriter = NIL THEN readerWriter := NewJvmReaderWriter(); readerWriter.Start(); END; ComTools.FreeSString(string); END CreateJVM; PROCEDURE DestroyJVM ; BEGIN bool := BDI.destroyJVM(); IF bool = FALSE_ THEN Done := FALSE; Log.String("destroying JVM failed"); Log.Ln; ELSE Done := TRUE; Log.String("JVM destroyed"); Log.Ln; END; jvmCreated := FALSE; END DestroyJVM; PROCEDURE CheckForExceptions * (): BOOLEAN; BEGIN RETURN (BDI.checkForExceptions() # FALSE_) END CheckForExceptions; (* ----------- USB --------------- *) PROCEDURE USB_Device_open ; BEGIN BDI.USB_Device_open; Done := ~CheckForExceptions(); IF Done THEN deviceOpen := TRUE; END END USB_Device_open; PROCEDURE USB_Device_close ; BEGIN BDI.USB_Device_close; Done := ~CheckForExceptions(); deviceOpen := FALSE; END USB_Device_close; PROCEDURE USB_Device_reset * ; BEGIN BDI.USB_Device_reset; Done := ~CheckForExceptions(); deviceOpen := FALSE; END USB_Device_reset; PROCEDURE USB_Device_getMaxPacketSize * (): INTEGER; VAR val: INTEGER; BEGIN IF deviceOpen THEN Done := TRUE; RETURN BDI.USB_Device_getMaxPacketSize(); ELSE Done := FALSE; RETURN - 1 END END USB_Device_getMaxPacketSize; (* --------- UART0 ----------------- *) PROCEDURE UART0_read * (VAR data: ARRAY OF SHORTCHAR; VAR length: INTEGER): BOOLEAN; BEGIN length := BDI.UART0_read(data); RETURN length # FALSE_; END UART0_read; PROCEDURE UART0_write * (VAR data: ARRAY OF SHORTCHAR; length: INTEGER): BOOLEAN; VAR result: INTEGER; BEGIN RETURN BDI.UART0_write(SYS.ADR(data[0]), length) # FALSE_; END UART0_write; (* -------------- Helper Functions ------------------*) PROCEDURE GetUserStartUpPath (VAR startupPath: ARRAY OF SHORTCHAR); VAR userStartUpLocator: Files.Locator; userStartUpHostLoc: HostFiles.Locator; startupPathChar: ARRAY PATH_LENGTH OF CHAR; i: INTEGER; BEGIN userStartUpLocator := Files.dir.This(""); userStartUpHostLoc := userStartUpLocator(HostFiles.Locator); startupPathChar := userStartUpHostLoc.path$; FOR i := 0 TO LEN(startupPathChar) - 1 DO startupPath[i] := SYS.VAL(SHORTCHAR, startupPathChar[i]); END END GetUserStartUpPath; PROCEDURE GetSystemStartUpPath (VAR startupPath: ARRAY OF SHORTCHAR); VAR cmdLinePtr: WinApi.PtrWSTR; args: WinApi.RetCommandLineToArgvW; n, i, nofArgs: INTEGER; sysStartUpLocator: Files.Locator; sysStartUpHostLoc: HostFiles.Locator; BEGIN cmdLinePtr := WinApi.GetCommandLineW(); args := WinApi.CommandLineToArgvW(cmdLinePtr, nofArgs); n := LEN(args[0]$); REPEAT DEC(n) UNTIL (args[0][n] = PATH_SEPERATOR) OR (n <= 0); args[0][n] := nul; sysStartUpLocator := HostFiles.NewLocator(args[0]$); args[0][n] := PATH_SEPERATOR; sysStartUpHostLoc := sysStartUpLocator(HostFiles.Locator); FOR i := 0 TO LEN(sysStartUpHostLoc.path$) DO startupPath[i] := SYS.VAL(SHORTCHAR, sysStartUpHostLoc.path[i]); END; END GetSystemStartUpPath; PROCEDURE CheckIfFileExists (filePath: ARRAY OF CHAR; fileName: ARRAY OF CHAR): BOOLEAN; VAR i: INTEGER; done: BOOLEAN; loc: Files.Locator; file: Files.File; name: Files.Name; BEGIN loc := Files.dir.This(filePath); IF loc.res # 0 THEN (*RETURN FALSE*) END; FOR i := 0 TO LEN(fileName$) DO name[i] := fileName[i]; END; file := Files.dir.Old(loc, name, Files.shared); RETURN loc.res = 0 END CheckIfFileExists; PROCEDURE ConnectDevice * ; VAR startupPath: ARRAY PATH_LENGTH OF SHORTCHAR; BEGIN IF ~deviceOpen THEN IF ~jvmCreated THEN (* Check for existence of jar-file (whether in user of system directory) *) GetUserStartUpPath(startupPath); IF ~CheckIfFileExists(startupPath + PATH_SEPERATOR + JAR_LOCATION, JAR_FILE) THEN GetSystemStartUpPath(startupPath); IF ~CheckIfFileExists(startupPath + PATH_SEPERATOR + JAR_LOCATION, JAR_FILE) THEN Log.String(JAR_FILE + " not found in user or system path; expected in " + JAR_LOCATION); Log.Ln; END END; Log.String("Startup Path: " + startupPath); Log.Ln; CreateJVM(startupPath + PATH_SEPERATOR + CLASSPATH); (* CreateJVM("D:\work\USB\eclipse\mcdp\bin\"); *) IF ~Done THEN HALT(78) END; jvmCreated := TRUE; END; USB_Device_open; IF ~Done THEN HALT(79) END; deviceOpen := TRUE; END END ConnectDevice; PROCEDURE DeviceOpen * (): BOOLEAN ; BEGIN RETURN deviceOpen END DeviceOpen; (* Close and reopen the device *) PROCEDURE ReopenDevice * ; BEGIN ConnectDevice; USB_Device_close; USB_Device_open; IF Done THEN Log.String("Device opened successfully"); Log.Ln; ELSE Log.String("Error while opening Device"); Log.Ln; END END ReopenDevice; BEGIN deviceOpen := FALSE; jvmCreated := FALSE; CLOSE IF readerWriter # NIL THEN readerWriter.Stop; END; IF deviceOpen THEN USB_Device_close; END; IF jvmCreated THEN DestroyJVM END END UsbBDI.