fact-project / FACT_js_scripts

trying to refactor Main.js
0 stars 0 forks source link

schedule object is too complicated #4

Open dneise opened 7 years ago

dneise commented 7 years ago

If you look at the website, where people can enter the schedule the schedule can be entered in some kind of two dimensional structure. foo3

On this website "observations" have a start-time and something like "sub-observations" do not necessarily have a start-time but follow each other, so they have an implicit start time only, which makes scheduling certainly a little bit harder.

In Main.js this schedule is represented as a list-of-lists, or rather a list of "Observation" objects, which inherit from list. Each of these Observation objects is thus a list of measurements, which have a task, i.e. a capital letter string such as "DATA" or "RATESCAN", which defines in a complicated way which fields of a "measurement" should be definded or not.

This so called "business logic" should be implemented by the user interface, so the scheduling user creating the schedule is informed about missing information already while typing it in. As an example:

Observation must have either 'source' or 'ra' & 'dec' if 'task' == 'ratescan'

So if I had provided a source and a position by ra/dec in the image above Main.js would have thrown an exception when reading the schedule (once per while loop), and thus would have stooped, resulting in a shifthelper call. It makes sense, that a user can provide a place to look at by different means:

and that only one of these should be possible at a time, and not providing any position should not be possible. However, when trying to deliberately specify both a "source" and a ra/dec position in the web interface I get this message:

ERROR - Task 'Ratescan' must not have source.

So some business logic was put into the end-user interface ... just it does not agree with the business logic in the application. I am puzzled at this moment, how to proceed .. since:

Why can't we say:

So here the logic is even broken.

This is not even what I wanted to tal kabout, but I got carried away


So these tasks are represented as a list of Observations (which inherits from list). And each Observation has a measurements. A measurement has these members: task, source, ra, dec, zd, az, orbit, angle, time, threshold, lidclosed, biason, rstype, sub, start. where certain conditions must apply, like if task == "DATA" it must not be anywhere in the list of measurements but in the end and so on.

If you now look at what is actually done by the telescope at night, you do not find such a two dimentsional structure. You find that one thing is done after another. So why is the schedule not a linear list of things. Let's call the things in the schedule Takss.

Now certainly a task can either have a start-time as in:

But some tasks have no start-time, their start-time is only implicitely defined by the end of their predecessor as in:

This makes things difficult like finding out what the next task to be done is given the current time. Since one also needs to know what task has been done already. Especially if you think about it like this: Main.js just started in the middle of the night, since it just crashed and needs to find out what to do next. It can do the following: Go through the flat list of tasks and find the first task, which has a start-time in the past. Start here.

dneise commented 7 years ago

So instead of telling, what I don't like about the current version, I should show, how I think it should be this.

After reading the raw tasks from the DB, a tasklist needs to be prepared (as is the case also now), in the following manner:

This tasklist is worked off as follows:

for task in tasklist:
    status = get_complete_system_status() # this is a huge dict-like thing
    while not task.is_complete(status):
        status = get_complete_system_status()
        task.advance(status)

task.is_complete uses some internal variable as well as the system status to find out if it was already done. E.g. a RATESCAN is done when the ratescan program is not doing a ratescan anymore. However that state is identical to never having even started, so the task needs to remember if it was started.

task.advance(status) tries to get a task done, given the current system status. This boils down to a list of states, which should be reached, so the task can consider itself started and then another list of states, which should be reached so it can consider itself completed. In some cases in can also consider itself as failed so that it can decide to try again.

How to reach this list of states is of course delegated down to another huge number of functions, which all only know how to get a certain program from one state to another.

As soon as a task considers itself to be started advance() will only be a NOP, so don't worry ... this will not crazily send command around ... but checking for incomplete events, and thus disconnected FADs, makes only sense if a DATA task considers itself even started.

The problem with this tasklist implementation I see at the moment is updating the tasklist on the go, which is done at the moment. So there are tasks added to the list, mostly SLEEP tasks. Now if I want a task to be a full blown control instance, which remembers its own state, so it can decided whether it was already started or not, I cannot re-create the tasklist all the time, when reading the DB.


Another solution would be, that the tasklist or rather the current-task should be part of the state as in: "I am tracking Crab and taking data, and I should be pointing north and do a single-pe run"

So the code should look like this then:

previous_status = None
while True:
    status = get_current_system_status_including_tasklist()
    previous_status = advance(status, previous_status)

By giving advance not only the current status, but also the previous status, we can understand if we need to start something, or if we are already done doing something. And by returning previous_status from advance we can store information like, what was the task we were working on before?.

This way advance can be implemented stateless, which makes it much much easier to test.