github.com/unirita/cuto@v0.9.8-0.20160830082821-aa6652f877b7/realtime/network/network.go (about) 1 package network 2 3 import ( 4 "encoding/csv" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "strconv" 12 13 scan "github.com/mattn/go-scan" 14 15 "github.com/unirita/cuto/flowgen/converter" 16 ) 17 18 // Number of columns 19 const columns = 14 20 21 // Indexes of columns 22 const ( 23 nameIdx = iota 24 nodeIdx 25 portIdx 26 pathIdx 27 paramIdx 28 envIdx 29 workIdx 30 wrcIdx 31 wptnIdx 32 ercIdx 33 eptnIdx 34 timeoutIdx 35 snodeIdx 36 sportIdx 37 ) 38 39 var jobex = make([][]string, 0) 40 41 // LoadJobex loads jobex csv which corresponds to name. 42 // LoadJobex returns empty jobex array if csv is not exists. 43 func LoadJobex(name string, nwkDir string) error { 44 csvPath := searchJobexCsvFile(name, nwkDir) 45 if csvPath == "" { 46 return nil 47 } 48 49 file, err := os.Open(csvPath) 50 if err != nil { 51 return err 52 } 53 defer file.Close() 54 55 jobex, err = loadJobexFromReader(file) 56 return err 57 } 58 59 func searchJobexCsvFile(name string, nwkDir string) string { 60 individualPath := filepath.Join(nwkDir, "realtime", name+".csv") 61 defaultPath := filepath.Join(nwkDir, "realtime", "default.csv") 62 63 if _, err := os.Stat(individualPath); !os.IsNotExist(err) { 64 return individualPath 65 } 66 if _, err := os.Stat(defaultPath); !os.IsNotExist(err) { 67 return defaultPath 68 } 69 70 return "" 71 } 72 73 // loadJobexFromReader reads reader as csv format, and create jobex data array. 74 func loadJobexFromReader(reader io.Reader) ([][]string, error) { 75 r := csv.NewReader(reader) 76 result := make([][]string, 0) 77 isTitleRow := true 78 for { 79 record, err := r.Read() 80 if err == io.EOF { 81 break 82 } else if err != nil { 83 return nil, err 84 } 85 if !isTitleRow { 86 result = append(result, record) 87 } 88 isTitleRow = false 89 } 90 if len(result) > 0 && len(result[0]) != columns { 91 return nil, fmt.Errorf("Number of jobex csv columns[%d] must be %d.", len(result[0]), columns) 92 } 93 94 return result, nil 95 } 96 97 func getJobexRecordByName(jobname string) []string { 98 for _, record := range jobex { 99 if record[nameIdx] == jobname { 100 return record 101 } 102 } 103 return nil 104 } 105 106 type Network struct { 107 Flow string `json:"flow"` 108 Jobs []Job `json:"jobs"` 109 } 110 111 // Parse parses str as json format, and create Network object. 112 func Parse(reader io.Reader) (*Network, error) { 113 decorder := json.NewDecoder(reader) 114 115 network := new(Network) 116 if err := decorder.Decode(network); err != nil { 117 return nil, err 118 } 119 network.complementJobs() 120 121 if err := network.DetectError(); err != nil { 122 return nil, err 123 } 124 125 return network, nil 126 } 127 128 func (n *Network) complementJobs() { 129 for _, record := range jobex { 130 isExists := false 131 for _, job := range n.Jobs { 132 if record[nameIdx] == job.Name { 133 isExists = true 134 break 135 } 136 } 137 138 if !isExists { 139 newJob := Job{Name: record[nameIdx]} 140 newJob.importJobex() 141 n.Jobs = append(n.Jobs, newJob) 142 } 143 } 144 } 145 146 // DetectError detects error in Network object, and return it. 147 // If there is no error, DetectError returns nil. 148 func (n *Network) DetectError() error { 149 for _, job := range n.Jobs { 150 if job.Name == "" { 151 return errors.New("Anonymous job detected.") 152 } 153 } 154 return nil 155 } 156 157 func (n *Network) Export(name, nwkDir string) error { 158 flowPath := filepath.Join(nwkDir, name+".bpmn") 159 jobexPath := filepath.Join(nwkDir, name+".csv") 160 161 flowHead, err := converter.ParseString(n.Flow) 162 if err != nil { 163 return err 164 } 165 definition := converter.GenerateDefinitions(flowHead) 166 if err := converter.ExportFile(flowPath, definition); err != nil { 167 return err 168 } 169 170 file, err := os.Create(jobexPath) 171 if err != nil { 172 return err 173 } 174 defer file.Close() 175 if err := n.exportJob(file); err != nil { 176 return nil 177 } 178 179 return nil 180 } 181 182 func (n *Network) Clean(name, nwkDir string) { 183 flowPath := filepath.Join(nwkDir, name+".bpmn") 184 jobexPath := filepath.Join(nwkDir, name+".csv") 185 os.Remove(flowPath) 186 os.Remove(jobexPath) 187 } 188 189 func (n *Network) exportJob(writer io.Writer) error { 190 w := csv.NewWriter(writer) 191 // Add title record. 192 if err := w.Write(make([]string, columns)); err != nil { 193 return err 194 } 195 196 for _, job := range n.Jobs { 197 record := make([]string, columns) 198 record[nameIdx] = job.Name 199 record[nodeIdx] = job.Node 200 record[portIdx] = strconv.Itoa(job.Port) 201 record[pathIdx] = job.Path 202 record[paramIdx] = job.Param 203 record[envIdx] = job.Env 204 record[workIdx] = job.Work 205 record[wrcIdx] = strconv.Itoa(job.WRC) 206 record[wptnIdx] = job.WPtn 207 record[ercIdx] = strconv.Itoa(job.ERC) 208 record[eptnIdx] = job.EPtn 209 record[timeoutIdx] = strconv.Itoa(job.Timeout) 210 record[snodeIdx] = job.SNode 211 record[sportIdx] = strconv.Itoa(job.SPort) 212 if err := w.Write(record); err != nil { 213 return err 214 } 215 } 216 w.Flush() 217 return nil 218 } 219 220 type Job struct { 221 Name string 222 Node string 223 Port int 224 Path string 225 Param string 226 Env string 227 Work string 228 WRC int 229 WPtn string 230 ERC int 231 EPtn string 232 Timeout int 233 SNode string 234 SPort int 235 } 236 237 // UnmarshalJSON create job object from data(JSON format). 238 // Use jobex value loaded by LoadJobex function if the parameter is null. 239 func (j *Job) UnmarshalJSON(data []byte) error { 240 var i interface{} 241 if err := json.Unmarshal(data, &i); err != nil { 242 return err 243 } 244 if err := scan.ScanTree(i, "/name", &j.Name); err != nil { 245 return err 246 } 247 j.importJobex() 248 249 // scan.ScanTree does not change value of 3rd parameter when error occured. 250 scan.ScanTree(i, "/node", &j.Node) 251 scan.ScanTree(i, "/port", &j.Port) 252 scan.ScanTree(i, "/path", &j.Path) 253 scan.ScanTree(i, "/param", &j.Param) 254 scan.ScanTree(i, "/env", &j.Env) 255 scan.ScanTree(i, "/work", &j.Work) 256 scan.ScanTree(i, "/wrc", &j.WRC) 257 scan.ScanTree(i, "/wptn", &j.WPtn) 258 scan.ScanTree(i, "/erc", &j.ERC) 259 scan.ScanTree(i, "/eptn", &j.EPtn) 260 scan.ScanTree(i, "/timeout", &j.Timeout) 261 scan.ScanTree(i, "/snode", &j.SNode) 262 scan.ScanTree(i, "/sport", &j.SPort) 263 264 return nil 265 } 266 267 func (j *Job) importJobex() { 268 for _, record := range jobex { 269 if record[nameIdx] == j.Name { 270 var err error 271 j.Node = record[nodeIdx] 272 j.Port, err = strconv.Atoi(record[portIdx]) 273 if err != nil { 274 j.Port = 0 275 } 276 j.Path = record[pathIdx] 277 j.Param = record[paramIdx] 278 j.Env = record[envIdx] 279 j.Work = record[workIdx] 280 j.WRC, err = strconv.Atoi(record[wrcIdx]) 281 if err != nil { 282 j.WRC = 0 283 } 284 j.WPtn = record[wptnIdx] 285 j.ERC, err = strconv.Atoi(record[ercIdx]) 286 if err != nil { 287 j.ERC = 0 288 } 289 j.EPtn = record[eptnIdx] 290 j.Timeout, err = strconv.Atoi(record[timeoutIdx]) 291 if err != nil { 292 j.Timeout = 0 293 } 294 j.SNode = record[snodeIdx] 295 j.SPort, err = strconv.Atoi(record[sportIdx]) 296 if err != nil { 297 j.SPort = 0 298 } 299 } 300 } 301 }