ShellSerial.Mod 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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. TRACE(context.arg.Available());
  57. IF context.arg.Available() # 0 THEN (* port settings are specified *)
  58. Serials.GetPortParameters(context.arg, portNbr, bps, data, parity, stop, params, res);
  59. IF res # 0 THEN
  60. context.result := 2;
  61. context.error.String("Invalid port settings, res="); context.error.Int(res,0); context.error.Ln;
  62. RETURN;
  63. END;
  64. ELSE
  65. (*
  66. if the command context is the same as the context of the currently executed command of a shell
  67. take that shell port as the port used for data transmission
  68. *)
  69. i := 0;
  70. WHILE (i < LEN(shells)) & ((shells[i] = NIL) OR ~shells[i].IsCurrentCmdContext(context)) DO
  71. INC(i);
  72. END;
  73. IF i < LEN(shells) THEN
  74. portNbr := i+1;
  75. bps := -1;
  76. ELSE
  77. context.result := 3;
  78. context.error.String("port number is not specified"); context.error.Ln;
  79. RETURN;
  80. END;
  81. END;
  82. port := Serials.GetPort(portNbr);
  83. IF port = NIL THEN
  84. context.result := 4;
  85. context.error.String("Cannot find port "); context.error.Int(portNbr, 0); context.error.Ln;
  86. RETURN;
  87. END;
  88. port.GetPortState(isOpen0, bps0, data0, parity0, stop0);
  89. IF bps = -1 THEN (* using port of a shell *)
  90. bps := bps0; data := data0; parity := parity0; stop := stop0;
  91. END;
  92. TRACE(portNbr, isOpen0, bps0, data0, parity0, stop0);
  93. (* disable tracing over the selected port *)
  94. IF Serials.IsTracePort(port) THEN
  95. TRACE("disabling trace port");
  96. isTracePort := TRUE;
  97. Serials.SetTracePort(0,0,0,0,0, res);
  98. END;
  99. TRACE("closing port");
  100. port.Close;
  101. TRACE("opening port", bps, data, parity, stop);
  102. port.Open(bps, data, parity, stop, res);
  103. TRACE(res);
  104. IF res # Serials.Ok THEN
  105. context.result := 5;
  106. context.error.String("Could not open port "); context.error.Int(portNbr, 0);
  107. context.error.String(", res="); context.error.Int(res,0);
  108. context.error.Ln;
  109. IF isTracePort THEN
  110. Serials.SetTracePort(portNbr, bps0, data0, parity0, stop0, res);
  111. ELSIF isOpen0 THEN
  112. port.Open(bps0, data0, parity0, stop0, res);
  113. END;
  114. RETURN;
  115. END;
  116. NEW(w, port.Send, 4096); NEW(r, port.Receive, 4096);
  117. XYModem.Receive(r,w,fileName,modemMode,15000,5000,length,Yield,receiveRes);
  118. TRACE("closing port");
  119. port.Close();
  120. IF isTracePort THEN
  121. Serials.SetTracePort(portNbr, bps0, data0, parity0, stop0, res);
  122. IF res # Serials.Ok THEN
  123. context.error.String("Warning: could not re-activate trace over port "); context.error.Int(portNbr, 0);
  124. context.error.String(", res="); context.error.Int(res,0);
  125. context.error.Ln;
  126. END;
  127. ELSIF isOpen0 THEN
  128. port.Open(bps0, data0, parity0, stop0, res);
  129. IF res # Serials.Ok THEN
  130. context.error.String("Warning: could not re-open port "); context.error.Int(portNbr, 0);
  131. context.error.String(", res="); context.error.Int(res,0);
  132. context.error.Ln;
  133. END;
  134. END;
  135. Wait(1000); (* Give the port open time so we see the output below *)
  136. IF receiveRes = 0 THEN
  137. context.out.String(" "); context.out.String(fileName);
  138. context.out.String(" ("); context.out.Int(length, 0); context.out.String(" Bytes"); context.out.String(")");
  139. context.out.String(" done.");
  140. context.out.Ln;
  141. ELSE
  142. context.result := 6;
  143. context.error.String("error "); context.error.Int(receiveRes,0); context.error.Ln;
  144. END;
  145. END ModemReceive;
  146. (** Receive a file using X-modem protocol *)
  147. PROCEDURE XReceive*(context : Commands.Context); (** [[filename] portNbr BitsPerSecond DataBits Parity StopBits] ~ *)
  148. BEGIN
  149. ModemReceive(context,XYModem.XModem);
  150. END XReceive;
  151. (** Receive a file using Y-modem protocol *)
  152. PROCEDURE YReceive*(context : Commands.Context); (** [[filename] portNbr BitsPerSecond DataBits Parity StopBits] ~ *)
  153. BEGIN
  154. ModemReceive(context,XYModem.YModem);
  155. END YReceive;
  156. (*PROCEDURE IsDigit(ch: CHAR): BOOLEAN;
  157. BEGIN
  158. RETURN (ch >= "0") & (ch <= "9")
  159. END IsDigit;*)
  160. PROCEDURE Wait(ms: LONGINT);
  161. VAR timer: Kernel.Timer;
  162. BEGIN
  163. NEW(timer); timer.Sleep(ms);
  164. END Wait;
  165. (** Open a shell listening on the specified <portNbr> *)
  166. PROCEDURE Open*(context : Commands.Context); (** [portNbr BitsPerSecond DataBits Parity StopBits Prompt] ~ *)
  167. VAR
  168. port : Serials.Port; portNbr, bps, data, parity, stop, res : LONGINT;
  169. prompt: ARRAY 32 OF CHAR;
  170. w : Streams.Writer; r : Streams.Reader;
  171. params: SET;
  172. BEGIN {EXCLUSIVE}
  173. Serials.GetPortParameters(context.arg, portNbr, bps, data, parity, stop, params, res);
  174. IF res # 0 THEN
  175. context.result := Commands.CommandError;
  176. context.error.String("Invalid port settings, res="); context.error.Int(res,0); context.error.Ln;
  177. RETURN;
  178. END;
  179. IF ~context.arg.GetString(prompt) THEN prompt := DefaultPrompt; END;
  180. port := Serials.GetPort(portNbr);
  181. IF port # NIL THEN
  182. TRACE("closing port");
  183. port.Close;
  184. IF shells[portNbr-1] # NIL THEN
  185. TRACE("exiting a shell");
  186. shells[portNbr-1].Exit;
  187. (* avoid dead-lock if this command is issued by shells[portNbr-1] *)
  188. TRACE(~shells[portNbr-1].IsCurrentCmdContext(context));
  189. IF ~shells[portNbr-1].IsCurrentCmdContext(context) THEN
  190. shells[portNbr-1].AwaitDeath;
  191. END;
  192. shells[portNbr-1] := NIL;
  193. END;
  194. TRACE("opening port", portNbr, bps, data, parity, stop);
  195. port.Open(bps, data, parity, stop, res);
  196. TRACE(res);
  197. IF (res = Serials.Ok) THEN
  198. NEW(w, port.Send, BufferSize); NEW(r, port.Receive, BufferSize);
  199. NEW(shells[portNbr-1], r, w, w, TRUE, prompt);
  200. ELSE
  201. context.error.String("Shell: could not open port "); context.error.Int(portNbr, 0);
  202. context.error.String(", res: "); context.error.Int(res, 0); context.error.Ln;
  203. END;
  204. ELSE
  205. context.error.String("Shell: serial port "); context.error.Int(portNbr, 0); context.error.String(" not found."); context.error.Ln;
  206. END;
  207. END Open;
  208. PROCEDURE Cleanup;
  209. VAR
  210. port : Serials.Port;
  211. i, res : LONGINT;
  212. isOpen0, isTracePort : BOOLEAN;
  213. bps0, data0, parity0, stop0 : LONGINT;
  214. BEGIN
  215. FOR i := 0 TO LEN(shells)-1 DO
  216. IF (shells[i] # NIL) THEN
  217. port := Serials.GetPort(i+1);
  218. isTracePort := Serials.IsTracePort(port);
  219. IF port # NIL THEN
  220. port.GetPortState(isOpen0, bps0, data0, parity0, stop0);
  221. port.Close;
  222. END;
  223. shells[i].Exit;
  224. (*shells[i].AwaitDeath;*)
  225. shells[i] := NIL;
  226. IF isTracePort THEN
  227. Serials.SetTracePort(i+1, bps0, data0, parity0, stop0, res);
  228. END;
  229. END;
  230. END;
  231. END Cleanup;
  232. BEGIN
  233. Modules.InstallTermHandler(Cleanup);
  234. END ShellSerial.