Commit 607b460d authored by Mart Lubbers's avatar Mart Lubbers
Browse files

Merge branch 'preload' into 'master'

Preload

See merge request !9
parents 09bf5b56 af71a78f
Pipeline #53981 passed with stage
in 1 minute and 22 seconds
Subproject commit dc91d25586178b40d50eae51a8ba82b831b91d1d
Subproject commit b2a59c3c8842278534aeb084465292caf072799b
......@@ -2,7 +2,7 @@ definition module Data.UInt
from Data.GenHash import generic gHash
from Data.GenDefault import generic gDefault
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Text.GenJSON import generic JSONDecode, generic JSONEncode, :: JSONNode
from GenEq import generic gEq
......
......@@ -124,9 +124,9 @@ gEditor{|Int8|} ep = boundedIntEditor INT8_MIN INT8_MAX ep "Enter an 8-bit integ
gEditor{|Int16|} ep = boundedIntEditor INT16_MIN INT16_MAX ep "Enter a 16-bit integer"
gEditor{|Int32|} ep = boundedIntEditor INT32_MIN INT32_MAX ep "Enter a 32-bit integer"
boundedIntEditor :: a a EditorPurpose String -> Editor a (?a) | fromInt, toInt, toString a
boundedIntEditor :: a a EditorPurpose String -> Editor a (EditorReport a) | fromInt, toInt, toString a
boundedIntEditor min max ViewValue hint
= mapEditorWrite (const ?None)
= mapEditorWrite (const EmptyEditor)
$ mapEditorRead toString textView
boundedIntEditor min max EditValue hint
= mapEditorWrite (fmap fromInt)
......
......@@ -39,10 +39,15 @@ from mTask.Interpret.Device import
withDevice`,
liftmTask,
liftmTaskWithOptions,
preloadTask,
preloadTaskWithOptions,
preloadTasks,
preloadTasksWithOptions,
mTaskSafe,
deviceSpecification,
class channelSync,
:: MTDevice,
:: MTaskBox(..),
:: Channels, :: MTMessageTo, :: MTMessageFro
from Data.UInt import
:: UInt8, instance toString UInt8
......
......@@ -13,7 +13,7 @@ from GenType.CSerialise import generic gCSerialise, generic gCDeserialise, :: CD
from StdOverloaded import class toString
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.WF.Definition import :: TaskException, :: TaskValue, class iTask
from mTask.Language import :: Gesture, :: Pin, :: APin, :: DPin, :: Button, :: ButtonStatus, :: Long, class type, :: PinMode, :: InterruptMode
......
......@@ -4,7 +4,7 @@ from StdOverloaded import class zero
from GenType.CSerialise import generic gCSerialise, generic gCDeserialise, :: CDeserialiseError
from Data.Either import :: Either
from iTasks.WF.Definition import class iTask, :: TaskValue, :: Task
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenEq import generic gEq
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
......
......@@ -9,7 +9,7 @@ from Data.GenEq import generic gEq
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from iTasks.SDS.Definition import class RWShared, class Readable, class Writeable, class Modifiable, class Registrable, class Identifiable
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.WF.Definition import :: Task, :: TaskValue
from iTasks.WF.Definition import class iTask
......@@ -23,6 +23,7 @@ from mTask.Interpret.Specification import :: MTDeviceSpec
from mTask.Language import :: Main, class type
:: Channels :== ([MTMessageFro], [MTMessageTo], Bool)
:: MTaskBox = E.u: MTB (Main (BCInterpret (TaskValue u))) & type u
:: MTDevice
class channelSync a :: a (sds () Channels Channels) -> Task () | RWShared sds
......@@ -57,6 +58,40 @@ liftmTask :: (Main (BCInterpret (TaskValue u))) MTDevice -> Task u | type u
*/
liftmTaskWithOptions :: CompileOpts (Main (BCInterpret (TaskValue u))) MTDevice -> Task u | type u
/**
* Prepare a task for preloading.
*
* @param mTask task
* @result the code to put in preloaded_task.h
*/
preloadTask :: (Main (BCInterpret (TaskValue u))) -> Task String | type u
/**
* Prepare a task for preloading and use the compilation options provided
*
* @param compilation options
* @param mTask task
* @result the code to put in preloaded_task.h
*/
preloadTaskWithOptions :: CompileOpts (Main (BCInterpret (TaskValue u))) -> Task String | type u
/**
* Prepare a list of boxed tasks for preloading.
*
* @param mTask tasks boxed in MTaskBox types
* @result the code to put in preloaded_task.h
*/
preloadTasks :: [MTaskBox] -> Task String
/**
* Prepare a list of boxed tasks for preloading and use the compilation options
* provided
*
* @param mTask tasks boxed in MTaskBox types
* @result the code to put in preloaded_task.h
*/
preloadTasksWithOptions :: CompileOpts [MTaskBox] -> Task String
/**
* Wraps a task and catches mTask exceptions
*
......
......@@ -6,22 +6,25 @@ import Control.Applicative
import Data.Either
import Data.Func
import Data.Functor
import Data.GenHash
import Data.List => qualified group
import Data.Map => qualified union, difference, find, updateAt, get
import Data.Map.GenJSON
import Data.Tuple
import Data.UInt
import Text
import Text.HTML
import System.Time
import iTasks
import mTask.Interpret.ByteCodeEncoding
import mTask.Interpret.Message => qualified :: MTask
import mTask.Interpret.Message
import mTask.Interpret.DSL
import mTask.Interpret.Compile
import mTask.Interpret.String255
import mTask.Interpret.VoidPointer
import mTask.Interpret
import System.Time
import Data.UInt
derive class iTask MTDeviceData, MTaskStatus
......@@ -64,7 +67,7 @@ withDevice` verbose device devfun =
,(Embedded
, \stl-> appendTask Embedded (\_->catchAll
(channelSync device channels <<@ NoUserInterface)
(\e->throw (MTESyncException (String255 (e % (0, 255))))) @! ?None) stl
(throw o MTESyncException o fromString) @! ?None) stl
>>- \cstid->appendTask Embedded (\_->processChannels dev sdsupdates channels <<@ NoUserInterface @! ?None) stl
>>- \pctid->watch dev
>>* [OnValue $ ifValue (\s->isJust s.deviceSpec)
......@@ -144,55 +147,124 @@ mTaskSafe t = try (Right <$> t) \e->case e of
//Task specific errors
e = return (Left e)
preloadTasks :: [MTaskBox] -> Task String
preloadTasks ts = preloadTasksWithOptions zero ts
preloadTasksWithOptions :: CompileOpts [MTaskBox] -> Task String
preloadTasksWithOptions opts tasks = header <$> ptwo [zero..] tasks footer
>>- viewInformation [ViewAs \a->PreTag [] [Text a]] o join "\n"
where
ptwo :: [UInt8] [MTaskBox] [String] -> Task [String]
ptwo _ [] c = return c
ptwo [n:ns] [(MTB t):ts] c = mkMessage n opts t \_ td
# td & status = MTNeedsInit
# bytes = join ", " [toString c\\c<-gCSerialise{|*|} td []]
= ptwo ns ts
[ concat5 "#define PRELOADED_HASH" (toString n) " " (toString $ gHash{|*|} bytes) "ll"
, concat5 "static const uint8_t preloaded_task" (toString n) "[] PROGMEM = {" bytes "};"
: c]
footer =
[ concat3 "static const uint8_t *const preloaded_tasks[PRELOADED_TASKS_NUM] PROGMEM = {" (join ", " ["preloaded_task" +++ toString n\\n<-[0..length tasks-1]]) "};"
, concat3 "static const int64_t preloaded_hashes[PRELOADED_TASKS_NUM] PROGMEM = {" (join ", " ["PRELOADED_HASH" +++ toString n\\n<-[0..length tasks-1]]) "};"
, ""
, "#ifdef __cplusplus"
, "}"
, "#endif /* __cplusplus */"
, ""
, "#endif /* !PRELOADED_TASKS_H */"
, ""
]
header s =
[ "/** @file preloaded_tasks.h"
, " *"
, " * Contains the binary data for the preloaded tasks"
, " */"
, "#ifndef PRELOADED_TASKS_H"
, "#define PRELOADED_TASKS_H"
, ""
, "#include \"preload.h\""
, ""
, "#ifdef __cplusplus"
, "extern \"C\" {"
, "#endif /* __cplusplus */"
, ""
, "#define PRELOADED_TASKS_NUM " +++ toString (length tasks)
, ""
: s]
preloadTask :: (Main (BCInterpret (TaskValue u))) -> Task String | type u
preloadTask a = preloadTaskWithOptions zero a
preloadTaskWithOptions :: CompileOpts (Main (BCInterpret (TaskValue u))) -> Task String | type u
preloadTaskWithOptions opts task = preloadTasksWithOptions opts [MTB task]
mkMessage :: UInt8 CompileOpts (Main (BCInterpret (TaskValue u)))
([?MTLens] MTaskMeta -> Task b)
-> Task b | type u
mkMessage sid opts task f
# (returnvalue, shares, hardware, instructions) = compileOpts opts task
# (mayberefs, shares) = unzip shares
= sequence shares >>- \shares->f mayberefs
{ taskid = sid
, tree = VoidPointer ()
, stability = MTNoValue
, value = returnvalue
, peripherals = hardware
, shares = {i\\i<-shares}
, instructions = BCIs {i\\i<-instructions}
, status = MTNeedsInit
, execution_min = UInt32 0
, execution_max = UInt32 0
, lastrun = UInt32 0
}
liftmTask :: (Main (BCInterpret (TaskValue u))) MTDevice -> Task u | type u
liftmTask a b = liftmTaskWithOptions zero a b
taskAccepted MTSPrepack = True
taskAccepted MTSAcked = True
taskAccepted (MTSValue _) = True
taskAccepted (MTSException _) = True
taskAccepted _ = False
liftmTaskWithOptions :: CompileOpts (Main (BCInterpret (TaskValue u))) MTDevice -> Task u | type u
liftmTaskWithOptions opts task (MTDevice dev sdsupdates channels)
# (returnvalue, shares, hardware, instructions) = compileOpts opts task
# (mayberefs, shares) = unzip shares
# taskshare = mapReadWrite
(\ s->s.deviceTasks
,\t s-> ?Just {s & deviceTasks=t}
) ?None dev
= get dev
//Compile
>>- \st=:{deviceIds=[sid:rest]}->upd (\s->{s & deviceTasks=put sid MTSInit s.deviceTasks, deviceIds=rest}) dev
//Make sure the task is deleted even if we are destroyed (e.g. from a step)
>>- \_->let taskView = sdsFocus sid $ mapLens "taskView" taskshare ?None
>-| let taskView = sdsFocus sid $ mapLens "taskView" taskshare ?None
in withCleanupHook (sendMessage (MTTTaskDel sid) channels)
//Resolve shares
$ sequence shares
$ mkMessage sid opts task \mayberefs msg->
//Add task to sdsupdates map
>>- \shares->upd (put sid []) sdsupdates
upd (put sid []) sdsupdates
//Send the prep
>>- \_->sendMessage (MTTTaskPrep sid) channels
>-| sendMessage (MTTTaskPrep {taskid=sid, hash=0}) channels
//Wait for the prepack
>>- \_->watch taskView
>>* [OnValue $ ifValue (\t->t=:MTSPrepack || t =: MTSAcked || t =: (MTSValue _) || t =: (MTSException _))
\_->sendMessage (MTTTask
{ taskid = sid
, tree = VoidPointer ()
, stability = MTNoValue
, value = returnvalue
, peripherals = hardware
, shares = {i\\i<-shares}
, instructions = BCIs {i\\i<-instructions}
, status = MTUnevaluated
, execution_min = UInt32 0
, execution_max = UInt32 0
, lastrun = UInt32 0
}) channels]
>-| watch taskView
>>* [OnValue $ ifValue taskAccepted \t->case t of
//If it was loaded from cache we don't need to send anything
MTSAcked -> return ()
MTSValue _ -> return ()
MTSException _ -> return ()
_ -> sendMessage (MTTTask msg) channels @! ()
]
//Wait for task ack
>>- \_->watch taskView
>-| watch taskView
>>* [OnValue $ ifValue (\t->t =: MTSAcked || t =: (MTSValue _) || t =: (MTSException _))
\_-> waitForReturn taskView
-|| allTasks [watchShareUpstream sid ish sh\\(?Just ish)<-mayberefs & sh<-shares]
-|| watchSharesDownstream mayberefs shares sid
-|| allTasks [watchShareUpstream sid ish sh\\(?Just ish)<-mayberefs & sh<-:msg.shares]
-|| watchSharesDownstream mayberefs [i\\i<-:msg.shares] sid
] @? \tv->case tv of
NoValue = NoValue
Value v _ = v
where
taskshare = mapReadWrite
(\ s->s.deviceTasks
,\t s-> ?Just {s & deviceTasks=t}
) ?None dev
//waitForReturn :: UInt8 -> Task u^
waitForReturn taskView = watch $ flip mapReadError taskView \s->case s of
MTSException e = Error (exception e)
......
......@@ -2,7 +2,7 @@ definition module mTask.Interpret.Device.MQTT
from mTask.Interpret.Device import class channelSync
from iTasks.WF.Definition import class iTask, :: Task
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenEq import generic gEq
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
......@@ -19,4 +19,4 @@ derive class iTask MQTTSettings
generateServerId :: Task String
instance channelSync MQTTSettings
\ No newline at end of file
instance channelSync MQTTSettings
......@@ -2,7 +2,7 @@ definition module mTask.Interpret.Device.TCP
from mTask.Interpret.Device import class channelSync
from iTasks.WF.Definition import class iTask
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenDefault import generic gDefault
from Data.GenEq import generic gEq
......
definition module mTask.Interpret.Devices
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from iTasks.UI.Editor.Generic import generic gEditor, :: EditorPurpose, :: Editor
from iTasks.UI.Editor.Generic import generic gEditor, :: EditorPurpose, :: Editor, :: EditorReport
from Data.GenEq import generic gEq
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
from iTasks.WF.Definition import class iTask, :: Task
......
definition module mTask.Interpret.Instructions
from iTasks.WF.Definition import class iTask
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenDefault import generic gDefault
from Data.GenEq import generic gEq
......
......@@ -4,7 +4,7 @@ from StdOverloaded import class toString
from GenType.CSerialise import generic gCSerialise, generic gCDeserialise, :: CDeserialiseError
from Data.Either import :: Either
from iTasks.WF.Definition import class iTask
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenEq import generic gEq
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
......@@ -20,9 +20,11 @@ from mTask.Interpret.VoidPointer import :: VoidPointer
:: MTMessageTo
//* taskid returnwidth peripherals shares instructions
= MTTTask MTask
= MTTTask MTaskMeta
//* taskid (task prep for devices that have small comm buffers)
| MTTTaskPrep UInt8
//* @param hash of the task
//* @param taskid
| MTTTaskPrep MTaskPrepData
//* taskid
| MTTTaskDel UInt8
//*
......@@ -32,7 +34,9 @@ from mTask.Interpret.VoidPointer import :: VoidPointer
//* taskid sdsid sds value
| MTTSdsUpdate UInt8 UInt8 String255
:: MTask =
:: MTaskPrepData = { taskid :: UInt8, hash :: Int }
:: MTaskMeta =
{ taskid :: UInt8
, tree :: VoidPointer
, stability :: MTaskValueState
......@@ -45,12 +49,15 @@ from mTask.Interpret.VoidPointer import :: VoidPointer
, execution_max :: UInt32
, lastrun :: UInt32
}
:: MTaskValueState = MTNoValue | MTUnstable | MTStable | MTRemoved
:: MTaskValueState = MTNoValue | MTUnstable | MTStable
// MTEvaluated: task tree is evaluated and the previously calculated execution interval is still correct
// MTPurged: task tree is evaluated, but the execution interval needs to be recalculated
// MTUnevaluated: task tree contains unevaluated parts, the execution interval is [0,0]
:: MTaskEvalStatus = MTEvaluated | MTPurged | MTUnevaluated
//* Status of the task
:: MTaskEvalStatus
= MTEvaluated //* task tree is evaluated and the previous execution interval is still correct
| MTPurged //* task tree is evaluated, but the execution interval needs to be recalculated
| MTUnevaluated //* task tree contains unevaluated parts, the execution interval is [0,0]
| MTRemoved //* task is removed
| MTNeedsInit //* task doesn't have a tasktree yet
:: MTMessageFro
//* taskid
......@@ -85,8 +92,8 @@ from mTask.Interpret.VoidPointer import :: VoidPointer
| MTERTSError
| MTEUnexpectedDisconnect
derive class iTask MTMessageFro, MTMessageTo, MTException, MTaskValueState, MTaskEvalStatus
derive gCSerialise MTMessageFro, MTMessageTo, MTException, TaskValue, MTaskValueState, MTaskEvalStatus
derive gCDeserialise MTMessageFro, MTMessageTo, MTException, TaskValue, MTaskValueState, MTaskEvalStatus
derive class iTask MTMessageFro, MTMessageTo, MTException, MTaskMeta, MTaskValueState, MTaskEvalStatus, MTaskPrepData
derive gCSerialise MTMessageFro, MTMessageTo, MTException, MTaskMeta, TaskValue, MTaskValueState, MTaskEvalStatus, MTaskPrepData
derive gCDeserialise MTMessageFro, MTMessageTo, MTException, MTaskMeta, TaskValue, MTaskValueState, MTaskEvalStatus, MTaskPrepData
instance toString MTException
......@@ -9,7 +9,7 @@ import mTask.Interpret.Instructions
import mTask.Interpret.Compile
import mTask.Interpret.VoidPointer
derive class iTask MTMessageFro, MTMessageTo, MTException, MTask, MTaskValueState, MTaskEvalStatus
derive gCSerialise MTMessageFro, MTMessageTo, MTException, MTask, TaskValue, MTaskValueState, MTaskEvalStatus
derive gCDeserialise MTMessageFro, MTMessageTo, MTException, MTask, TaskValue, MTaskValueState, MTaskEvalStatus
derive class iTask MTMessageFro, MTMessageTo, MTException, MTaskMeta, MTaskValueState, MTaskEvalStatus, MTaskPrepData
derive gCSerialise MTMessageFro, MTMessageTo, MTException, MTaskMeta, TaskValue, MTaskValueState, MTaskEvalStatus, MTaskPrepData
derive gCDeserialise MTMessageFro, MTMessageTo, MTException, MTaskMeta, TaskValue, MTaskValueState, MTaskEvalStatus, MTaskPrepData
instance toString MTException where toString e = toSingleLineText e
......@@ -5,7 +5,7 @@ from StdOverloaded import class toString
from GenType.CSerialise import generic gCSerialise, generic gCDeserialise, :: CDeserialiseError
from Data.Either import :: Either
from iTasks.WF.Definition import class iTask
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenEq import generic gEq
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
......
definition module mTask.Interpret.Specification
from iTasks.WF.Definition import class iTask, :: Stability, :: TaskValue(..), :: Task
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenDefault import generic gDefault
from Data.GenEq import generic gEq
......
......@@ -9,7 +9,7 @@ from StdOverloaded import class +++, class %, class fromString, class ==
from Text import class Text
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.WF.Definition import class iTask
:: String255 =: String255 String
......
......@@ -5,7 +5,7 @@ from Data.GenEq import generic gEq
from GenType import generic gType, :: Box, :: GType
from Text.GenJSON import generic JSONEncode, generic JSONDecode, :: JSONNode
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.WF.Definition import class iTask
from GenType import generic gType, :: Box, :: GType
from GenType.CSerialise import generic gCSerialise, generic gCDeserialise, :: CDeserialiseError
......
......@@ -5,7 +5,7 @@ import mTask.Language
from StdOverloaded import class +, class -, class zero, class one, class *, class /, class <, class toInt, class ~
from iTasks.WF.Definition import class iTask
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenDefault import generic gDefault
from Data.GenEq import generic gEq
......
......@@ -5,7 +5,7 @@ import mTask.Language
from mTask.Interpret.ByteCodeEncoding import generic toByteCode, class toByteWidth, generic fromByteCode, :: FBC
from iTasks.WF.Definition import class iTask, :: Stability, :: TaskValue(..)
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose
from iTasks.UI.Editor.Generic import generic gEditor, :: Editor, :: EditorPurpose, :: EditorReport
from iTasks.Internal.Generic.Visualization import generic gText, :: TextFormat
from Data.GenDefault import generic gDefault
from Data.GenEq import generic gEq
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment