-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into emulation_generator
# Conflicts: # constants.go # socketManager.go Implemented emulated status updates
- Loading branch information
Showing
6 changed files
with
365 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/binary" | ||
"fmt" | ||
"github.com/goburrow/modbus" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
"sync" | ||
"time" | ||
) | ||
|
||
var modbusClient = &_modbusClient{ | ||
modbus.NewClient(nil), | ||
sync.Mutex{}, | ||
sync.WaitGroup{}, | ||
_registers, | ||
} | ||
|
||
type _modbusClient struct { | ||
conn modbus.Client | ||
lock sync.Mutex | ||
wait sync.WaitGroup | ||
registers map[int][2]string | ||
} | ||
|
||
func (m *_modbusClient) start() { | ||
m.wait.Add(1) | ||
go m.connect() | ||
} | ||
|
||
func (m *_modbusClient) connect() { | ||
handler := modbus.NewRTUClientHandler("/dev/ttyUSB0") | ||
handler.BaudRate = 19200 | ||
handler.DataBits = 8 | ||
handler.Parity = "N" | ||
handler.StopBits = 1 | ||
handler.SlaveId = 1 | ||
handler.Timeout = 5 * time.Second | ||
if err := handler.Connect(); err != nil { | ||
fmt.Printf("Error connecting to modbus RTU client: %v\n", err) | ||
} | ||
m.conn = modbus.NewClient(handler) | ||
m.wait.Done() | ||
} | ||
|
||
func (m *_modbusClient) readHoldingRegisters(start uint16, num uint16) []byte { | ||
m.wait.Wait() | ||
m.lock.Lock() | ||
defer m.lock.Unlock() | ||
data, err := m.conn.ReadHoldingRegisters(start, num) | ||
if err != nil { | ||
fmt.Printf("Error reading %v holding registers starting with %v: %v\n", start, num, err) | ||
return []byte{} | ||
} else { | ||
return data | ||
} | ||
} | ||
|
||
func (m *_modbusClient) readAllRegisterData() map[string]StampedReading { | ||
m.wait.Wait() // Wait for client to start up | ||
var keys []int // Array for register numbers | ||
for k := range m.registers { | ||
keys = append(keys, k) | ||
} | ||
sort.Ints(keys) // Make sure it's ascending | ||
|
||
results := make(map[string]StampedReading) // Result map | ||
intervals := m.readsForAllRegisters() // Modbus reading intervals | ||
var data []byte // Data from modbus | ||
|
||
m.lock.Lock() // One reader at a time to be safe | ||
for _, interval := range intervals { // Do readings for all intervals | ||
start := uint16(interval[0]) | ||
number := uint16(interval[1]) | ||
reading, err := m.conn.ReadHoldingRegisters(start, number) // Read the registers | ||
if err != nil { // Make sure it worked | ||
fmt.Printf("Error reading %v registers starting at %v: %v\n", number, start, err) | ||
continue | ||
} | ||
data = append(data, reading...) // Accumulate the data | ||
} | ||
m.lock.Unlock() | ||
|
||
timestamp := int(time.Now().Unix()) | ||
for _, interval := range intervals { // Do mappings for all intervals | ||
start := uint16(interval[0]) | ||
number := uint16(interval[1]) | ||
for r := 0; r < int(start+number); r++ { // Check all the registers in this range | ||
if val, found := m.registers[r]; found { // If we have a metric for this, add it to results | ||
switch val[1] { | ||
case WORD: | ||
results[val[0]] = StampedReading{timestamp, int32(binary.LittleEndian.Uint16(data[2*r : 2*r+2]))} | ||
case SWORD: | ||
results[val[0]] = StampedReading{timestamp, int32(int16(binary.LittleEndian.Uint16(data[2*r : 2*r+2])))} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return results | ||
} | ||
|
||
func (m *_modbusClient) readsForAllRegisters() [][2]int { | ||
var reads [][2]int | ||
var keys []int | ||
for k := range m.registers { | ||
keys = append(keys, k) | ||
} | ||
sort.Ints(keys) | ||
|
||
startReg := keys[0] | ||
var i int | ||
for i = 0; i < len(keys); i++ { | ||
if keys[i]-startReg > 49 { | ||
reads = append(reads, [2]int{startReg, keys[i-1] - startReg + 1}) | ||
startReg = keys[i] | ||
} | ||
} | ||
reads = append(reads, [2]int{startReg, keys[i-1] - startReg + 1}) | ||
|
||
return reads | ||
} | ||
|
||
func reverse(s string) string { | ||
runes := []rune(s) | ||
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { | ||
runes[i], runes[j] = runes[j], runes[i] | ||
} | ||
return string(runes) | ||
} | ||
|
||
func (m *_modbusClient) getStatusCodes() string { | ||
data := m.readHoldingRegisters(uint16(1499), uint16(16)) | ||
binstr := make([]string, len(data)) | ||
for i := 0; i < len(data); i += 2 { | ||
binstr[i] = reverse(fmt.Sprintf("%08s", strconv.FormatInt(int64(data[i+1]), 2))) | ||
binstr[i+1] = reverse(fmt.Sprintf("%08s", strconv.FormatInt(int64(data[i]), 2))) | ||
} | ||
return strings.Join(binstr, "") | ||
} | ||
|
||
func (m *_modbusClient) getStatusUpdates() map[string]int { | ||
statuses := m.getStatusCodes() | ||
fmt.Printf("Statuses: %v\n", statuses) | ||
statusMap := make(map[string]int) | ||
for k, v := range _statuses { | ||
status := statuses[k] - 48 | ||
statusMap[v] = int(status) | ||
} | ||
if len(_lastStatus) == 0 { | ||
_lastStatus = statusMap | ||
return statusMap | ||
} else { | ||
updateMap := make(map[string]int) | ||
for k, v := range _lastStatus { | ||
newv := statusMap[k] | ||
if newv != v { | ||
updateMap[k] = newv | ||
} | ||
} | ||
_lastStatus = statusMap | ||
return updateMap | ||
} | ||
} | ||
|
||
var _registers = map[int][2]string{ | ||
0: {"l1.l2.voltage", WORD}, | ||
1: {"l2.l3.voltage", WORD}, | ||
2: {"l3.l1.voltage", WORD}, | ||
3: {"l1.l0.voltage", WORD}, | ||
4: {"l2.l0.voltage", WORD}, | ||
5: {"l3.l0.voltage", WORD}, | ||
6: {"l1.current", WORD}, | ||
7: {"l2.current", WORD}, | ||
8: {"l3.current", WORD}, | ||
9: {"frequency", WORD}, | ||
10: {"total.kw", WORD}, | ||
11: {"rate.kw", WORD}, | ||
12: {"total.pf", SWORD}, | ||
13: {"l1.kw", WORD}, | ||
14: {"l1.pf", SWORD}, | ||
15: {"l2.kw", WORD}, | ||
16: {"l2.pf", SWORD}, | ||
17: {"l3.kw", WORD}, | ||
18: {"l3.pf", SWORD}, | ||
19: {"total.kvar", SWORD}, | ||
20: {"l1.kvar", SWORD}, | ||
21: {"l2.kvar", SWORD}, | ||
22: {"l3.kvar", SWORD}, | ||
23: {"total.kva", WORD}, | ||
24: {"l1.kva", WORD}, | ||
25: {"l2.kva", WORD}, | ||
26: {"l3.kva", WORD}, | ||
32: {"oil.pressure", SWORD}, | ||
33: {"coolant.temp", SWORD}, | ||
34: {"engine.rpm", WORD}, | ||
35: {"battery.voltage", WORD}, | ||
36: {"fuel.pressure", WORD}, | ||
37: {"fuel.temp", SWORD}, | ||
38: {"fuel.rate", WORD}, | ||
40: {"coolant.pressure", WORD}, | ||
41: {"coolant.level", WORD}, | ||
42: {"oil.temp", SWORD}, | ||
43: {"oil.level", WORD}, | ||
44: {"crankcase.pressure", WORD}, | ||
45: {"ambient.temp", SWORD}, | ||
46: {"ecm.battery.voltage", WORD}, | ||
48: {"intake.temp", SWORD}, | ||
49: {"intake.pressure", WORD}, | ||
} | ||
|
||
var _statuses = map[int]string{ | ||
0: "fault.emstop", | ||
1: "fault.overspeed", | ||
2: "fault.overcrank", | ||
5: "fault.coolant.temp.low", | ||
6: "fault.fuel.low", | ||
7: "fault.engine.temp.high", | ||
8: "fault.oil.pressure.low", | ||
9: "status.notinauto", | ||
11: "fault.batt.volt.low", | ||
12: "fault.batt.volt.high", | ||
14: "status.system.ready", | ||
19: "fault.coolant.level.low", | ||
27: "fault.crank.volt.low", | ||
34: "status.eps", | ||
39: "status.gen.running", | ||
207: "fault.common", | ||
} | ||
|
||
var _lastStatus = map[string]int{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.