Skip to content

Commit 2f14971

Browse files
committed
Improve serial console interaction and network driver fixes
- Serial agent script: stream output as it arrives with 10s idle timeout (replaces fixed CMD_TIMEOUT, handles both fast and slow commands) - PingCommand: fix timeout handling, synchronized cancelRequest(), bounded cleanup loop to prevent infinite iterations - LanceCore: increase RX/TX descriptor rings from 4 to 16 - ResolverImpl: filter DNS answers to only A records with null check - Update jnode-interact skill docs for streaming behavior
1 parent 8987eb5 commit 2f14971

5 files changed

Lines changed: 73 additions & 42 deletions

File tree

.opencode/skills/jnode-interact/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ python3 .opencode/skills/jnode-interact/scripts/jnode_agent_cmd.py \
245245
1. Connects to the Unix socket (`/tmp/jnode_com2``/tmp/jnode.serial2`)
246246
2. Clears any pending data from the buffer
247247
3. Sends each command followed by `\r\n`
248-
4. Reads response until timeout (1 second of silence)
249-
5. Prints output, strips trailing whitespace
248+
4. Streams output as it arrives — prints complete lines immediately
249+
5. Waits for either the prompt or 10 seconds of silence since last output
250250

251251
### Important: Single client only
252252

.opencode/skills/jnode-interact/scripts/jnode_agent_cmd.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@
1111
import sys
1212

1313

14-
PROMPT = b'[JNODE_AGENT_READY]'
15-
PROMPT_TIMEOUT = 3.0
16-
CMD_TIMEOUT = 2.0
17-
18-
1914
def connect_serial():
2015
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
2116
sock.settimeout(5)
2217
sock.connect('/tmp/jnode_com2')
2318
return sock
2419

2520

21+
PROMPT = b'[JNODE_AGENT_READY]'
22+
PROMPT_TIMEOUT = 3.0
23+
OUTPUT_TIMEOUT = 10.0
24+
25+
2626
def clear_buffer(sock):
2727
"""Clear any pending data in socket buffer."""
2828
sock.settimeout(0.1)
@@ -33,7 +33,6 @@ def clear_buffer(sock):
3333
break
3434
except socket.timeout:
3535
break
36-
sock.settimeout(CMD_TIMEOUT)
3736

3837

3938
def wait_for_prompt(sock, timeout):
@@ -58,30 +57,49 @@ def wait_for_prompt(sock, timeout):
5857

5958

6059
def send_cmd(sock, cmd):
61-
"""Send command and read response."""
60+
"""Send command and stream response until prompt or output timeout."""
6261
clear_buffer(sock)
6362

6463
sock.send(f"{cmd}\r\n".encode())
6564

6665
buf = b""
66+
last_output_time = time.time()
67+
printed_up_to = 0
68+
6769
while True:
6870
try:
69-
sock.settimeout(CMD_TIMEOUT)
71+
remaining = last_output_time + OUTPUT_TIMEOUT - time.time()
72+
if remaining <= 0:
73+
break
74+
sock.settimeout(min(1.0, remaining))
7075
r = sock.recv(4096)
7176
if r:
7277
buf += r
73-
if b'Packet statistics' in buf:
74-
time.sleep(0.3)
78+
last_output_time = time.time()
79+
80+
# Print new complete lines immediately
81+
decoded = buf[printed_up_to:].decode(errors='replace')
82+
lines = decoded.split('\n')
83+
for line in lines[:-1]:
84+
stripped = line.strip()
85+
if stripped and PROMPT.decode() not in stripped:
86+
print(line)
87+
printed_up_to = len(buf) - len(lines[-1].encode(errors='replace'))
88+
89+
if PROMPT in buf:
7590
break
7691
else:
7792
break
7893
except socket.timeout:
79-
break
94+
if time.time() - last_output_time >= OUTPUT_TIMEOUT:
95+
break
8096

81-
output = buf.decode(errors='replace').rstrip()
82-
if output:
83-
for line in output.split('\n'):
84-
if line.strip():
97+
# Print any remaining buffer
98+
if printed_up_to < len(buf):
99+
remaining = buf[printed_up_to:].decode(errors='replace')
100+
for line in remaining.split('\n'):
101+
stripped = line.strip()
102+
if stripped and PROMPT.decode() not in stripped:
85103
print(line)
86104

87105

cli/src/commands/org/jnode/command/net/PingCommand.java

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@
88
* by the Free Software Foundation; either version 2.1 of the License, or
99
* (at your option) any later version.
1010
*
11-
* This library is distributed in the hope that it will be useful, but
11+
* This library is distributed in the hope that it will be useful, but
1212
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13-
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
1414
* License for more details.
1515
*
1616
* You should have received a copy of the GNU Lesser General Public License
17-
* along with this library; If not, write to the Free Software Foundation, Inc.,
17+
* along with this library; If not, write to the Free Software Foundation, Inc.,
1818
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1919
*/
20-
20+
2121
package org.jnode.command.net;
2222

2323
import java.io.PrintWriter;
@@ -55,7 +55,7 @@ public class PingCommand extends AbstractCommand implements ICMPListener {
5555
private static final String fmt_stats = "-> Packet statistics%n%s%n";
5656
private static final String fmt_get_stats = "%d packets transmitted, %d packets received%nround-trip min/avg/max" +
5757
" = %d/%.3f/%dms";
58-
58+
5959
// FIXME Some of the following could be command parameters ...
6060
private final Statistics stat = new Statistics();
6161
private boolean wait = true;
@@ -79,7 +79,7 @@ public PingCommand() {
7979
public static void main(String[] args) throws Exception {
8080
new PingCommand().execute(args);
8181
}
82-
82+
8383
public void execute() throws SocketException, InterruptedException {
8484
try {
8585
this.dst = new IPv4Address(argHost.getAsInetAddress());
@@ -88,7 +88,7 @@ public void execute() throws SocketException, InterruptedException {
8888
exit(1);
8989
}
9090
final PrintWriter out = getOutput().getPrintWriter();
91-
91+
9292
final IPv4Header netHeader =
9393
new IPv4Header(0, this.ttl, IPv4Constants.IPPROTO_ICMP, this.dst, 8);
9494
netHeader.setDontFragment(this.dontFragment);
@@ -122,14 +122,10 @@ public void execute() throws SocketException, InterruptedException {
122122
r.arm();
123123

124124
while (this.wait) {
125-
long time = System.currentTimeMillis() - r.getTimestamp();
126-
if (time > this.interval) {
127-
this.wait = false;
128-
}
129-
Thread.sleep(500);
125+
Thread.sleep(Math.min(500, this.interval));
130126
synchronized (this) {
131127
if (response) {
132-
out.format(fmt_reply,
128+
out.format(fmt_reply,
133129
dst.toString(), hdr1.getDataLength(), hdr1.getTtl(), hdr2.getSeqNumber(), roundt);
134130
response = false;
135131
}
@@ -139,16 +135,17 @@ public void execute() throws SocketException, InterruptedException {
139135
seq_count++;
140136
}
141137

142-
while (!isEmpty()) {
143-
Thread.sleep(100);
138+
long cleanupDeadline = System.currentTimeMillis() + 100;
139+
while (!isEmpty() && (System.currentTimeMillis() < cleanupDeadline)) {
140+
Thread.sleep(10);
144141
}
145142
} finally {
146143
icmpProtocol.removeListener(this);
147144
}
148-
145+
149146
out.format(fmt_stats, this.stat.getStatistics());
150147
}
151-
148+
152149
private long match(int id, int seq, Request r) {
153150
if (r != null && id == r.getId()) {
154151
return r.getTimestamp();
@@ -231,24 +228,24 @@ void arm() {
231228
}
232229

233230
public void run() {
234-
if (!this.Obsolete()) {
235-
stat.recordLost();
236-
removeRequest(this.seq);
237-
}
231+
Obsolete();
238232
}
239233

240234
synchronized boolean Obsolete() {
241235
if (!obsolete) {
242236
this.obsolete = true;
243237
this.cancelled = true;
244238
this.timer.cancel();
239+
this.stat.recordLost();
240+
removeRequest(this.seq);
241+
wait = false;
245242
return false;
246243
} else {
247244
return true;
248245
}
249246
}
250247

251-
void cancelRequest() {
248+
synchronized void cancelRequest() {
252249
this.cancelled = true;
253250
this.timer.cancel();
254251
}

net/src/driver/org/jnode/driver/net/lance/LanceCore.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ public class LanceCore extends AbstractDeviceCore implements IRQHandler, LanceCo
6666

6767
// This is the number of descriptors for the receive and transmit rings
6868
// Note: Valid numbers are 2^x where x is 0..8 (1, 2, 4, 8, 16, .., 512)
69-
private static final int RX_DESCRIPTOR_LENGTH = 4;
70-
private static final int TX_DESCRIPTOR_LENGTH = 4;
69+
private static final int RX_DESCRIPTOR_LENGTH = 16;
70+
private static final int TX_DESCRIPTOR_LENGTH = 16;
7171

7272
/**
7373
* Device Driver

net/src/net/org/jnode/net/ipv4/util/ResolverImpl.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,23 @@ private static Record[] resolveFromServer(String hostname, String server, int ti
244244
if ((answers == null) || (answers.length == 0)) {
245245
throw new UnknownHostException("No DNS answer for " + hostname);
246246
}
247-
return answers;
247+
int answerCount = 0;
248+
for (int i = 0; i < answers.length; i++) {
249+
if (answers[i].getType() == Type.A) {
250+
answerCount++;
251+
}
252+
}
253+
if (answerCount == 0) {
254+
throw new UnknownHostException("No DNS A answer for " + hostname);
255+
}
256+
Record[] aAnswers = new Record[answerCount];
257+
int index = 0;
258+
for (int i = 0; i < answers.length; i++) {
259+
if (answers[i].getType() == Type.A) {
260+
aAnswers[index++] = answers[i];
261+
}
262+
}
263+
return aAnswers;
248264
} catch (TextParseException ex) {
249265
throw new UnknownHostException(hostname);
250266
} catch (IOException ex) {

0 commit comments

Comments
 (0)