Difference between revisions of "MC-Basic Programs"
(moved SERCOS to extra page) |
(BackToTop button) |
||
(27 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{Out-of-date}} | {{Out-of-date}} | ||
+ | <div id="BackToTop" class="noprint" style="background-color:; position:fixed; bottom:32px; left:95%; z-index:9999; padding:0; margin:0;"> | ||
+ | <span style="color:blue; font-size:8pt; font-face:verdana,sans-serif; border:0.2em outset:#ceebf7; padding:0.1em; font-weight:bolder; -moz-border-radius:8px; "> | ||
+ | [[Image:TOP2.png|50px|link=#top]] </span></div> | ||
− | A project is all the software written for an application. The projects in MC- | + | = Introduction = |
+ | A project is all the software written for an application. The projects in MC-Basic are multitasking. They consist of many tasks running concurrently and independently of each other. The project is stored in multiple files, one file for each task. Tasks are routines that run simultaneously with many other tasks. Projects also include other files such as cam tables and record files. Projects are controlled from the ControlStudio software. | ||
+ | <!-- For detailed information on any commands listed within this section, refer to the Reference Manual. --> | ||
− | + | ControlStudio manages projects and handles a group of logically coupled files (programs, cam tables, etc.), while the softMC deals with separate files (PRG, CAM, etc.). The softMC does not keep project files in the flash memory and is not aware of logical coupling of files and programs. | |
+ | |||
+ | ControlStudio automatically sets up a separate directory for each project. | ||
= Project Structure = | = Project Structure = | ||
− | Project files include tasks and cam tables | + | Project files include tasks and cam tables. |
− | |||
− | |||
− | |||
− | |||
− | An '''optional autoexec task''' (Autoexec.Prg) to automatically start the application on power-up. | + | Each project can contain three types of tasks: |
+ | *'''General-purpose task''' | ||
+ | *An '''optional configuration task''' (Config.Prg) to declare groups, programmable limit switches (PLS), cams, global variables and load of global libraries. | ||
+ | *An '''optional autoexec task''' (Autoexec.Prg) to automatically start the application on power-up. | ||
− | [[Image:Axystems;UserManual-31ProjectStructure.png|caption| | + | [[Image:Axystems;UserManual-31ProjectStructure.png|caption|400px]] |
= Tasks = | = Tasks = | ||
Line 20: | Line 26: | ||
== General Purpose Tasks == | == General Purpose Tasks == | ||
− | General-purpose tasks are the | + | General-purpose tasks are the workhorse of the softMC language. They implement the basic logic of your application. The great majority of your programming is implemented with general-purpose tasks. Each general-purpose task is divided into three sections: a task-variable section, a main program, and subroutines. The main program is further divided into three sections: a Start-up section, an OnError section, and an OnEvent section. |
[[Image:Axystems;UserManual-321GeneralPurposeTasks.png|caption]] | [[Image:Axystems;UserManual-321GeneralPurposeTasks.png|caption]] | ||
Line 41: | Line 47: | ||
The name of the configuration task is Config.Prg. The configuration task is used for declaration of number of axes, global variables and other constructs, such as cam tables, programmable limit switches (PLS), and group. The key to understanding the configuration task is that all data and constructs shared across tasks must be declared in the configuration task. Refer to the following figure. | The name of the configuration task is Config.Prg. The configuration task is used for declaration of number of axes, global variables and other constructs, such as cam tables, programmable limit switches (PLS), and group. The key to understanding the configuration task is that all data and constructs shared across tasks must be declared in the configuration task. Refer to the following figure. | ||
− | [[Image:Axystems;UserManual-322ConfigurationTask.png|caption]] | + | [[Image:Axystems;UserManual-322ConfigurationTask.png|caption|400px]] |
The configuration task can contain a program. Axes can be renamed here. | The configuration task can contain a program. Axes can be renamed here. | ||
== AutoExec Task == | == AutoExec Task == | ||
− | The AutoExec task (AutoExec.Prg) is executed once on power up, just after the Configuration Task. Use AutoExec to start power-up logic. For example, you might want to use AutoExec to start a task that sets the outputs to the desired starting values. That way, the outputs are set immediately after the | + | The AutoExec task (AutoExec.Prg) is executed once on power up, just after the Configuration Task. Use AutoExec to start power-up logic. For example, you might want to use AutoExec to start a task that sets the outputs to the desired starting values. That way, the outputs are set immediately after the softMC boots, usually sooner than the PC. |
For safety considerations we do not recommend starting of motion from the AutoExec task. Motion should be started by explicit operator’s request either by I/O or communication link from host PC. | For safety considerations we do not recommend starting of motion from the AutoExec task. Motion should be started by explicit operator’s request either by I/O or communication link from host PC. | ||
Line 52: | Line 58: | ||
Set the AutoExec task to run on power up. To do this, add the keyword Continue'' ''to the Program command. Do not include OnError or OnEvent sections in the AutoExec. Limit the AutoExec task to starting other tasks in the system. Refer to the next figure. | Set the AutoExec task to run on power up. To do this, add the keyword Continue'' ''to the Program command. Do not include OnError or OnEvent sections in the AutoExec. Limit the AutoExec task to starting other tasks in the system. Refer to the next figure. | ||
− | [[Image:Axystems;UserManual-323AutoExecTask.png|caption]] | + | [[Image:Axystems;UserManual-323AutoExecTask.png|caption|400px]] |
= Program Declarations = | = Program Declarations = | ||
Line 89: | Line 95: | ||
'''Parentheses are not used in a subroutine if no parameters are passed.''' | '''Parentheses are not used in a subroutine if no parameters are passed.''' | ||
− | MC- | + | MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any type mismatch causes an error during program loading. Automatic casting applies to numeric variables types. For example, suppose MySub is a subroutine that takes a Long parameter. In this case, the following scenario applies: |
<pre> | <pre> | ||
CALL MySub(3.3432) | CALL MySub(3.3432) | ||
− | |||
'OK: The double value 3.3432 is demoted to the Long value, 3 | 'OK: The double value 3.3432 is demoted to the Long value, 3 | ||
− | |||
CALL MySub(a) | CALL MySub(a) | ||
− | |||
'Error: a is a string, there is a type mismatch | 'Error: a is a string, there is a type mismatch | ||
− | |||
Call MySub(3,4) | Call MySub(3,4) | ||
− | |||
'Error: The number of parameters is not correct | 'Error: The number of parameters is not correct | ||
</pre> | </pre> | ||
+ | |||
See the Subroutine Example in Appendix A for further details. | See the Subroutine Example in Appendix A for further details. | ||
Line 123: | Line 125: | ||
<nowiki>+: means one or more [*]</nowiki> | <nowiki>+: means one or more [*]</nowiki> | ||
+ | |||
+ | |||
<pre> | <pre> | ||
SUB<nowiki> CalculateMean(x[*][*] as DOUBLE, TheMean[*] as LONG)</nowiki> | SUB<nowiki> CalculateMean(x[*][*] as DOUBLE, TheMean[*] as LONG)</nowiki> | ||
− | |||
DIM sum as DOUBLE | DIM sum as DOUBLE | ||
− | |||
DIM I as LONG | DIM I as LONG | ||
− | |||
FOR i = 1 to 100 | FOR i = 1 to 100 | ||
− | |||
<nowiki>sum = sum + x[i][1]</nowiki> | <nowiki>sum = sum + x[i][1]</nowiki> | ||
− | |||
NEXT i | NEXT i | ||
− | |||
<nowiki>TheMean[1] = sum/100</nowiki> | <nowiki>TheMean[1] = sum/100</nowiki> | ||
− | |||
END SUB | END SUB | ||
− | |||
SUB PrintMean(ByVal Mean as LONG) | SUB PrintMean(ByVal Mean as LONG) | ||
− | |||
PRINT “Mean Value Is “, Mean | PRINT “Mean Value Is “, Mean | ||
− | |||
END SUB | END SUB | ||
− | |||
− | |||
CALL CalculateMean(XArray, TheMeanArray) ‘ Pass entire array by reference | CALL CalculateMean(XArray, TheMeanArray) ‘ Pass entire array by reference | ||
− | |||
− | |||
CALL<nowiki> PrintMean(TheMeanArray[1]) </nowiki>‘ Pass a single array element by value | CALL<nowiki> PrintMean(TheMeanArray[1]) </nowiki>‘ Pass a single array element by value | ||
</pre> | </pre> | ||
+ | |||
When a variable is passed by reference (whether the variable is local to the task or global), the address of the variable is passed to the subroutine, which changes the value of the original variable (if the code of the subroutine is written to do this). | When a variable is passed by reference (whether the variable is local to the task or global), the address of the variable is passed to the subroutine, which changes the value of the original variable (if the code of the subroutine is written to do this). | ||
Line 163: | Line 154: | ||
Parentheses are not used in subroutine '''CALL''' if no parameters are passed. | Parentheses are not used in subroutine '''CALL''' if no parameters are passed. | ||
− | MC- | + | MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any mismatch (in number of parameters or parameters types) causes an error during program loading. Automatic type casting applies only for “by value” long and double parameters. |
<pre> | <pre> | ||
SUB MySub(RefPar as long, byval ValPar as double) | SUB MySub(RefPar as long, byval ValPar as double) | ||
− | |||
… | … | ||
− | |||
END SUB | END SUB | ||
− | |||
− | |||
CALL MySub(LongVar, “String”) -> type mismatch in second parameter | CALL MySub(LongVar, “String”) -> type mismatch in second parameter | ||
− | |||
CALL MySub(LongVar) -> wrong number of parameters | CALL MySub(LongVar) -> wrong number of parameters | ||
− | |||
CALL MySub(LongVar, 2) -> a valid type casting for a by-value parameter | CALL MySub(LongVar, 2) -> a valid type casting for a by-value parameter | ||
− | |||
CALL MySub(DoubleVar, 2.2) -> invalid type casting for a by-ref parameter | CALL MySub(DoubleVar, 2.2) -> invalid type casting for a by-ref parameter | ||
</pre> | </pre> | ||
+ | |||
== User-Defined Functions == | == User-Defined Functions == | ||
− | MC | + | MC-Basic allows the definition of user functions to be used in programs in the same manner as using BASIC pre-defined functions. User-defined functions are composed with multiple lines and may be recursive (can call itself). Unlike BASIC system functions, the scope of user-defined functions is limited to the task in which it is defined. |
Functions are different from subroutines in one respect. Functions always return a value to the task that called the function. Otherwise, functions and subroutines use the same syntax and follow the same rules of application and behavior. | Functions are different from subroutines in one respect. Functions always return a value to the task that called the function. Otherwise, functions and subroutines use the same syntax and follow the same rules of application and behavior. | ||
Line 188: | Line 173: | ||
<pre> | <pre> | ||
PRINT<nowiki> <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} | PRINT<nowiki> <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} | ||
− | |||
<nowiki><</nowiki>variable_name<nowiki>> = <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} | <nowiki><</nowiki>variable_name<nowiki>> = <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} | ||
− | |||
IF<nowiki> <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} > 10 THEN | IF<nowiki> <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} > 10 THEN | ||
− | |||
? LOG<nowiki>( <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} ) | ? LOG<nowiki>( <</nowiki>function_name<nowiki>>{(<</nowiki>par_1<nowiki>>{, …<</nowiki>par_n>})} ) | ||
</pre> | </pre> | ||
+ | |||
Parentheses are not used in function '''CALL''' if no parameters are passed. Parameters (either scalar or array) passed to the function are used within the code of the function. Declare variable names and types of parameters in the declaration line of the function. Parameters can be passed by reference or by value. The default is by reference. | Parentheses are not used in function '''CALL''' if no parameters are passed. Parameters (either scalar or array) passed to the function are used within the code of the function. Declare variable names and types of parameters in the declaration line of the function. Parameters can be passed by reference or by value. The default is by reference. | ||
Line 203: | Line 186: | ||
There is no explicit limit on the number of functions allowed in a task. All functions must be located following the main program and must be contained wholly outside of the main program. | There is no explicit limit on the number of functions allowed in a task. All functions must be located following the main program and must be contained wholly outside of the main program. | ||
− | MC- | + | MC-Basic automatically checks the type of compliance between the function declaration and the function call. Any mismatch (in number of parameters, in parameters and returned value types) causes an error during program loading. Automatic type casting applies only for long and double returned values and “by value” parameters. For example: |
<pre> | <pre> | ||
Function LongReturnFunc(…) As Long | Function LongReturnFunc(…) As Long | ||
− | |||
LongReturnFunc = “String” -> type mismatch in returned value | LongReturnFunc = “String” -> type mismatch in returned value | ||
− | |||
End Function | End Function | ||
− | |||
Function LongReturnFunc(…) As Long | Function LongReturnFunc(…) As Long | ||
− | |||
LongReturnFunc = 43.7 -> valid type casting in returned value | LongReturnFunc = 43.7 -> valid type casting in returned value | ||
− | |||
End Function | End Function | ||
</pre> | </pre> | ||
+ | |||
A function can be recursive (can call itself). The following example defines a recursive function to calculate the value of ''N'': | A function can be recursive (can call itself). The following example defines a recursive function to calculate the value of ''N'': | ||
<pre> | <pre> | ||
FUNCTION Factorial (ByVal N As Long) As Double | FUNCTION Factorial (ByVal N As Long) As Double | ||
− | |||
'By declaring N to be long, this truncates floating point numbers | 'By declaring N to be long, this truncates floating point numbers | ||
− | |||
'to integers | 'to integers | ||
− | |||
'The function returns a Double value | 'The function returns a Double value | ||
− | |||
<nowiki>If N < 3 then </nowiki>'This statement stops the recursion | <nowiki>If N < 3 then </nowiki>'This statement stops the recursion | ||
− | |||
Factorial = N '0!=0; 1!=1' 2!=2 | Factorial = N '0!=0; 1!=1' 2!=2 | ||
− | |||
Else | Else | ||
− | |||
Factorial=N * Factorial(N-1) 'Recursive statement | Factorial=N * Factorial(N-1) 'Recursive statement | ||
− | |||
End If | End If | ||
− | |||
END FUNCTION | END FUNCTION | ||
</pre> | </pre> | ||
+ | |||
When writing a recursive function, you must have an '''IF''' statement to force the function to return without the recursive call being executed. Otherwise, the function never returns once it is called. | When writing a recursive function, you must have an '''IF''' statement to force the function to return without the recursive call being executed. Otherwise, the function never returns once it is called. | ||
Line 266: | Line 237: | ||
<pre> | <pre> | ||
<nowiki>SUB mean(x[*][*] as DOUBLE, TheMean[*] as LONG)</nowiki> | <nowiki>SUB mean(x[*][*] as DOUBLE, TheMean[*] as LONG)</nowiki> | ||
− | |||
DIM sum as DOUBLE | DIM sum as DOUBLE | ||
− | |||
DIM I as LONG | DIM I as LONG | ||
− | |||
FOR i = 1 to 100 | FOR i = 1 to 100 | ||
− | |||
<nowiki>sum = sum + x[i][1]</nowiki> | <nowiki>sum = sum + x[i][1]</nowiki> | ||
− | |||
NEXT i | NEXT i | ||
− | |||
<nowiki>TheMean[1] = sum/100</nowiki> | <nowiki>TheMean[1] = sum/100</nowiki> | ||
− | |||
END SUB | END SUB | ||
</pre> | </pre> | ||
− | = | + | = Multitasking = |
− | The | + | The softMC supports multitasking. You can have multiple tasks running independently, sharing a single computer. A task is a section of code that runs in its own context. Microsoft Windows<sup></sup> is a multitasking system. If you open Explorer and Word at the same time, they run nearly independently of each another. |
In this case, both Explorer and Word have their own contexts. They share one computer, but run as if the other were not present. There is inter-task communication. If you double-click on a document in the file manager, it launches Word to edit the file you clicked. | In this case, both Explorer and Word have their own contexts. They share one computer, but run as if the other were not present. There is inter-task communication. If you double-click on a document in the file manager, it launches Word to edit the file you clicked. | ||
− | With MC- | + | With MC-Basic, you can use different tasks to control different operational modes: one for power up, one for set-up, one for normal operation, and another for when problems occur. Like Windows, each task can run independently of the others, and you can prescribe interactions between tasks. |
− | + | Multitasking is used when you want multiple processes to run largely independent of each other. For example, if you are using the softMC to interface to the operator, you will usually use a separate task to execute the interface code. Another example is when two parts of a machine are largely independent of each other. There is usually some control required between tasks as one task may start or stop another. | |
− | If a machine is simple to control, you should try to keep the entire program in one task (in addition to Config.Prg). If you do need to use | + | If a machine is simple to control, you should try to keep the entire program in one task (in addition to Config.Prg). If you do need to use multitasking, you should keep a highly structured architecture. It is recommended that you limit use of the main task for axis and group set up, machine initialization, and controlling the other tasks. Normal machine operation should be programmed in other tasks. For example, Main.Prg might be limited to setting up axes, and then starting Pump.Prg,'' ''Conveyor.Prg'','' and Operator.Prg. |
Do not split control of an axis or group across tasks. You can put control for multiple axes in one task. Ideally, you should use multiple tasks for machines where different sections operate more or less independently. You can also use tasks to implement different operational modes. | Do not split control of an axis or group across tasks. You can put control for multiple axes in one task. Ideally, you should use multiple tasks for machines where different sections operate more or less independently. You can also use tasks to implement different operational modes. | ||
− | + | Multitasking is a powerful tool, but it carries a cost. It is easy to make errors that are difficult to find. When multiple tasks are running concurrently, complex interaction is difficult to understand and recreate. Limit the use of tasks to situations where they are needed. | |
− | Do not create a task as a substitute for an event handler. Events and tasks are not the same. MC- | + | Do not create a task as a substitute for an event handler. Events and tasks are not the same. MC-Basic supports event handlers to respond to realtime events. Events are similar to interrupts in microprocessor systems. They normally run at higher priorities than the programs that contain them. They are ideal for quick responses to realtime events. Add the event handler to an existing task to respond to an event. |
Do not use tasks in place of subroutines. Remember that when you start a task, the original task continues to run. When you call a subroutine, you expect the calling program to suspend execution until the subroutine is complete. The behavior of tasks where the two routines continue to execute can cause complex problems. | Do not use tasks in place of subroutines. Remember that when you start a task, the original task continues to run. When you call a subroutine, you expect the calling program to suspend execution until the subroutine is complete. The behavior of tasks where the two routines continue to execute can cause complex problems. | ||
− | Knowing when to use | + | Knowing when to use multitasking and when to avoid it requires some experience. If you are new to multitasking, you may want to start slow until you are familiar with how it affects program structure. When you start a new project, ControlStudio creates the main task as part of opening a new project. After that process is complete, you can add a new task to your project by selecting File, New. You can also press the new task button on the ControlStudio tool bar. |
== Loading the Program == | == Loading the Program == | ||
− | + | ControlStudio automatically loads all tasks in your project when you select Run Project. You can select Run Project by selecting it from the Debug menu, by pressing the F5 key, or by pressing the “Load Task”and “Run Task” buttons on the tool bar. By default, tasks are loaded from the host PC to the softMC at a low priority (Priority = 16). | |
When you select Run Task, the project’s main task is started at the lowest priority (Priority = 16). You can change the priority of the main task by selecting View-> Project Manager->Options and then changing the priority in the bottom of the window. If you structure your software so that the main program loads all other tasks, the Run Project button starts your machine. | When you select Run Task, the project’s main task is started at the lowest priority (Priority = 16). You can change the priority of the main task by selecting View-> Project Manager->Options and then changing the priority in the bottom of the window. If you structure your software so that the main program loads all other tasks, the Run Project button starts your machine. | ||
− | == Preemptive | + | == Preemptive Multitasking & Priority Levels == |
Because many tasks share one processor, you must carefully design your system so tasks get processing resources when they need them. You do not want the operator interface taking all the resources when you need fast response to a realtime event. The operating system provides system resources based on two criteria: task priority level and time slice. | Because many tasks share one processor, you must carefully design your system so tasks get processing resources when they need them. You do not want the operator interface taking all the resources when you need fast response to a realtime event. The operating system provides system resources based on two criteria: task priority level and time slice. | ||
− | When you create a program, you must select the task '''priority level'''. MC- | + | When you create a program, you must select the task '''priority level'''. MC-Basic allows you to specify 16 levels of priority. The task with the highest priority takes all the system resources it can use. In fact, no task of a lower priority receives any resources until all tasks of higher priority relinquish them. Most systems have one main task that runs at a medium priority and perhaps a background task that runs at a low priority, with a few high priority tasks. At every time slice, the softMC reevaluates which task has the highest priority and assigns resources to it. |
− | The | + | The ControlStudio terminal window relies on the softMC command line task, which runs at priority 2. If you start a task with priority 1, the terminal will not be available until the task is complete or idle. Consequently, you will not be able to communicate with the softMC and you may have to power-down the system to recover the terminal window. You can optionally set the priority of a task when you start it. For example: |
<pre>StartTask Aux.Prg Priority=6</pre> | <pre>StartTask Aux.Prg Priority=6</pre> | ||
Line 319: | Line 283: | ||
The default priority of events is 1 (highest) and the default priority of programs is 16. | The default priority of events is 1 (highest) and the default priority of programs is 16. | ||
− | '''Time Slice''' is a method by which the operating system divides up resources when multiple tasks share the same priority level. The | + | '''Time Slice''' is a method by which the operating system divides up resources when multiple tasks share the same priority level. The softMC provides the first task with one time slice, the next time slice goes to the second, the next to the third, and so on. The time slice is currently one millisecond duration. This method is sometimes called round robin scheduling. |
== Inter-Task Communications and Control == | == Inter-Task Communications and Control == | ||
− | Tasks can control one-another. In fact, any task can start, continue, idle, or kill any other task, regardless of which task has the higher priority. For detailed information on these commands, refer to the | + | Tasks can control one-another. In fact, any task can start, continue, idle, or kill any other task, regardless of which task has the higher priority. For detailed information on these commands, refer to the Reference Manual. |
− | '''StartTask '''starts tasks from the main task. For testing, you can use '''STARTTASK''' from the terminal window. | + | '''StartTask '''starts tasks from the main task. For testing, you can use '''STARTTASK''' from the terminal window. Do not use '''STARTTASK''' in AutoExec.Prg. The syntax of '''STARTTASK''' is: |
<nowiki>StartTask <</nowiki>''TaskName''<nowiki>> {Priority = <</nowiki>''Level''<nowiki>>}{NumberOfLoops = <</nowiki>''Loop Count''>} | <nowiki>StartTask <</nowiki>''TaskName''<nowiki>> {Priority = <</nowiki>''Level''<nowiki>>}{NumberOfLoops = <</nowiki>''Loop Count''>} | ||
Line 338: | Line 302: | ||
<pre> | <pre> | ||
StartTask Task1.Prg Priority=8 NumberOfLoops = -1 'Run Task1 forever | StartTask Task1.Prg Priority=8 NumberOfLoops = -1 'Run Task1 forever | ||
− | |||
StartTask Main.Prg NOL=1 'Run Main once | StartTask Main.Prg NOL=1 'Run Main once | ||
</pre> | </pre> | ||
+ | |||
'''IdleTask''' stops the task at the end of the line currently being executed and idles all its events. An idled task can be continued (using '''CONTINUETASK''') or terminated (using '''KILLTASK''').''' IDLETASK''' does not stop motion currently being executed. This is significant because all the events are idled and cannot respond to an axis' motion. Tasks can be idled explicitly by other tasks, but cannot idel itself. This command is issued from a task or the terminal window. The syntax of '''IdleTask''' is: | '''IdleTask''' stops the task at the end of the line currently being executed and idles all its events. An idled task can be continued (using '''CONTINUETASK''') or terminated (using '''KILLTASK''').''' IDLETASK''' does not stop motion currently being executed. This is significant because all the events are idled and cannot respond to an axis' motion. Tasks can be idled explicitly by other tasks, but cannot idel itself. This command is issued from a task or the terminal window. The syntax of '''IdleTask''' is: | ||
Line 367: | Line 331: | ||
== Monitoring Tasks From the Terminal == | == Monitoring Tasks From the Terminal == | ||
− | For detailed information on these commands, refer to the | + | For detailed information on these commands, refer to the Reference Manual. |
'''TASK.STATUS''' provides the current state of any task. You can query '''TASK.STATE''' from the terminal window. You cannot use '''TASK.STATUS''' from within a program. The syntax for '''TASK.STATUS''' is: | '''TASK.STATUS''' provides the current state of any task. You can query '''TASK.STATE''' from the terminal window. You cannot use '''TASK.STATUS''' from within a program. The syntax for '''TASK.STATUS''' is: | ||
Line 383: | Line 347: | ||
? TaskList | ? TaskList | ||
</pre> | </pre> | ||
+ | |||
A typical result is: | A typical result is: | ||
<pre> | <pre> | ||
TaskName = TASK1.PRG, Status = sstep, Priority=16 | TaskName = TASK1.PRG, Status = sstep, Priority=16 | ||
− | |||
TaskName = DO.PRG, Status = suspend, Priority=4 | TaskName = DO.PRG, Status = suspend, Priority=4 | ||
</pre> | </pre> | ||
== Relinquishing Resources == | == Relinquishing Resources == | ||
− | When tasks of different priorities compete for processor time, the highest priority task always takes all the resources it needs. However, tasks of high priority can relinquish computer resources under some conditions. In these cases, tasks of lower priority run until the high priority tasks again demand the resources. There are three conditions under which a task relinquishes resources: when the task is terminated, when the task is suspended, or when the task is idled. For detailed information on these commands, refer to the | + | When tasks of different priorities compete for processor time, the highest priority task always takes all the resources it needs. However, tasks of high priority can relinquish computer resources under some conditions. In these cases, tasks of lower priority run until the high priority tasks again demand the resources. There are three conditions under which a task relinquishes resources: when the task is terminated, when the task is suspended, or when the task is idled. For detailed information on these commands, refer to the Reference Manual. |
A '''task terminates''' when it is finished executing. If a task starts with '''NUMBEROFLOOPS''' greater than zero, the task executes the specified number of times and terminates. The task relinquishes all resources. Terminated tasks remain loaded in the system and can be restarted. | A '''task terminates''' when it is finished executing. If a task starts with '''NUMBEROFLOOPS''' greater than zero, the task executes the specified number of times and terminates. The task relinquishes all resources. Terminated tasks remain loaded in the system and can be restarted. | ||
Line 411: | Line 375: | ||
The main program can contain sections which automatically handle events. This reduces the programming effort required to make tasks respond quickly and easily to realtime events. Event handlers begin with '''OnEvent''' and end with '''End OnEvent''' and occur just after the '''Program''' keyword. | The main program can contain sections which automatically handle events. This reduces the programming effort required to make tasks respond quickly and easily to realtime events. Event handlers begin with '''OnEvent''' and end with '''End OnEvent''' and occur just after the '''Program''' keyword. | ||
− | After '''OnEvent''' is loaded, turn the event On with '''EventOn''' just after the '''End OnEvent''' keyword (enable immediately). However, you can enable and disable '''OnEvent '''at any time using '''EventOn''' and '''EventOff'''. Multiple '''OnEvents''' can be written sequentially. The | + | After '''OnEvent''' is loaded, turn the event On with '''EventOn''' just after the '''End OnEvent''' keyword (enable immediately). However, you can enable and disable '''OnEvent '''at any time using '''EventOn''' and '''EventOff'''. Multiple '''OnEvents''' can be written sequentially. The softMC system can support up to 64 events. The number of '''OnEvent'''(s) in a single task is not restricted, so a task may have from 0 to 64 '''OnEvent'''(s). |
It is important to understand that '''OnEvent'''s are different from ordinary tasks. '''OnEvent'''s are preemptive within the task. That is, an '''OnEvent '''runs until complete and the program execution returns to the main program. While an '''OnEvent''' is executing, it does not release CPU time to the parent task or any other task. In this sense, '''OnEvents '''are similar to interrupt calls. They run to completion before execution returns to the main program. An '''OnEvent''' must have a higher priority than its parent task to ensure that when an event occurs, it interrupts its parent task and runs to completion. The rules are valid for a single process, (that is, the parent task and its events), while events of the respective tasks in the system share the CPU among themselves. | It is important to understand that '''OnEvent'''s are different from ordinary tasks. '''OnEvent'''s are preemptive within the task. That is, an '''OnEvent '''runs until complete and the program execution returns to the main program. While an '''OnEvent''' is executing, it does not release CPU time to the parent task or any other task. In this sense, '''OnEvents '''are similar to interrupt calls. They run to completion before execution returns to the main program. An '''OnEvent''' must have a higher priority than its parent task to ensure that when an event occurs, it interrupts its parent task and runs to completion. The rules are valid for a single process, (that is, the parent task and its events), while events of the respective tasks in the system share the CPU among themselves. | ||
Line 433: | Line 397: | ||
<pre> | <pre> | ||
OnEvent MOVE_ON_TRIGGER System.Din.1=ON | OnEvent MOVE_ON_TRIGGER System.Din.1=ON | ||
− | |||
Move X-axis 10000 | Move X-axis 10000 | ||
− | |||
End OnEvent | End OnEvent | ||
</pre> | </pre> | ||
+ | |||
Normally, event handlers run at a high priority so that once the event occurs, they run to completion. In most cases, this code should be very short as it usually takes all resources until it is complete. | Normally, event handlers run at a high priority so that once the event occurs, they run to completion. In most cases, this code should be very short as it usually takes all resources until it is complete. | ||
Line 444: | Line 407: | ||
OnEvent System.Din.2 = ON ScanTime = 5 | OnEvent System.Din.2 = ON ScanTime = 5 | ||
</pre> | </pre> | ||
+ | |||
Events can either be controlled from within the task in which they reside, or from the terminal. The main program or any subroutine can issue EventOn (to enable the OnEvent command) or EventOff (to disable it). OnEvents cannot be controlled from other tasks. | Events can either be controlled from within the task in which they reside, or from the terminal. The main program or any subroutine can issue EventOn (to enable the OnEvent command) or EventOff (to disable it). OnEvents cannot be controlled from other tasks. | ||
Line 465: | Line 429: | ||
? EventList | ? EventList | ||
</pre> | </pre> | ||
+ | |||
the result is something like the following line for each task: | the result is something like the following line for each task: | ||
Line 503: | Line 468: | ||
Example of a mutually exclusive semaphore: | Example of a mutually exclusive semaphore: | ||
<pre> | <pre> | ||
− | + | ' common shared comMutex as semaphore | |
− | + | ' defined in config.prg | |
− | |||
− | |||
Program | Program | ||
− | |||
Open COM2 BaudRate=9600 Parity=0 DataBits=8 StopBit=1 As #1 | Open COM2 BaudRate=9600 Parity=0 DataBits=8 StopBit=1 As #1 | ||
− | |||
While 1 | While 1 | ||
− | + | ' take semaphore lock serial port | |
− | |||
− | |||
If SemTake(comMutex,5000) = 1 Then | If SemTake(comMutex,5000) = 1 Then | ||
− | |||
Print #1,”Hello ”; | Print #1,”Hello ”; | ||
− | |||
Print #1,”world” | Print #1,”world” | ||
− | |||
SemGive(comMutex) ‘unlock serial port | SemGive(comMutex) ‘unlock serial port | ||
− | |||
End if | End if | ||
− | |||
End While | End While | ||
− | |||
End program | End program | ||
</pre> | </pre> | ||
+ | |||
== Synchronization Semaphores == | == Synchronization Semaphores == | ||
Synchronization semaphores are essential in producer-consumer applications where task A prepares some data, while task B consumes it. In this case, the semaphore may eliminate constant polling for ready data and save considerable CPU resources. | Synchronization semaphores are essential in producer-consumer applications where task A prepares some data, while task B consumes it. In this case, the semaphore may eliminate constant polling for ready data and save considerable CPU resources. | ||
Line 534: | Line 488: | ||
Example of Producer: | Example of Producer: | ||
<pre> | <pre> | ||
− | + | ' common shared syncSemaphore as semaphore | |
− | + | ' defined in config.prg | |
− | + | ' common shared globalA as long ' defined in config.prg | |
− | |||
− | |||
− | |||
Program | Program | ||
− | |||
Dim dummy as long | Dim dummy as long | ||
− | + | ' Semaphore is created as “full” - flush it before first use | |
− | + | dummy=semTake(syncSemaphore) ' no waiting | |
− | |||
− | dummy=semTake(syncSemaphore) | ||
− | |||
While 1 | While 1 | ||
− | + | globalA=globalA+1 ' produce some data | |
− | globalA=globalA+1 | ||
− | |||
semGive(syncSemaphore) | semGive(syncSemaphore) | ||
− | |||
sleep (100) | sleep (100) | ||
− | |||
End While | End While | ||
− | |||
End program | End program | ||
</pre> | </pre> | ||
Example of Consumer: | Example of Consumer: | ||
+ | |||
<pre> | <pre> | ||
' common shared syncSemaphore as semaphore | ' common shared syncSemaphore as semaphore | ||
− | |||
' defined in config.prg | ' defined in config.prg | ||
− | |||
' common shared globalA as long ' defined in config.prg | ' common shared globalA as long ' defined in config.prg | ||
− | |||
Program | Program | ||
− | |||
While 1 | While 1 | ||
− | |||
If SemTake(syncSemaphore,5000) = 1 Then | If SemTake(syncSemaphore,5000) = 1 Then | ||
− | |||
Print "A is "; globalA | Print "A is "; globalA | ||
− | |||
End if | End if | ||
− | |||
End While | End While | ||
− | |||
End program | End program | ||
</pre> | </pre> | ||
− | + | [[Category:MC-Basic|Programs]] | |
− | [[Category |
Latest revision as of 09:04, 8 August 2017
IMPORTANT | |
This entry is outdated and requires revision. |
Contents
Introduction
A project is all the software written for an application. The projects in MC-Basic are multitasking. They consist of many tasks running concurrently and independently of each other. The project is stored in multiple files, one file for each task. Tasks are routines that run simultaneously with many other tasks. Projects also include other files such as cam tables and record files. Projects are controlled from the ControlStudio software.
ControlStudio manages projects and handles a group of logically coupled files (programs, cam tables, etc.), while the softMC deals with separate files (PRG, CAM, etc.). The softMC does not keep project files in the flash memory and is not aware of logical coupling of files and programs.
ControlStudio automatically sets up a separate directory for each project.
Project Structure
Project files include tasks and cam tables.
Each project can contain three types of tasks:
- General-purpose task
- An optional configuration task (Config.Prg) to declare groups, programmable limit switches (PLS), cams, global variables and load of global libraries.
- An optional autoexec task (Autoexec.Prg) to automatically start the application on power-up.
Tasks
The three types of tasks are general-purpose tasks, configuration tasks, and autoexec tasks. Each type of task is outlined below.
General Purpose Tasks
General-purpose tasks are the workhorse of the softMC language. They implement the basic logic of your application. The great majority of your programming is implemented with general-purpose tasks. Each general-purpose task is divided into three sections: a task-variable section, a main program, and subroutines. The main program is further divided into three sections: a Start-up section, an OnError section, and an OnEvent section.
A task-variable definition sectionThe task-variable definition section, where all task variables are declared with the Dim…Shared command.
The main programMost programming is done in the main programming section. The main programming section falls between the Program…End Program keywords. The main program itself has three sub-sections:
The Start-up sectionThe start-up section immediately follows the Program keyword. This is where task execution begins when you start a task.
OnError sectionThe OnError section responds to errors generated by the task, allowing your program to automatically respond to error conditions and (where possible), gracefully recover and restart the machine. There is (at most) one OnError section for each task and it is normally written just before the OnEvent section.
OnEvent sectionThis section contains optional blocks of code that respond to realtime changes, such as a motor position changing or an input switch turning on. The main program can contain code to automatically respond to events. This reduces the effort required to make tasks respond quickly and easily to realtime events.
Event handlers begin with the OnEvent and end with End OnEvent keywords. One OnEvent…End OnEven keyword combination is required for each realtime event. Event handlers must be contained wholly within the main program.
Optional subroutinesEach task can have any number of subroutines. Subroutines are component parts of the task, and consequently, they can only be called from within the task. If you want to call the same subroutine from two tasks, place one copy in each task.
Configuration Task
The name of the configuration task is Config.Prg. The configuration task is used for declaration of number of axes, global variables and other constructs, such as cam tables, programmable limit switches (PLS), and group. The key to understanding the configuration task is that all data and constructs shared across tasks must be declared in the configuration task. Refer to the following figure.
The configuration task can contain a program. Axes can be renamed here.
AutoExec Task
The AutoExec task (AutoExec.Prg) is executed once on power up, just after the Configuration Task. Use AutoExec to start power-up logic. For example, you might want to use AutoExec to start a task that sets the outputs to the desired starting values. That way, the outputs are set immediately after the softMC boots, usually sooner than the PC.
For safety considerations we do not recommend starting of motion from the AutoExec task. Motion should be started by explicit operator’s request either by I/O or communication link from host PC.
Set the AutoExec task to run on power up. To do this, add the keyword Continue to the Program command. Do not include OnError or OnEvent sections in the AutoExec. Limit the AutoExec task to starting other tasks in the system. Refer to the next figure.
Program Declarations
You must declare the start of programs and subroutines. For programs, use the Program…End Program keywords. Use Sub…End Sub keywords for subroutines.
The Program…End Program keywords mark the boundary between the variable declaration section and the main program. Each task must have only one Program keyword and end with the End Program keyword.
Program ‘Standard form of program command <code for program> End Program
The AutoExec task, which must be loaded and run automatically at power-up must have CONTINUE following Program.
You pass parameters (either scalar or array) to the subroutine, which can then be used in the code of the subroutine. In the declaration line for the subroutine (SUB<name>), you declare the variable names and types of the parameters to pass. Parameters are passed either by reference or by value (ByVal). The default method is to pass parameters by reference. Arrays are passed only by reference. When you pass a variable (whether the variable is local to the task or is global) by reference, you pass the address of the variable to the subroutine, which changes the value of the variable (if the code of the subroutine is written to do this). When you pass a variable by value (ByVal) a copy of the value of the local variable is passed to the subroutine. The subroutine cannot change the value of the local variable. The syntax for defining a subroutine is:
SUB <name> ({{ByVal} <p_1> as <type_1> }…{, {ByVal} <p_n> as type_n>})
{ local variable declaration }
{ subroutine code }
END SUB
There is no explicit limit on the number of subroutines allowed in a task. All subroutines must be located following the main program, and must be contained wholly outside the main program. Subroutines can only be called from within the task where they reside. Subroutines may be recursive (call itself).
Use CALL to execute a subroutine:
CALL <subroutine>({<p_1>...<p_n>})
where:
<subroutine> is the name of the subroutine<p_1>...<p_n> are the subroutine parameters
Parentheses are not used in a subroutine if no parameters are passed.
MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any type mismatch causes an error during program loading. Automatic casting applies to numeric variables types. For example, suppose MySub is a subroutine that takes a Long parameter. In this case, the following scenario applies:
CALL MySub(3.3432) 'OK: The double value 3.3432 is demoted to the Long value, 3 CALL MySub(a) 'Error: a is a string, there is a type mismatch Call MySub(3,4) 'Error: The number of parameters is not correct
See the Subroutine Example in Appendix A for further details.
Subroutines
Parameters (either scalar or array) passed to the subroutine are used within the code of the subroutine. The declaration line for the subroutine (SUB<name>) is used to declare names and types of the parameters to pass.
Parameters are passed either by reference or by value (ByVal). The default method is to pass parameters by reference. Whole arrays are passed only by reference. Trying to pass a whole array by value results in a translation error. However, array elements can be passed both by reference and by value. The syntax for a subroutine is:
SUB <name> ({<par_1>([*])+ as <type_1>}…{, <par_n>([*])+ as<type_n>})
…
END SUB
<par_l>: name of array variable
<par_n>: name of array variable
[*]: dimension of an array without specifying the bounds
+: means one or more [*]
SUB CalculateMean(x[*][*] as DOUBLE, TheMean[*] as LONG) DIM sum as DOUBLE DIM I as LONG FOR i = 1 to 100 sum = sum + x[i][1] NEXT i TheMean[1] = sum/100 END SUB SUB PrintMean(ByVal Mean as LONG) PRINT “Mean Value Is “, Mean END SUB CALL CalculateMean(XArray, TheMeanArray) ‘ Pass entire array by reference CALL PrintMean(TheMeanArray[1]) ‘ Pass a single array element by value
When a variable is passed by reference (whether the variable is local to the task or global), the address of the variable is passed to the subroutine, which changes the value of the original variable (if the code of the subroutine is written to do this).
When a variable is passed by value (ByVal) a local copy of the value of the variable is passed to the subroutine, and the subroutine cannot change the value of the original variable.
There is no explicit limit on the number of subroutines allowed in a task. All subroutines must be located following the main program and must be contained wholly outside the main program. Subroutines can only be called from within the task where they reside. Subroutines may be recursive (can call itself). Use CALL to execute a subroutine with the following syntax:
CALL <subroutine_name>{(<par_1>{, …<par_n>})}
Parentheses are not used in subroutine CALL if no parameters are passed.
MC-Basic automatically checks the type of compliance between the subroutine declaration and the subroutine call. Any mismatch (in number of parameters or parameters types) causes an error during program loading. Automatic type casting applies only for “by value” long and double parameters.
SUB MySub(RefPar as long, byval ValPar as double) … END SUB CALL MySub(LongVar, “String”) -> type mismatch in second parameter CALL MySub(LongVar) -> wrong number of parameters CALL MySub(LongVar, 2) -> a valid type casting for a by-value parameter CALL MySub(DoubleVar, 2.2) -> invalid type casting for a by-ref parameter
User-Defined Functions
MC-Basic allows the definition of user functions to be used in programs in the same manner as using BASIC pre-defined functions. User-defined functions are composed with multiple lines and may be recursive (can call itself). Unlike BASIC system functions, the scope of user-defined functions is limited to the task in which it is defined.
Functions are different from subroutines in one respect. Functions always return a value to the task that called the function. Otherwise, functions and subroutines use the same syntax and follow the same rules of application and behavior.
Because functions return a value, function calls should be treated as expressions. Therefore, function called can be combined within print commands, assignment statements, mathematical operations and conditions of flow control statements. They can also be passed as by-value parameters of system or user-defined functions and subroutines.
PRINT <function_name>{(<par_1>{, …<par_n>})} <variable_name> = <function_name>{(<par_1>{, …<par_n>})} IF <function_name>{(<par_1>{, …<par_n>})} > 10 THEN ? LOG( <function_name>{(<par_1>{, …<par_n>})} )
Parentheses are not used in function CALL if no parameters are passed. Parameters (either scalar or array) passed to the function are used within the code of the function. Declare variable names and types of parameters in the declaration line of the function. Parameters can be passed by reference or by value. The default is by reference.
Arrays can only be passed by reference. Trying to pass a whole array by value results in a translation error. On the other hand, array elements can be passed by reference and by value.
To set up the return value, assign the return value to a virtual local variable with the same name as the function somewhere within the code of the function. This local variable is declared automatically during function declaration and the function uses it to obtain the return value.
There is no explicit limit on the number of functions allowed in a task. All functions must be located following the main program and must be contained wholly outside of the main program.
MC-Basic automatically checks the type of compliance between the function declaration and the function call. Any mismatch (in number of parameters, in parameters and returned value types) causes an error during program loading. Automatic type casting applies only for long and double returned values and “by value” parameters. For example:
Function LongReturnFunc(…) As Long LongReturnFunc = “String” -> type mismatch in returned value End Function Function LongReturnFunc(…) As Long LongReturnFunc = 43.7 -> valid type casting in returned value End Function
A function can be recursive (can call itself). The following example defines a recursive function to calculate the value of N:
FUNCTION Factorial (ByVal N As Long) As Double 'By declaring N to be long, this truncates floating point numbers 'to integers 'The function returns a Double value If N < 3 then 'This statement stops the recursion Factorial = N '0!=0; 1!=1' 2!=2 Else Factorial=N * Factorial(N-1) 'Recursive statement End If END FUNCTION
When writing a recursive function, you must have an IF statement to force the function to return without the recursive call being executed. Otherwise, the function never returns once it is called.
Arrays
Arrays can only be passed by reference. If a user tries to pass a whole array by value, the translator gives an error. Array syntax is:
SUB <name> ({<p_1>([*])+ as <type_1>}…{, <p_n>([*])+ as <type_n>})
{ local variable declaration }
{ subroutine code }
END SUB
where
<p_l> : name of array variable
<p_n> : name of array variable
[*] : dimension of array without specifying the bounds
+ : means one or more
Syntax example:
SUB mean(x[*][*] as DOUBLE, TheMean[*] as LONG) DIM sum as DOUBLE DIM I as LONG FOR i = 1 to 100 sum = sum + x[i][1] NEXT i TheMean[1] = sum/100 END SUB
Multitasking
The softMC supports multitasking. You can have multiple tasks running independently, sharing a single computer. A task is a section of code that runs in its own context. Microsoft Windows is a multitasking system. If you open Explorer and Word at the same time, they run nearly independently of each another.
In this case, both Explorer and Word have their own contexts. They share one computer, but run as if the other were not present. There is inter-task communication. If you double-click on a document in the file manager, it launches Word to edit the file you clicked.
With MC-Basic, you can use different tasks to control different operational modes: one for power up, one for set-up, one for normal operation, and another for when problems occur. Like Windows, each task can run independently of the others, and you can prescribe interactions between tasks.
Multitasking is used when you want multiple processes to run largely independent of each other. For example, if you are using the softMC to interface to the operator, you will usually use a separate task to execute the interface code. Another example is when two parts of a machine are largely independent of each other. There is usually some control required between tasks as one task may start or stop another.
If a machine is simple to control, you should try to keep the entire program in one task (in addition to Config.Prg). If you do need to use multitasking, you should keep a highly structured architecture. It is recommended that you limit use of the main task for axis and group set up, machine initialization, and controlling the other tasks. Normal machine operation should be programmed in other tasks. For example, Main.Prg might be limited to setting up axes, and then starting Pump.Prg, Conveyor.Prg, and Operator.Prg.
Do not split control of an axis or group across tasks. You can put control for multiple axes in one task. Ideally, you should use multiple tasks for machines where different sections operate more or less independently. You can also use tasks to implement different operational modes.
Multitasking is a powerful tool, but it carries a cost. It is easy to make errors that are difficult to find. When multiple tasks are running concurrently, complex interaction is difficult to understand and recreate. Limit the use of tasks to situations where they are needed.
Do not create a task as a substitute for an event handler. Events and tasks are not the same. MC-Basic supports event handlers to respond to realtime events. Events are similar to interrupts in microprocessor systems. They normally run at higher priorities than the programs that contain them. They are ideal for quick responses to realtime events. Add the event handler to an existing task to respond to an event.
Do not use tasks in place of subroutines. Remember that when you start a task, the original task continues to run. When you call a subroutine, you expect the calling program to suspend execution until the subroutine is complete. The behavior of tasks where the two routines continue to execute can cause complex problems.
Knowing when to use multitasking and when to avoid it requires some experience. If you are new to multitasking, you may want to start slow until you are familiar with how it affects program structure. When you start a new project, ControlStudio creates the main task as part of opening a new project. After that process is complete, you can add a new task to your project by selecting File, New. You can also press the new task button on the ControlStudio tool bar.
Loading the Program
ControlStudio automatically loads all tasks in your project when you select Run Project. You can select Run Project by selecting it from the Debug menu, by pressing the F5 key, or by pressing the “Load Task”and “Run Task” buttons on the tool bar. By default, tasks are loaded from the host PC to the softMC at a low priority (Priority = 16).
When you select Run Task, the project’s main task is started at the lowest priority (Priority = 16). You can change the priority of the main task by selecting View-> Project Manager->Options and then changing the priority in the bottom of the window. If you structure your software so that the main program loads all other tasks, the Run Project button starts your machine.
Preemptive Multitasking & Priority Levels
Because many tasks share one processor, you must carefully design your system so tasks get processing resources when they need them. You do not want the operator interface taking all the resources when you need fast response to a realtime event. The operating system provides system resources based on two criteria: task priority level and time slice.
When you create a program, you must select the task priority level. MC-Basic allows you to specify 16 levels of priority. The task with the highest priority takes all the system resources it can use. In fact, no task of a lower priority receives any resources until all tasks of higher priority relinquish them. Most systems have one main task that runs at a medium priority and perhaps a background task that runs at a low priority, with a few high priority tasks. At every time slice, the softMC reevaluates which task has the highest priority and assigns resources to it.
The ControlStudio terminal window relies on the softMC command line task, which runs at priority 2. If you start a task with priority 1, the terminal will not be available until the task is complete or idle. Consequently, you will not be able to communicate with the softMC and you may have to power-down the system to recover the terminal window. You can optionally set the priority of a task when you start it. For example:
StartTask Aux.Prg Priority=6
The default priority of events is 1 (highest) and the default priority of programs is 16.
Time Slice is a method by which the operating system divides up resources when multiple tasks share the same priority level. The softMC provides the first task with one time slice, the next time slice goes to the second, the next to the third, and so on. The time slice is currently one millisecond duration. This method is sometimes called round robin scheduling.
Inter-Task Communications and Control
Tasks can control one-another. In fact, any task can start, continue, idle, or kill any other task, regardless of which task has the higher priority. For detailed information on these commands, refer to the Reference Manual.
StartTask starts tasks from the main task. For testing, you can use STARTTASK from the terminal window. Do not use STARTTASK in AutoExec.Prg. The syntax of STARTTASK is:
StartTask <TaskName> {Priority = <Level>}{NumberOfLoops = <Loop Count>}
NOTE | |
NOL is a short form for NumberOfLoops. |
where:
<Level> is a long with value between 1 and 16. If <Level> is not entered, it defaults to 16, the lowest priority. Priority = 1 is the highest priority.
<Loop Count> is either -1 (indicating unlimited number of loops) or between 1 and 32768 indicating the number of times the task is executed. If <Loop Count> is not entered, it defaults to 1.
For example:
StartTask Task1.Prg Priority=8 NumberOfLoops = -1 'Run Task1 forever StartTask Main.Prg NOL=1 'Run Main once
IdleTask stops the task at the end of the line currently being executed and idles all its events. An idled task can be continued (using CONTINUETASK) or terminated (using KILLTASK). IDLETASK does not stop motion currently being executed. This is significant because all the events are idled and cannot respond to an axis' motion. Tasks can be idled explicitly by other tasks, but cannot idel itself. This command is issued from a task or the terminal window. The syntax of IdleTask is:
IdleTask <TaskName>
For example:
IdleTask TASK1.PRG
Tasks that have been idled with IDLETASK are restarted only with CONTINUETASK. The command continues an idled task from the point at which it was stopped, or continues task execution after a break point has been reached. It is frequently used to restart tasks from the ONERROR error handler. If a run time error has occured, CONTINUETASK retries the line which caused the error. The error must be corrected before the task continues. This command is issued from any task or the terminal window. The syntax of ContinueTask is:
ContinueTask <TaskName>
For example:
ContinueTask TASK1.PRG
KillTask aborts the execution of a task. The program pointer is left on the line at which the task was stopped. The first action of KILLTASK is to kill and delete all events associated with the task. This is done to ensure that no event initiates an action after KILLTASK was executed. KILLTASK is issued from the terminal window. The syntax of the KillTask is:
KillTask <TaskName>
For example:
KillTask TASK1.PRG
Monitoring Tasks From the Terminal
For detailed information on these commands, refer to the Reference Manual.
TASK.STATUS provides the current state of any task. You can query TASK.STATE from the terminal window. You cannot use TASK.STATUS from within a program. The syntax for TASK.STATUS is:
? <TaskName>.Status
For example:
? TASK1.PRG.Status
TASKLIST returns the state and priority of all tasks loaded in the system. You can query TASKLIST only from the terminal window.
For example, if you type:
? TaskList
A typical result is:
TaskName = TASK1.PRG, Status = sstep, Priority=16 TaskName = DO.PRG, Status = suspend, Priority=4
Relinquishing Resources
When tasks of different priorities compete for processor time, the highest priority task always takes all the resources it needs. However, tasks of high priority can relinquish computer resources under some conditions. In these cases, tasks of lower priority run until the high priority tasks again demand the resources. There are three conditions under which a task relinquishes resources: when the task is terminated, when the task is suspended, or when the task is idled. For detailed information on these commands, refer to the Reference Manual.
A task terminates when it is finished executing. If a task starts with NUMBEROFLOOPS greater than zero, the task executes the specified number of times and terminates. The task relinquishes all resources. Terminated tasks remain loaded in the system and can be restarted.
One task can terminate another task by issuing KILLTASK. A task relinquishes all resources after the kill command. Killed tasks remain in the system and can be restarted.
Tasks relinquish processing resources temporarily when they are suspended. A task is suspended when it is waiting for a resource or is delayed. Suspended tasks still monitor events as long as the event priority is higher than the task priority. Never run a task at a higher priority level than any of its events.
Use SLEEP to delay a task for a specific period of time. This command can only be issued from within the task. One task cannot issue a SLEEP for another task. SLEEP causes the task to relinquish resources until the sleep time has expired.
Idled tasks relinquish resources. In this case, resources are relinquished until another task revokes the idle by issuing a CONTINUETASK.
Delete Task/Library deletes a file from the Flash Disk. Only filesnot loaded into RAM can be deleted.Files that are protected by a password may not be deleted. For example:
Delete FILE1.PRG
Event Handler
The main program can contain sections which automatically handle events. This reduces the programming effort required to make tasks respond quickly and easily to realtime events. Event handlers begin with OnEvent and end with End OnEvent and occur just after the Program keyword.
After OnEvent is loaded, turn the event On with EventOn just after the End OnEvent keyword (enable immediately). However, you can enable and disable OnEvent at any time using EventOn and EventOff. Multiple OnEvents can be written sequentially. The softMC system can support up to 64 events. The number of OnEvent(s) in a single task is not restricted, so a task may have from 0 to 64 OnEvent(s).
It is important to understand that OnEvents are different from ordinary tasks. OnEvents are preemptive within the task. That is, an OnEvent runs until complete and the program execution returns to the main program. While an OnEvent is executing, it does not release CPU time to the parent task or any other task. In this sense, OnEvents are similar to interrupt calls. They run to completion before execution returns to the main program. An OnEvent must have a higher priority than its parent task to ensure that when an event occurs, it interrupts its parent task and runs to completion. The rules are valid for a single process, (that is, the parent task and its events), while events of the respective tasks in the system share the CPU among themselves.
OnEvent
The syntax of OnEvent is:
OnEvent [EventName] [Condition] {Priority=EventPriority}{ScanTime=time}
where
EventName is any legal name that is otherwise not used in the task.
Condition is any logical expression such as System.Dout.1 = 1. The event fires on transitions of the condition from false to true.
Priority is an integer from 1 (highest) to 16 (lowest). If not entered, priority defaults to 1. The priority should always be higher than the priority of the task or the event never runs.
Time is an integer indicating the number of cycles between each scan. Time defaults to 1.
In this example, an event is set up to move axis "X-axis" to 10000 counts each time an input goes high:
OnEvent MOVE_ON_TRIGGER System.Din.1=ON Move X-axis 10000 End OnEvent
Normally, event handlers run at a high priority so that once the event occurs, they run to completion. In most cases, this code should be very short as it usually takes all resources until it is complete.
ScanTime is in cycles of the SERCOS update rate. This is normally 2 or 4 milliseconds. Setting ScanTime to 5 configures the system to check the event condition every 10 or 20 milliseconds, depending on your update rate. For example:
OnEvent System.Din.2 = ON ScanTime = 5
Events can either be controlled from within the task in which they reside, or from the terminal. The main program or any subroutine can issue EventOn (to enable the OnEvent command) or EventOff (to disable it). OnEvents cannot be controlled from other tasks.
EventOn
EventOn enables OnEvent. The syntax of EventOn is:
EventOn [Event Name]
EventOn must come after the definition of the OnEvent.
EventOff
EventOff disables OnEvent. The syntax of EventOff is:
EventOff [Event Name]
Refer to the MC Reference Manual for information additional information about OnEvent, EventOn, and EventOff.
EventList
EventList provides a complete list of all events in all tasks with their name, the task name, the priority, whether the event is armed, and current status. EventList is valid only from the terminal window. For example:
? EventList
the result is something like the following line for each task:
Name = IOEvent Owner=Task1 Edge=1 Status=1 Scan=1 Priority=5 Action = Stop
where:
edge=1 indicates the event is armed (that is, the condition is false so that the condition becoming true will fire the OnEvent)
status=1 means the event is enabled
EventDelete
Deletes the specified event. The event does not respond to the specified condition until the task is executed again and the event is enabled.
EventDelete EVENT1
Events at Start-up
Events are normally triggered by the OnEvent condition changing from false to true. So a single transition in the condition is required to run OnEvent. One exception to this is start-up. At start-up, if the condition is true, OnEvent executes once, even if there has not been a transition.
Program Flow and OnEvent
You can use GoTo commands within an OnEvent block of code. However, because OnEvent interrupts the main program, you cannot use GoTo to branch out of the event handler or into the event handler. You cannot place OnEvent…End OnEvent in the middle of program flow commands (e.g., For…Next, If…Then, etc.). You cannot declare or use local variables inside an OnEvent block.
Semaphores
Semaphores are the basis for synchronization and mutual exclusion. The difference is that the mutual exclusion semaphore is created as “full” or “1”, while the synchronization semaphore is empty “0”. If the semaphore is used for protecting mutual resources, it is taken before accessing the resource and releases at the end. A synchronization semaphore is given by the producer and taken (consumed) by the consumer.
NOTE | |
A semaphore is created as “full.” |
Global semaphore are defined with COMMON SHARED in CONFIG.PRG or from the command line. Since a semaphore's purpose is to protect data among tasks, there is no meaning to local semaphores.
A semaphore is given/released by SEMAPHOREGIVE and taken/consumed by SEMAPHORETAKE. It is possible to specify a time out of up to 5000 ms. SEMAPHORETAKE acquires a semaphore and returns before timeout or does not acquire a semaphore and returns after timeout.
NOTE | |
Mutual exclusion semaphores are taken and given by the same task. Synchronization semaphores are given by one task and taken by another task. |
Mutual Exclusion Semaphores
Mutual exclusion semaphores lock resources by taking a semaphore. Another task(s) competing for the same resource is blocked until the semaphore is released.
Example of a mutually exclusive semaphore:
' common shared comMutex as semaphore ' defined in config.prg Program Open COM2 BaudRate=9600 Parity=0 DataBits=8 StopBit=1 As #1 While 1 ' take semaphore lock serial port If SemTake(comMutex,5000) = 1 Then Print #1,”Hello ”; Print #1,”world” SemGive(comMutex) ‘unlock serial port End if End While End program
Synchronization Semaphores
Synchronization semaphores are essential in producer-consumer applications where task A prepares some data, while task B consumes it. In this case, the semaphore may eliminate constant polling for ready data and save considerable CPU resources.
Example of Producer:
' common shared syncSemaphore as semaphore ' defined in config.prg ' common shared globalA as long ' defined in config.prg Program Dim dummy as long ' Semaphore is created as “full” - flush it before first use dummy=semTake(syncSemaphore) ' no waiting While 1 globalA=globalA+1 ' produce some data semGive(syncSemaphore) sleep (100) End While End program
Example of Consumer:
' common shared syncSemaphore as semaphore ' defined in config.prg ' common shared globalA as long ' defined in config.prg Program While 1 If SemTake(syncSemaphore,5000) = 1 Then Print "A is "; globalA End if End While End program