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