Method of timing delivery input

Posted by ljzxtww on Sat, 05 Mar 2022 15:52:06 +0100

Carry the posts written by myself in the object-oriented curriculum discussion area since 2021 (last year), and make some revisions.

thank:

This content was written when I studied OO course last year. The posted code is a little crude, which may not run completely, or it needs some modification, which is only used as a hint / guide for solving the problem.

The complete and operable timing input delivery tool has been reproduced and open-source this year GitHub , otherwise Coekjan A copy of open source Similar tools , can be used directly, and there is no need to introduce irrelevant code into the job. (please do not submit the source code or binary of the above auxiliary tools to the operation warehouse. If they are checked again, you will bear the consequences)

In this job, the requirement for input and output is real-time interaction. The input mode adopted is to release the input regularly through the console (stdin). Because the program is multi-threaded, in addition to the input, the running thread (elevator, etc.) will also output the content to the console in real time. Therefore, releasing the input regularly has become a small difficulty in the process of this job test. This post gives several debugging methods of regular input, and welcome the big guys to actively supplement!

Manual input

describe

Manually input the elevator request directly in the console.

realization

Slightly.

Pros and cons

Advantages: it is the most original and does not need additional tools as an aid.

Disadvantages: it is difficult to accurately control the time; It is difficult to put in more input; The input will be disturbed by the output (stdin and stdout share the same console)

Recommended or not: not recommended.

Input emitter + pipe

describe

Write a transmitter that can output regularly, and use the pipeline to transmit the input content to the elevator program.

realization

launcher

The transmitter reads the input information containing time from the standard input or file, and outputs the content to the standard output regularly according to the read time.

In this post, a relatively simple C language implementation is given:

// timeInput.c
#include <stdio.h>
#include <time.h>

#ifdef WIN32
#include <windows.h>
#define SLEEP_MS(x) Sleep((x))
#else
#include <unistd.h>
#define SLEEP_MS(x) usleep(1000*(x))
#endif

char buf[1005];
long curMillis;

int main()
{
    long millis;
    while (scanf("%ld:", &millis) != EOF) {
        gets(buf);
        SLEEP_MS(millis - curMillis);
        puts(buf);
        fflush(stdout);
        curMillis = millis;
    }

    return 0;
}

The corresponding input format is time: one line of content, where time is the number of milliseconds from the beginning to the release of the line of content. Example:

0:Start
1000:Now is 1 second
1500:Now is 1.5 second
3000:Now is 3 second

Operation method

Package the elevator program into jar (such as Elevator.jar) and run it on the command line:

timeInput.exe < input.txt | java -jar Elevator.jar

Pros and cons

Advantages: the command line is adopted, which is convenient for automatic test and batch test

Disadvantages: it is difficult to interrupt debugging without the IDEA environment.

Recommended or not: recommended for batch self-test or mutual test of local evaluation machine.

matters needing attention

come from BUAAWander Reply:

[Tips] the second method must be timely fflush, otherwise the pipeline will flow the content to the running jar only after all outputs are written

Scheduled task + pipeline flow

describe

The timed task of Java is adopted to directly read the input content with time stamp from the standard input, and deliver it to the standard input interface (elevator input) through pipeline flow according to the time

realization

Concept introduction

  • Scheduled task

    The Timer class in Java is used to add timing tasks, and input to the standard input interface through timing tasks.

  • Pipe flow

    Java provides a flow for mutual communication between threads - pipeline flow, which is divided into pipeline input stream and pipeline output stream. They usually appear in pairs (connected through the connect method) and are used in different threads respectively.

Realization idea

First, take a look at the source code of the official input package. You can see that it provides two construction methods:

public ElevatorInput() {...} // Adopt system In standard input stream
public ElevatorInput(InputStream inputStream) {...} // Specify your own input stream

In order to realize the controllable delivery of content to the input interface, the system is no longer used when instantiating the input interface In, but uses a PipedInputStream. At the same time, in order to make the pipeline input flow work normally, you also need to instantiate a paired pipeline output flow and connect them.

Pipeline flow initialization:

// Instantiate a pair of pipe flows
PipedOutputStream myPipeOut = new PipedOutputStream();
PipedInputStream myPipeIn = new PipedInputStream();
// Connect the two, and the connect method of PipedOutputStream will throw IOException
try {
    myPipeOut.connect(myPipeIn); 
} catch (IOException e) {
    throw new AssertionError(e); // Never happen
}

Initialization of official input package:

ElevatorInput elevatorInput;
if (DEBUG) { // Debugging switch, be sure to turn it off when submitting evaluation!
    elevatorInput = new ElevatorInput(myPipeIn);
} else {
    elevatorInput = new ElevatorInput(System.in);
}

Then we can use the pipeline output stream object myPipeOut to deliver the input content to the official package.

In order to avoid the trouble of real-time interaction, we can prepare the input content containing time information in advance, then open a thread to read the prepared input content, analyze the time information, and add a scheduled task through Timer's schedule method. The content of the scheduled task is to output the input content to the pipeline flow.

public class DebugInput implements Runnable {

    private final OutputStream outputStream;

    public DebugInput(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    @Override
    public void run() {
        Scanner scanner = new Scanner(System.in);
        Timer timer = new Timer(); // Initialize a timer
        long currentMillis = System.currentTimeMillis();
        long maxMillis = 0; // Record the time of the last entry
        while (scanner.hasNext()) {
            long millis = scanner.nextLong(); // Read time first
            String input = scanner.next(); // Read the input (no spaces)
            maxMillis = millis; // Update maxMillis
            timer.schedule(new TimerTask() { // Create scheduled task
                @Override
                public void run() {
                    try {
                        outputStream.write(input.getBytes());
                        outputStream.write(0x0a); // Line feed
                        outputStream.flush(); // force refresh 
                    } catch (IOException e) {
                        throw new AssertionError(e);
                    }
                }
            }, new Date(currentMillis + millis));
        }
        // Finally, don't forget to turn off the pipeline flow and turn off the timer to add the last scheduled task
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    throw new AssertionError(e);
                }
                timer.cancel(); // Turn off the timer. If this sentence is not added, the timer may wait indefinitely
            }
        }, new Date(currentMillis + maxMillis + 1));
    }
}

Note: the above code reads the input information containing time from stdin. In order to facilitate debugging, you can store the input content in a file and configure Redirect Input in IDEA.

Input format: input content in milliseconds (the input content cannot have spaces)

0 Random
1000 1-FROM-1-TO-2
1500 2-FROM-2-TO-1

Start the thread of timed delivery input just written:

new Thread(new DebugInput(myPipeOut)).start();

Other points

Using this method for debugging, it is recommended to add a debugging switch, which can be turned on during local debugging and turned off when submitting evaluation. There is no need to modify the code. (be sure to turn off this debugging switch in the submitted code)

Pros and cons

Advantages: it is convenient to debug the break point in the IDEA, and it can also be packaged into a jar to directly redirect the standard input for testing.

Disadvantages: you need to add classes in the project, which is troublesome to implement.

Recommended: recommended during local debugging or self-test of local evaluation machine.

Method improvement

The input format given in the post is inconsistent with the time format of the actual example. Students who adopt this method can consider how to support the time format given by the example, for example:

[0.0]Random
[1.0]1-FROM-1-TO-2
[2.5]2-FROM-5-TO-1

(tip: modify 16 to 17 lines of code. You can use the preview homework and the regular expression learned in unit 1 to extract the time in brackets and the input after brackets)