ShellSerial.Mod 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. MODULE ShellSerial; (** AUTHOR "staubesv/be" PURPOSE "Serial port utilities for shell"; *)
  2. (**
  3. * Note: Based on code of "be"
  4. *
  5. * Usage:
  6. *
  7. * ShellSerial.Open [portNbr BitsPerSecond DataBits Parity StopBits] ~ opens a shell listening to serial port <portNbr>
  8. *
  9. * ShellSerial.YReceive [[filename] portNbr BitsPerSecond DataBits Parity StopBits Prompt] ~
  10. * ShellSerial.XReceive [[filename] portNbr BitsPerSecond DataBits Parity StopBits Prompt] ~
  11. *
  12. * Whereas
  13. * Parity = "odd"|"even"|"mark"|"space"|"no"
  14. * StopBits = "1"|"1.5"|"2"
  15. *
  16. * Examples:
  17. *
  18. * ShellSerial.Open 1 115200 no 1 8 ~
  19. * ShellSerial.YReceive ~
  20. * ShellSerial.XReceive ~
  21. *
  22. * History:
  23. *
  24. * 25.06.2007 First release (staubesv)
  25. *)
  26. IMPORT
  27. Modules, Kernel, Commands, Streams, (*Strings,*) Files, Serials, Shell, Objects, XYModem;
  28. CONST
  29. BufferSize = 1024;
  30. DefaultPrompt = "SHELL>";
  31. VAR
  32. shells : ARRAY Serials.MaxPorts + 1 OF Shell.Shell;
  33. PROCEDURE Yield(): BOOLEAN;
  34. BEGIN
  35. Objects.Yield();
  36. RETURN TRUE;
  37. END Yield;
  38. PROCEDURE ModemReceive(context : Commands.Context; modemMode: LONGINT); (** [[filename] portNbr BitsPerSecond DataBits Parity StopBits] ~ *)
  39. VAR
  40. fileName : Files.FileName;
  41. length: LONGINT;
  42. port : Serials.Port; portNbr, bps, data, parity, stop, res : LONGINT;
  43. isOpen0 : BOOLEAN;
  44. bps0, data0, parity0, stop0 : LONGINT;
  45. w : Streams.Writer; r : Streams.Reader;
  46. isTracePort: BOOLEAN;
  47. params: SET;
  48. i, receiveRes: LONGINT;
  49. BEGIN
  50. IF ~context.arg.GetString(fileName) & (modemMode # XYModem.YModem) THEN
  51. context.result := Commands.CommandParseError;
  52. context.error.String("file name is missing"); context.error.Ln;
  53. RETURN;
  54. END;
  55. context.arg.SkipWhitespace;
  56. IF context.arg.Available() # 0 THEN (* port settings are specified *)
  57. Serials.GetPortParameters(context.arg, portNbr, bps, data, parity, stop, params, res);
  58. IF res # 0 THEN
  59. context.result := 2;
  60. context.error.String("Invalid port settings, res="); context.error.Int(res,0); context.error.Ln;
  61. RETURN;
  62. END;
  63. ELSE
  64. (*
  65. if the command context is the same as the context of the currently executed command of a shell
  66. take that shell port as the port used for data transmission
  67. *)
  68. i := 0;
  69. WHILE (i < LEN(shells)) & ((shells[i] = NIL) OR ~shells[i].IsCurrentCmdContext(context)) DO
  70. INC(i);
  71. END;
  72. IF i < LEN(shells) THEN
  73. portNbr := i+1;
  74. bps := -1;
  75. ELSE
  76. context.result := 3;
  77. context.error.String("port number is not specified"); context.error.Ln;
  78. RETURN;
  79. END;
  80. END;
  81. port := Serials.GetPort(portNbr);
  82. IF port = NIL THEN
  83. context.result := 4;
  84. context.error.String("Cannot find port "); context.error.Int(portNbr, 0); context.error.Ln;
  85. RETURN;
  86. END;
  87. port.GetPortState(isOpen0, bps0, data0, parity0, stop0);
  88. IF bps = -1 THEN (* using port of a shell *)
  89. bps := bps0; data := data0; parity := parity0; stop := stop0;
  90. END;
  91. (* disable tracing over the selected port *)
  92. IF Serials.IsTracePort(port) THEN
  93. isTracePort := TRUE;
  94. Serials.SetTracePort(0,0,0,0,0, res);
  95. END;
  96. port.Close;
  97. port.Open(bps, data, parity, stop, res);
  98. IF res # Serials.Ok THEN
  99. context.result := 5;
  100. context.error.String("Could not open port "); context.error.Int(portNbr, 0);
  101. context.error.String(", res="); context.error.Int(res,0);
  102. context.error.Ln;
  103. IF isTracePort THEN
  104. Serials.SetTracePort(portNbr, bps0, data0, parity0, stop0, res);
  105. ELSIF isOpen0 THEN
  106. port.Open(bps0, data0, parity0, stop0, res);
  107. END;
  108. RETURN;
  109. END;
  110. NEW(w, port.Send, 4096); NEW(r, port.Receive, 4096);
  111. XYModem.Receive(r,w,fileName,modemMode,15000,5000,length,Yield,receiveRes);
  112. port.Close();
  113. IF isTracePort THEN
  114. Serials.SetTracePort(portNbr, bps0, data0, parity0, stop0, res);
  115. IF res # Serials.Ok THEN
  116. context.error.String("Warning: could not re-activate trace over port "); context.error.Int(portNbr, 0);
  117. context.error.String(", res="); context.error.Int(res,0);
  118. context.error.Ln;
  119. END;
  120. ELSIF isOpen0 THEN
  121. port.Open(bps0, data0, parity0, stop0, res);
  122. IF res # Serials.Ok THEN
  123. context.error.String("Warning: could not re-open port "); context.error.Int(portNbr, 0);
  124. context.error.String(", res="); context.error.Int(res,0);
  125. context.error.Ln;
  126. END;
  127. END;
  128. Wait(1000); (* Give the port open time so we see the output below *)
  129. IF receiveRes = 0 THEN
  130. context.out.String(" "); context.out.String(fileName);
  131. context.out.String(" ("); context.out.Int(length, 0); context.out.String(" Bytes"); context.out.String(")");
  132. context.out.String(" done.");
  133. context.out.Ln;
  134. ELSE
  135. context.result := 6;
  136. context.error.String("error "); context.error.Int(receiveRes,0); context.error.Ln;
  137. END;
  138. END ModemReceive;
  139. (** Receive a file using X-modem protocol *)
  140. PROCEDURE XReceive*(context : Commands.Context); (** [[filename] portNbr BitsPerSecond DataBits Parity StopBits] ~ *)
  141. BEGIN
  142. ModemReceive(context,XYModem.XModem);
  143. END XReceive;
  144. (** Receive a file using Y-modem protocol *)
  145. PROCEDURE YReceive*(context : Commands.Context); (** [[filename] portNbr BitsPerSecond DataBits Parity StopBits] ~ *)
  146. BEGIN
  147. ModemReceive(context,XYModem.YModem);
  148. END YReceive;
  149. (*PROCEDURE IsDigit(ch: CHAR): BOOLEAN;
  150. BEGIN
  151. RETURN (ch >= "0") & (ch <= "9")
  152. END IsDigit;*)
  153. PROCEDURE Wait(ms: LONGINT);
  154. VAR timer: Kernel.Timer;
  155. BEGIN
  156. NEW(timer); timer.Sleep(ms);
  157. END Wait;
  158. (** Open a shell listening on the specified <portNbr> *)
  159. PROCEDURE Open*(context : Commands.Context); (** [portNbr BitsPerSecond DataBits Parity StopBits Prompt] ~ *)
  160. VAR
  161. port : Serials.Port; portNbr, bps, data, parity, stop, res : LONGINT;
  162. prompt: ARRAY 32 OF CHAR;
  163. w : Streams.Writer; r : Streams.Reader;
  164. params: SET;
  165. BEGIN {EXCLUSIVE}
  166. Serials.GetPortParameters(context.arg, portNbr, bps, data, parity, stop, params, res);
  167. IF res # 0 THEN
  168. context.result := Commands.CommandError;
  169. context.error.String("Invalid port settings, res="); context.error.Int(res,0); context.error.Ln;
  170. RETURN;
  171. END;
  172. IF ~context.arg.GetString(prompt) THEN prompt := DefaultPrompt; END;
  173. port := Serials.GetPort(portNbr);
  174. IF port # NIL THEN
  175. port.Close;
  176. IF shells[portNbr-1] # NIL THEN
  177. shells[portNbr-1].Exit;
  178. (*! avoid dead-lock if this command is issued by shells[portNbr-1] *)
  179. IF ~shells[portNbr-1].IsCurrentCmdContext(context) THEN
  180. shells[portNbr-1].AwaitDeath;
  181. END;
  182. shells[portNbr-1] := NIL;
  183. END;
  184. port.Open(bps, data, parity, stop, res);
  185. IF (res = Serials.Ok) THEN
  186. NEW(w, port.Send, BufferSize); NEW(r, port.Receive, BufferSize);
  187. NEW(shells[portNbr-1], r, w, w, TRUE, prompt);
  188. ELSE
  189. context.error.String("Shell: could not open port "); context.error.Int(portNbr, 0);
  190. context.error.String(", res: "); context.error.Int(res, 0); context.error.Ln;
  191. END;
  192. ELSE
  193. context.error.String("Shell: serial port "); context.error.Int(portNbr, 0); context.error.String(" not found."); context.error.Ln;
  194. END;
  195. END Open;
  196. PROCEDURE Cleanup;
  197. VAR
  198. port : Serials.Port;
  199. i, res : LONGINT;
  200. isOpen0, isTracePort : BOOLEAN;
  201. bps0, data0, parity0, stop0 : LONGINT;
  202. BEGIN
  203. FOR i := 0 TO LEN(shells)-1 DO
  204. IF (shells[i] # NIL) THEN
  205. port := Serials.GetPort(i+1);
  206. isTracePort := Serials.IsTracePort(port);
  207. IF port # NIL THEN
  208. port.GetPortState(isOpen0, bps0, data0, parity0, stop0);
  209. port.Close;
  210. END;
  211. shells[i].Exit;
  212. (*shells[i].AwaitDeath;*)
  213. shells[i] := NIL;
  214. IF isTracePort THEN
  215. Serials.SetTracePort(i+1, bps0, data0, parity0, stop0, res);
  216. END;
  217. END;
  218. END;
  219. END Cleanup;
  220. BEGIN
  221. Modules.InstallTermHandler(Cleanup);
  222. END ShellSerial.