X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scl.tutorial%2Fscl%2FTutorial%2F2.03%20%28Exercise%29%20Pipelines.md;fp=bundles%2Forg.simantics.scl.tutorial%2Fscl%2FTutorial%2F2.03%20%28Exercise%29%20Pipelines.md;h=6e19955455911854b7aaa27b9ea68f578c213f62;hb=39fd9bd29b18a2f7abe62fb13da3359b3618dda7;hp=0000000000000000000000000000000000000000;hpb=f024c0a7208e379a82f0ab51a71a123f9bb2a2bb;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scl.tutorial/scl/Tutorial/2.03 (Exercise) Pipelines.md b/bundles/org.simantics.scl.tutorial/scl/Tutorial/2.03 (Exercise) Pipelines.md new file mode 100644 index 000000000..6e1995545 --- /dev/null +++ b/bundles/org.simantics.scl.tutorial/scl/Tutorial/2.03 (Exercise) Pipelines.md @@ -0,0 +1,199 @@ +[pipes.txt]: "http://www.simantics.org/~niemisto/SCL20150513/pipes.txt" + +## Pipelines exercise + +In this exercise, you create a model configuration based on the data in the file [pipes.txt]. + +### Step 1 + +Implement a function + + isNonemptyString :: String -> Boolean + +that returns true, when the string given as a parameter is nonempty: + + > isNonemptyString "" + False + > isNonemptyString "foo" + True + +You need the empty string `""` and the inequality comparison: + +::value[Prelude/!=] + +### Step 2 + +Implement a function + + removeComment :: String -> String + +that removes a comment from a line. A comment starts with `!` and continues +to the end of the line. It should also remove leading and trailing whitespace. For example + + > removeComment "A;B;C ! This is a comment" + "A;B;C" + > removeComment "Hello World!" + "Hello World" + > removeComment "This line contains no comments." + "This line contains no comments." + +The following functions are useful here: + +::value[Prelude/splitString, Prelude/!, Prelude/trim] + +### Step 3 + +New, lets read the file [pipes.txt]. Store it to somewhere in your file system. + +You need to import the module `StringIO`, either using the import dialog or +with the command + + import "StringIO" + +Now, try to read the file using + +::value[StringIO/readLines] + +Remember that `\` is an escape character in SCL. A string containing directory separators +must be written in one of the following forms: + + "c:/temp/pipes.txt" + "c:\\temp\\pipes.txt" + +### Step 4 + +As you see, the file contains empty lines and comments. Implement a function + + loadAndPreprocess :: String -> [String] + +that reads the file, whose name is given as a parameter, removes the comments, empty lines and leading and trailing +whitespace at every line. It returns the preprocessed lines. You need the +functions `isNonemptyString`, `removeComment` you implemented before, +`readLines` and the following functions + +::value[Prelude/map,Prelude/filter] + +### Step 5 + +Create a new SCL module and move your definitions there (if you have not done so already). It +is much easier to continue handling the increasing number of function definitions there. + +### Step 6 + +You may have noticed that the lines in the preprocessed file have entries separated by `;`. +The first entry in each line is either "POINT" or "PIPE". Implement the functions + + isPointLine, isPipeLine :: [String] -> Boolean + +that check whether the first string in a list of strings is "POINT" or "PIPE". +For example + + > isPointLine ["POINT", "1", "2", "3.4", "100", "200"] + +### Step 7 + +Now, add the following definitions to your SCL module: + +~~~ +handlePointEntry :: String -> [String] -> () +handlePointEntry diagram ["POINT", id, pointElevation, x, y] = do + print "Add a point \(id) into the diagram \(diagram) with elevation \(pointElevation) at coordinates \(x),\(y)." + +handlePipeEntry :: String -> Integer -> [String] -> () +handlePipeEntry diagram id ["PIPE", id1, id2, pipeLength] = do + print "Add a pipe \(id) into the diagram \(diagram) connecting the point \(id1) to the point \(id2) with length \(pipeLength)" +~~~ + +Create a function + + readPipesFile :: String -> String -> () + +that is called as + + readPipesFile "diagramName" "fileName" + +It should first read the file and preprocess it using `loadAndPreprocess` you implemented in Step 4. +It should then split each line into entries with `splitString` and `map` +Note that because of the order of the parameters of `splitString` you need either anonymous +functions, a separate funtion definition or +::value[Prelude/flip] + +It should then filter the lines into two lists, one containing all definitions of points +and one all definitions of pipes. Finally, the function should call +`handlePointEntry` for all points using + +::value[Prelude/iter] + +and `handlePipeEntry` for all pipes using (because `handlePipeEntry` has an extra integer parameter) + +::value[Prelude/iterI] + +When finished the function should work like this from the console: + +~~~ +> readPipesFile "X" "c:/temp/pipes.txt" +Add a point 1 into the diagram X with elevation 0 at coordinates 100,100. +Add a point 2 into the diagram X with elevation 1.1 at coordinates 120,100. +Add a point 3 into the diagram X with elevation 1.5 at coordinates 140,100. +Add a point 4 into the diagram X with elevation 2.5 at coordinates 160,100. +Add a pipe 0 into the diagram X connecting the point 1 to the point 2 with length 12 +Add a pipe 1 into the diagram X connecting the point 2 to the point 3 with length 11 +Add a pipe 2 into the diagram X connecting the point 3 to the point 4 with length 13 +~~~ + +### Step 8 + +Reimplement the function `handlePointEntry` so that it creates the points into the diagram with the +specified elevation and diagram coordinates. You need the functions + +::value[Apros/Legacy/aadd, Apros/Legacy/amodi] + +You need also the function + +::value[Prelude/read] + +to convert the diagram coordinates from strings to doubles. +Add some prefix to the point indicies to form the point name (for example +`name = "PO" + id`. The name of the elevation attribute is +`PO11_ELEV`. + +Test your implementation with the example data. + +### Step 9 + +Reimplement the function `handlePipeEntry` so that it creates the pipes into the diagram with +the specified length (`PI12_LENGTH`) and connects it to the specified points +(`PI12_CONNECT_POINT_1` and `PI12_CONNECT_POINT_2`). You may place the pipes to the origin +`(0,0)`. + +Test your implementation again with the example data. + +### Step 10 + +In this final step, fix the coordinates of the pipes so that they are located between the points +they connect. + +You may do this in the following way. In `readPipesFile`, define a function + + coordinates :: String -> Maybe (Double, Double) + +that gives the coordinates of a point when given the index of the point. You may create it +by partially applying the function + +::value[Prelude/index] + +for this. Then, add the new parameter `coordinates` to the function `handlePipeEntry` so that +its signature becomes + + String -> (String -> Maybe (Double, Double)) -> Integer -> [String] -> () + +Now, you may read the coordinates of the points in `handlePipeEntry` like this + + (x1,y1) = fromJust (coordinates id1) + +where + +::value[Prelude/fromJust] + +Use the average of the connected points as the diagram coordinate for the pipe. + \ No newline at end of file