Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: f7fb3acb9bdf299659cb5d702debdbab3f027c9d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*******************************************************************************
 * Copyright (c) 2011 protos software gmbh (http://www.protos.de).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * CONTRIBUTORS:
 * 		Eyrak Pean (initial contribution)
 * 		Juergen Haug
 * 
 *******************************************************************************/

package org.eclipse.etrice.generator.gnuplot

import com.google.inject.Inject
import org.eclipse.etrice.core.common.base.Annotation
import org.eclipse.etrice.core.common.base.IntLiteral
import org.eclipse.etrice.core.common.base.KeyValue
import org.eclipse.etrice.core.common.base.RealLiteral
import org.eclipse.etrice.core.common.base.StringLiteral
import org.eclipse.etrice.core.genmodel.etricegen.Root
import org.eclipse.etrice.core.genmodel.etricegen.SubSystemInstance
import org.eclipse.etrice.generator.base.io.IGeneratorFileIO
import org.eclipse.etrice.generator.generic.RoomExtensions
import com.google.inject.Singleton

@Singleton
class GnuplotScriptGenerator { 
	@Inject
	IGeneratorFileIO fileIo

	@Inject
	extension RoomExtensions roomExtensions

	def doGenerate(Root root) {
		if (root.subSystemInstances.empty)
			return;

		// TODO: warning more than one ssi
		val ssi = root.subSystemInstances.head
		if(!ssi.subSystemClass.annotations.exists[a |a.type.name == "Gnuplot"])
			return;
		
		val path = ssi.subSystemClass.getGenerationTargetPath
		val infoPath = ssi.subSystemClass.generationInfoPath
		try {
			fileIo.generateFile("Generating gnuplot script for subsystem " + ssi.name, path, infoPath,
				"/gnuplot/main.data.csv-script.plt", ssi.generatePlotScript)
			fileIo.generateFile("Generating gnuplot launch configuration", path, infoPath,
				"/gnuplot/create_gnuplot.launch", gnuPlotLaunchFile)
		} catch (Exception e) {
			//e.printStackTrace
		}

	}

	def protected gnuPlotLaunchFile() '''
		<?xml version="1.0" encoding="UTF-8" standalone="no"?>
		<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
		<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LAUNCH_CONFIGURATION_BUILD_SCOPE" value="${none}"/>
		<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${system_path:gnuplot}"/>
		<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="${project_loc}/src-gen/gnuplot/main.data.csv-script.plt"/>
		<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${project_loc}"/>
		</launchConfiguration>

	'''

	def protected getAttribute(Annotation anno, String name) {
		anno.attributes.findFirst[attr|attr.key == name]
	}

	def protected asString(KeyValue kv) {
		if (kv.value instanceof StringLiteral)
			(kv.value as StringLiteral).value
		else
			null
	}

	def protected asReal(KeyValue kv) {
		if (kv.value instanceof RealLiteral)
			(kv.value as RealLiteral).value
		else if (kv.value instanceof IntLiteral)
			(kv.value as IntLiteral).value as double
		else
			null
	}

	def protected asInteger(KeyValue kv) {
		if (kv.value instanceof IntLiteral)
			(kv.value as IntLiteral).value
		else
			null
	}

	def protected generatePlotScript(SubSystemInstance ssi) {

		// TODO: warn if more than one GnuPlot annotation
		val plotAnnotation = ssi.subSystemClass.annotations.filter [ a |
			a.type.name == "Gnuplot"
		].head

		val defaultFontsize = 10

		// TODO: error checking
		val format = plotAnnotation?.getAttribute("format")?.asString
		val outputfile = plotAnnotation?.getAttribute("outputfile")?.asString
		val width = plotAnnotation?.getAttribute("width")?.asInteger
		val height = plotAnnotation?.getAttribute("height")?.asInteger
		val fontsize = plotAnnotation?.getAttribute("fontsize")?.asInteger ?: defaultFontsize

		val graphAnnotations = ssi.subSystemClass.annotations.filter [ a |
			a.type.name == "GnuplotGraph"
		].toList

		'''
			#!/gnuplot
			
			# Color Brewer set1 5-set
			set linetype 1 lc rgb '#e41a1c' lw 1
			set linetype 2 lc rgb '#377eb8' lw 1
			set linetype 3 lc rgb '#4daf4a' lw 1
			set linetype 4 lc rgb '#984ea3' lw 1
			set linetype 5 lc rgb '#ff7f00' lw 1
			set linetype cycle 5
			
			cd 'log'
			set datafile separator comma
			set terminal «format» size «width»,«height» font ",«fontsize»" background "white"
			set output '«outputfile»'
			set size 1,1
			set multiplot layout «graphAnnotations.size»,1
			set grid
			show grid
			set format y "% 5.3f"
			«var i = 0»
			«FOR a : graphAnnotations»
				
				«ssi.generateGraph(a, i++, graphAnnotations.size)»
			«ENDFOR»
			
			unset multiplot
			unset output
			
		'''
	}

	def protected generateGraph(SubSystemInstance ssi, Annotation graph, int index, int total) {

		// TODO: handle multiple paths in the same graph
		val paths = graph.getAttribute("paths")?.asString

		// TODO: take interval from physical thread associated with actor instance instead of annotation attribute
		val interval = graph.getAttribute("interval")?.asInteger ?: 20
		val xtics = graph.getAttribute("xtics")?.asReal ?: 100
		val mxtics = graph.getAttribute("mxtics")?.asInteger ?: 4
		val ymin = graph.getAttribute("ymin")?.asReal
		val ymax = graph.getAttribute("ymax")?.asReal

		val vertOrigin = ((total - (index + 1)) as double) / total
		val vertSize = 1.0F / total

		'''
			set yrange [«ymin ?: "*"» : «ymax ?: "*"»]
			set xtics rotate «xtics»
			set mxtics «mxtics»
			set ylabel
			set xlabel "time (ms)"
			timeInMs(x) = «interval» * x
			set origin 0,«vertOrigin»
			set size 1,«vertSize»
			plot 'main.data.csv' using (timeInMs(column(1))):(column("«paths»")) with lines
		'''
	}
}

Back to the top