commit 598ec1c8d94ad8c94bfcf939fee3e3b47f991d19 Author: kk <13160659024@163.com> Date: Thu Jan 31 09:59:26 2019 +0800 gateway diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/conf/README.md b/conf/README.md new file mode 100644 index 0000000..8f95078 --- /dev/null +++ b/conf/README.md @@ -0,0 +1,252 @@ +### 网关配置文件说明 + +## Example: + +{ + + "gateway_info": { + + "id": "F23FACD2-7D85-4FCC-88E3-C5B78931A26D", + + "version": "yunhorn_gw_c_v1", + + "localtion": [113.960347, 22.54143], + + "单位": "深圳市云兴科技", + + "config": [{ + + + "id": "EC13C2D9-6A93-4894-BB5B-89BDAA786D8A", + "version": "yunhorn_kgl_c8_v1", + + "data": [ + + + ["男厕位", 1], + ["男厕位", 2], + ["男厕位", 3], + ["男厕位", 4], + ["男厕位", 5], + ["男厕位", 6], + ["男厕位", 7], + ["男厕位", 8] + ] + }, { + "id": "82BF7B5E-EC0D-4DB6-807F-B097C3C84073", + "version": "yunhorn_kgl_c8_v1", + "data": [ + ["母婴", 1], + ["残卫", 1], + ["残卫", 2], + ["男便池", 4], + ["男便池", 5], + ["男便池", 6], + ["男便池", 7], + ["男便池", 8] + ] + }, { + "id": "F5B7FDCC-79A8-499E-A925-F2FF357BE30D", + "version": "yunhorn_kgl_c8_v1", + "data": [ + ["男厕位", 9], + ["男厕位", 10], + ["女厕位", 14], + ["女厕位", 15], + ["女厕位", 16], + ["女厕位", 17], + ["女厕位", 18], + [null, null] + ] + }, + { + "id": "00DD66A6-378E-450D-98AA-44AC02741980", + "version": "yunhorn_kgl_c16_v1", + "data": [ + ["女厕位", 1], + ["女厕位", 2], + ["女厕位", 3], + ["女厕位", 4], + ["女厕位", 5], + ["女厕位", 6], + ["女厕位", 7], + ["女厕位", 8], + ["女厕位", 9], + ["女厕位", 10], + ["女厕位", 11], + ["女厕位", 12], + ["女厕位", 13], + ["男便池", 1], + ["男便池", 2], + ["男便池", 3] + ] + } + ] + }, + + "devices_list": [ + ["EC13C2D9-6A93-4894-BB5B-89BDAA786D8A", "port3", "yunhorn_kgl_c8_v1", "1"], + ["52A79B3D-D9D9-48AF-AEB4-7D456BA22B31", "port1", "yunhorn_db_c_v1", "1"], + ["607E11C4-8986-4F8E-807E-FFA655A6F677", "port1", "yunhorn_sb_c_v1", "1"], + ["066BB944-AFA2-4E5D-86FA-61D9080A97BB", "port2", "yunhorn_kq_c_v1", "1"], + ["22E175B2-8DD5-4740-B428-0297F409AA47", "port2", "yunhorn_lhq_c_v1", "1"], + ["20F44B9C-E4A1-478E-B28F-0B7B14A2AF64", "port2", "yunhorn_aq_c_v1", "1"] + ], + + "serialports_list": { + "port1": { + "com": "/dev/ttyUSB2", + "bauds": 2400, + "size": 8, + "parity": "E", + "stopbits": 1, + "readtimeout": 1, + "note": "水表电表" + }, + "port2": { + "com": "/dev/ttyUSB1", + "bauds": 9600, + "size": 8, + "parity": "N", + "stopbits": 1, + "readtimeout": 1, + "note": "空气质量" + }, + "port3": { + "com": "/dev/ttyUSB0", + "bauds": 9600, + "size": 8, + "parity": "N", + "stopbits": 1, + "readtimeout": 1, + "note": "开关量监测" + } + }, + + "rs485devices": { + "EC13C2D9-6A93-4894-BB5B-89BDAA786D8A": { + "version": "yunhorn_kgl_c8_v1", + "num": "1", + + "command": [ + [1, 2, 0, 0, 0, 1, 185, 202], + [1, 2, 0, 1, 0, 1, 232, 10], + [1, 2, 0, 2, 0, 1, 24, 10], + [1, 2, 0, 3, 0, 1, 73, 202], + [1, 2, 0, 4, 0, 1, 248, 11], + [1, 2, 0, 5, 0, 1, 169, 203], + [1, 2, 0, 6, 0, 1, 89, 203], + [1, 2, 0, 7, 0, 1, 8, 11] + ] + }, + "00DD66A6-378E-450D-98AA-44AC02741980": { + "version": "yunhorn_kgl_c16_v1", + "num": "1", + "command": [ + [3, 2, 0, 0, 0, 16, 120, 36] + ] + }, + "52A79B3D-D9D9-48AF-AEB4-7D456BA22B31": { + "version": "yunhorn_db_c_v1", + "num": "1", + "command": [ + [104, 65, 4, 0, 120, 16, 68, 104, 17, 4, 51, 51, 52, 51, 195, 22] + ] + }, + "607E11C4-8986-4F8E-807E-FFA655A6F677": { + "version": "yunhorn_sb_c_v1", + "num": "1", + "command": [ + [104, 16, 103, 19, 146, 128, 1, 0, 0, 1, 3, 31, 144, 2, 186, 22] + ] + }, + "066BB944-AFA2-4E5D-86FA-61D9080A97BB": { + "version": "yunhorn_kq_c_v1", + "num": "1", + "command": [ + [2, 3, 0, 0, 0, 7, 4, 59] + ] + }, + "22E175B2-8DD5-4740-B428-0297F409AA47": { + "version": "yunhorn_lhq_c_v1", + "num": "1", + "command": [ + [4, 3, 0, 6, 0, 1, 100, 94] + ] + }, + "20F44B9C-E4A1-478E-B28F-0B7B14A2AF64": { + "version": "yunhorn_aq_c_v1", + "num": "1", + "command": [ + [3, 3, 0, 0, 0, 1, 133, 232] + ] + } + }, + "iconurl": "http://192.168.3.188/icons/", + "websocketurl": ["192.168.3.216:8080", "/echo"], + + "post_to_server": "https://smartoilets.cn/socketServer/statis/push", + + "debug": false +} + +## 设备类型说明 + # 水表: + "yunhoron_sb_c_v1": + 通讯方式: RS485 + 串口配置: 2400 8 E 1 + 串口命令条目: 1 + 数据条目: 1 + + # 电表: + "yunhoron_db_c_v1": + 通讯方式: RS485 + 串口配置: 2400 8 E 1 + 串口命令条目: 1 + 数据条目: 1 + + # 空气检测仪: + "yunhoron_kq_c_v1": + 通讯方式: RS485 + 串口配置: 9600 8 N 1 + 串口命令条目: 1 + 数据条目: 7 + + # 硫化氢检测仪: + "yunhoron_lhq_c_v1": + 通讯方式: RS485 + 串口配置: 9600 8 N 1 + 串口命令条目: 1 + 数据条目: 1 + + # 氨气检测仪: + "yunhoron_aq_c_v1": + 通讯方式: RS485 + 串口配置: 9600 8 N 1 + 串口命令条目: 1 + 数据条目: 1 + + # 开关量检测: + "yunhoron_kgl_c8_v1": + 通讯方式: RS485 + 串口配置: 9600 8 N 1 + 串口命令条目: 8 + 数据条目: 8 + + "yunhoron_kgl_c16_v1": + 通讯方式: RS485 + 串口配置: 9600 8 N 1 + 串口命令条目: 1 + 数据条目: 16 + +## 当前版本说明 + 支持多设备采集,只需将串口参数配置一样的设备放在相对应的串口里面,并配置设备即可 + 目前集成RS485有线通讯,(zigbee,lora未集成) + 提供api,详见api说明 + 数据解析发送,目前4种数据格式,code1001,code1004,code1005,code1006 + code1001: 5分钟一次 (设备列表里的所有设备的数据) + code1004: 不定时,取决于客流摄像机 + code1005: 不定时,当设备数据变化时发送该设备数据 + code1006: 不定时,当有人评价时,发送评价设备的数据给服务器 + + diff --git a/conf/gateway.conf b/conf/gateway.conf new file mode 100644 index 0000000..8913a57 --- /dev/null +++ b/conf/gateway.conf @@ -0,0 +1,170 @@ +{ + "gateway_info": { + "id": "F23FACD2-7D85-4FCC-88E3-C5B78931A26D", + "version": "yunhorn_gw_c_v1", + "localtion": [113.960347, 22.54143], + "单位": "深圳市云兴科技", + "config": [{ + "id": "EC13C2D9-6A93-4894-BB5B-89BDAA786D8A", + "version": "yunhorn_kgl_c8_v1", + "data": [ + ["男厕位", 1], + ["男厕位", 2], + ["男厕位", 3], + ["男厕位", 4], + ["男厕位", 5], + ["男厕位", 6], + ["男厕位", 7], + ["男厕位", 8] + ] + }, { + "id": "82BF7B5E-EC0D-4DB6-807F-B097C3C84073", + "version": "yunhorn_kgl_c8_v1", + "data": [ + ["母婴", 1], + ["残卫", 1], + ["残卫", 2], + ["男便池", 4], + ["男便池", 5], + ["男便池", 6], + ["男便池", 7], + ["男便池", 8] + ] + }, { + "id": "F5B7FDCC-79A8-499E-A925-F2FF357BE30D", + "version": "yunhorn_kgl_c8_v1", + "data": [ + ["男厕位", 9], + ["男厕位", 10], + ["女厕位", 14], + ["女厕位", 15], + ["女厕位", 16], + ["女厕位", 17], + ["女厕位", 18], + [null, null] + ] + }, + { + "id": "00DD66A6-378E-450D-98AA-44AC02741980", + "version": "yunhorn_kgl_c16_v1", + "data": [ + ["女厕位", 1], + ["女厕位", 2], + ["女厕位", 3], + ["女厕位", 4], + ["女厕位", 5], + ["女厕位", 6], + ["女厕位", 7], + ["女厕位", 8], + ["女厕位", 9], + ["女厕位", 10], + ["女厕位", 11], + ["女厕位", 12], + ["女厕位", 13], + ["男便池", 1], + ["男便池", 2], + ["男便池", 3] + ] + } + ] + }, + "devices_list": [ + ["EC13C2D9-6A93-4894-BB5B-89BDAA786D8A", "port3", "yunhorn_kgl_c8_v1", "1"], + ["52A79B3D-D9D9-48AF-AEB4-7D456BA22B31", "port1", "yunhorn_db_c_v1", "1"], + ["607E11C4-8986-4F8E-807E-FFA655A6F677", "port1", "yunhorn_sb_c_v1", "1"], + ["066BB944-AFA2-4E5D-86FA-61D9080A97BB", "port2", "yunhorn_kq_c_v1", "1"], + ["22E175B2-8DD5-4740-B428-0297F409AA47", "port2", "yunhorn_lhq_c_v1", "1"], + ["20F44B9C-E4A1-478E-B28F-0B7B14A2AF64", "port2", "yunhorn_aq_c_v1", "1"] + ], + "serialports_list": { + "port1": { + "com": "/dev/ttyUSB2", + "bauds": 2400, + "size": 8, + "parity": "E", + "stopbits": 1, + "readtimeout": 1, + "note": "水表电表" + }, + "port2": { + "com": "/dev/ttyUSB1", + "bauds": 9600, + "size": 8, + "parity": "N", + "stopbits": 1, + "readtimeout": 1, + "note": "空气质量" + }, + "port3": { + "com": "/dev/ttyUSB0", + "bauds": 9600, + "size": 8, + "parity": "N", + "stopbits": 1, + "readtimeout": 1, + "note": "开关量监测" + } + }, + "rs485devices": { + "EC13C2D9-6A93-4894-BB5B-89BDAA786D8A": { + "version": "yunhorn_kgl_c8_v1", + "num": "1", + "command": [ + [1, 2, 0, 0, 0, 1, 185, 202], + [1, 2, 0, 1, 0, 1, 232, 10], + [1, 2, 0, 2, 0, 1, 24, 10], + [1, 2, 0, 3, 0, 1, 73, 202], + [1, 2, 0, 4, 0, 1, 248, 11], + [1, 2, 0, 5, 0, 1, 169, 203], + [1, 2, 0, 6, 0, 1, 89, 203], + [1, 2, 0, 7, 0, 1, 8, 11] + ] + }, + "00DD66A6-378E-450D-98AA-44AC02741980": { + "version": "yunhorn_kgl_c16_v1", + "num": "1", + "command": [ + [3, 2, 0, 0, 0, 16, 120, 36] + ] + }, + "52A79B3D-D9D9-48AF-AEB4-7D456BA22B31": { + "version": "yunhorn_db_c_v1", + "num": "1", + "command": [ + [104, 65, 4, 0, 120, 16, 68, 104, 17, 4, 51, 51, 52, 51, 195, 22] + ] + }, + "607E11C4-8986-4F8E-807E-FFA655A6F677": { + "version": "yunhorn_sb_c_v1", + "num": "1", + "command": [ + [104, 16, 103, 19, 146, 128, 1, 0, 0, 1, 3, 31, 144, 2, 186, 22] + ] + }, + "066BB944-AFA2-4E5D-86FA-61D9080A97BB": { + "version": "yunhorn_kq_c_v1", + "num": "1", + "command": [ + [2, 3, 0, 0, 0, 7, 4, 59] + ] + }, + "22E175B2-8DD5-4740-B428-0297F409AA47": { + "version": "yunhorn_lhq_c_v1", + "num": "1", + "command": [ + [4, 3, 0, 6, 0, 1, 100, 94] + ] + }, + "20F44B9C-E4A1-478E-B28F-0B7B14A2AF64": { + "version": "yunhorn_aq_c_v1", + "num": "1", + "command": [ + [3, 3, 0, 0, 0, 1, 133, 232] + ] + } + }, + "iconurl": "http://192.168.3.188/icons/", + "websocketurl": ["192.168.3.216:8080", "/echo"], + "post_to_server": "https://smartoilets.cn/socketServer/statis/push", + "debug": false +} \ No newline at end of file diff --git a/db/gateway.db b/db/gateway.db new file mode 100644 index 0000000..b26e007 Binary files /dev/null and b/db/gateway.db differ diff --git a/do_send_data.go b/do_send_data.go new file mode 100644 index 0000000..e69527a --- /dev/null +++ b/do_send_data.go @@ -0,0 +1,115 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + "strings" +) + +type P1001 struct { + CODE int `json:"code"` + GATEWAY_INFO Gateway_info `json:"gateway_info"` + DEVICES []Device `json:"devices"` +} + +type P1004_6 struct { + CODE uint16 `json:"code"` + ID string `json:"id"` + VERSION string `json:"version"` + ONLINE bool `json:"online"` + DATA map[string]interface{} `json:"data"` +} + +func send_data(code int, data ...Device) { + log.Println("************************************************************************") + data_buf := string(build_data(code, data)) + log.Println(data_buf) + post_to_server(data_buf) + log.Println("************************************************************************") +} + +func build_data(code int, data []Device) []byte { + switch code { + case 1001: + var R_data P1001 + R_data.CODE = 1001 + R_data.DEVICES = make([]Device, 0) + R_data.GATEWAY_INFO = conf.GATEWAY_INFO + DEVICEDATAS.RLock() + for _, i := range DEVICEDATAS.DATA { + R_data.DEVICES = append(R_data.DEVICES, i) + } + DEVICEDATAS.RUnlock() + data_buf, _ := json.Marshal(R_data) + return data_buf + case 1004: + var R_data P1004_6 + R_data.CODE = 1004 + R_data.ID = rl_data.MACADDRESS + R_data.VERSION = "hk_kll_v1" + R_data.ONLINE = true + R_data.DATA = make(map[string]interface{}) + R_data.DATA["starttime"] = rl_data.PEOPLECOUNTING.TIMERANGE.STARTTIME + R_data.DATA["endtime"] = rl_data.PEOPLECOUNTING.TIMERANGE.ENDTIME + R_data.DATA["enter"] = rl_data.PEOPLECOUNTING.ENTER + R_data.DATA["pass"] = rl_data.PEOPLECOUNTING.PASS + R_data.DATA["exit"] = rl_data.PEOPLECOUNTING.EXIT + traffic_data.TRAFFIC = traffic_data.TRAFFIC + rl_data.PEOPLECOUNTING.ENTER + data_buf, _ := json.Marshal(R_data) + return data_buf + case 1005: + var R_data P1004_6 + R_data.CODE = 1005 + R_data.ID = data[0].ID + R_data.VERSION = data[0].VERSION + R_data.ONLINE = data[0].ONLINE + R_data.DATA = data[0].DATA + data_buf, _ := json.Marshal(R_data) + return data_buf + case 1006: + var R_data P1004_6 + R_data.CODE = 1006 + R_data.ID = pj_data.ID + R_data.VERSION = "yunhorn_pj_c_v1" + R_data.ONLINE = true + R_data.DATA = make(map[string]interface{}) + R_data.DATA["feedback"] = pj_data.FEEDBACK + switch pj_data.FEEDBACK { + case "棒棒哒": + good++ + case "没厕纸": + bad++ + case "气味难闻": + bad++ + case "地面湿滑": + bad++ + case "洗手台不洁": + bad++ + case "厕位不洁": + bad++ + case "其他": + bad++ + } + satisfaction_data.SATISFACTION = good / (good + bad) + data_buf, _ := json.Marshal(R_data) + return data_buf + } + return nil +} + +func post_to_server(post_data string) { + url := conf.POST_TO_SERVER + payload := strings.NewReader("------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"data\"\r\n\r\n" + post_data + "\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--") + req, _ := http.NewRequest("POST", url, payload) + req.Header.Add("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW") + res, err := http.DefaultClient.Do(req) + if err != nil { + log.Println(err) + } else { + defer res.Body.Close() + body, _ := ioutil.ReadAll(res.Body) + log.Println("post_to_server:", string(body)) + } +} diff --git a/get_weather.go b/get_weather.go new file mode 100644 index 0000000..ee684e8 --- /dev/null +++ b/get_weather.go @@ -0,0 +1,155 @@ +package main + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" +) + +type Weather struct { + STATUS string + LANG string + UNIT string + SERVER_TIME uint16 + LOCATION []float32 + API_STATUS string + TZSHIFT uint16 + API_VERSION string + RESULT Result +} + +type Result struct { + STATUS string + O3 float32 + CO float32 + TEMPERATURE float32 + PM10 float32 + SKYCON string + CLOUDRATE float32 + AQI float32 + DSWRF float32 + VISIBILITY float32 + HUMIDITY float32 + SO2 float32 + ULTRAVIOLET Ultraviolet + PRES float32 + PM25 float32 + NO2 float32 + PERCIPITATION Precipitation + COMFORT Comfort + WIND Wind +} + +type Ultraviolet struct { + INDEX int + DESC string +} + +type Precipitation struct { + NEAREST Nearest + LOCAL Local +} + +type Nearest struct { + STATUS string + DISTANCE float32 + INTENSITY float32 +} + +type Local struct { + STATUS string + INTENSITY int + DATASOURCE string +} + +type Comfort struct { + INDEX int + DESC string +} + +type Wind struct { + DIRECTION float32 + SPEED float32 +} + +var iconurl = conf.ICONURL + +func get_weather() { + result, _ := http.Get("https://api.caiyunapp.com/v2/TAkhjf8d1nlSlspN/113.960256,22.541454/realtime.json") + body, _ := ioutil.ReadAll(result.Body) + log.Println(string(body)) + data := Weather{} + json.Unmarshal(body, &data) + var icon string + switch data.RESULT.SKYCON { + case "CLEAR_DAY": + icon = iconurl + "CLEAR_DAY.png" + case "CLEAR_NIGHT": + icon = iconurl + "CLEAR_NIGHT.png" + case "PARTLY_CLOUDY_DAY": + icon = iconurl + "PARTLY_CLOUDY_DAY.png" + case "PARTLY_CLOUDY_NIGHT": + icon = iconurl + "PARTLY_CLOUDY_NIGHT.png" + case "CLOUDY": + icon = iconurl + "icon/CLOUDY.png" + case "RAIN": + icon = iconurl + "RAIN.png" + case "SNOW": + icon = iconurl + "SNOW.png" + case "WIND": + icon = iconurl + "WIND.png" + case "HAZE": + icon = iconurl + "HAZE.png" + } + insert_db(data, icon) +} + +func insert_db(data Weather, icon string) { + stmt, _ := db.Prepare(`INSERT INTO weather ( + skycon, + icon_url, + o3, + co, + so2, + no2, + temperature, + humidity, + pm25, + pm10, + cloudrate, + aqi, + dswrf, + visibility, + ultraviolet_desc, + pres, + precipitation_nearest_distance, + precipitation_nearest_intensity, + precipitation_local_intensity, + comfort_desc, + wind_direction, + wind_speed) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`) + stmt.Exec( + data.RESULT.SKYCON, + icon, + data.RESULT.O3, + data.RESULT.CO, + data.RESULT.SO2, + data.RESULT.NO2, + data.RESULT.TEMPERATURE, + data.RESULT.HUMIDITY, + data.RESULT.PM25, + data.RESULT.PM10, + data.RESULT.CLOUDRATE, + data.RESULT.AQI, + data.RESULT.DSWRF, + data.RESULT.VISIBILITY, + data.RESULT.ULTRAVIOLET.DESC, + data.RESULT.PRES, + data.RESULT.PERCIPITATION.NEAREST.DISTANCE, + data.RESULT.PERCIPITATION.NEAREST.INTENSITY, + data.RESULT.PERCIPITATION.LOCAL.INTENSITY, + data.RESULT.COMFORT.DESC, + data.RESULT.WIND.DIRECTION, + data.RESULT.WIND.SPEED) +} diff --git a/loadconfig.go b/loadconfig.go new file mode 100644 index 0000000..6e5310d --- /dev/null +++ b/loadconfig.go @@ -0,0 +1,125 @@ +package main + +import ( + "database/sql" + "encoding/json" + _ "github.com/mattn/go-sqlite3" + "os" + "time" +) + +type Traffic struct { + TRAFFIC uint16 `json:"traffic"` +} + +type Satisfaction struct { + SATISFACTION float32 `json:"satisfaction"` +} + +type Conf struct { + GATEWAY_INFO Gateway_info `json:"gateway_info"` + DEVICES_LIST [][]string `json:"devices_list` + SERIALPORTS_LIST map[string]Serialconf `json:"serialports_list"` + RS485DEVICES map[string]Rs485devices `json:"rs485devices"` + ICONURL string `json:"iconurl` + WEBSOCKETURL []string `json:"websocketurl"` + POST_TO_SERVER string `json:"post_to_server"` + DEBUG bool `json:"debug"` +} + +type Gateway_info struct { + ID string `json:"id"` + VERSION string `json:"version"` + LOCATION []float32 `json:"location"` + UNITNAME string `json:"单位"` + CONFIG []Config `json:"config"` +} + +type Config struct { + ID string `json:"id"` + VERAION string `json:"version"` + DATA [][]interface{} `json:"data"` +} + +type Rs485devices struct { + VERSION string `json:"version"` + NUM string `json:"num"` + COMMAND [][]byte `json:"command"` +} + +type Serialconf struct { + COM string `json:"com"` + BAUDS int `json:"bauds"` + SIZE uint16 `json:"size"` + PARITY string `json:"parity"` + STOPBITS int `json:"stopbits"` + READTIMEOUT int `json:"readtimeout"` + NOTE string `json:"note"` +} + +var conf Conf +var DEVICEDATAS Devicedatas + +var db, _ = sql.Open("sqlite3", "./db/gateway.db") + +func init() { + configfile, err := os.Open("conf/gateway.conf") + check(err) + defer configfile.Close() + err = json.NewDecoder(configfile).Decode(&conf) + check(err) + var device Device + + DEVICEDATAS.Lock() + DEVICEDATAS.DATA = make(map[string]Device) + for _, i := range conf.DEVICES_LIST { + device.DATA = make(map[string]interface{}) + device.ID = i[0] + device.VERSION = i[2] + DEVICEDATAS.DATA[i[2]+"_"+i[3]] = device + } + DEVICEDATAS.Unlock() + dbdata_init() +} + +var good, bad float32 +var traffic_data Traffic +var satisfaction_data Satisfaction + +func dbdata_init() { + rows, err := db.Query("SELECT good, bad FROM satisfaction order by id DESC limit 1") + if err != nil { + good = 1 + bad = 0 + } else { + rows.Next() + rows.Scan(&good, &bad) + rows.Close() + } + rows.Close() + rows, err = db.Query("SELECT traffic FROM traffic t where datetime(t.create_time) > datetime('now','start of day','-1 day') order by id DESC limit 1 ") + if err != nil { + traffic_data.TRAFFIC = 0 + } else { + rows.Next() + rows.Scan(&traffic_data.TRAFFIC) + rows.Close() + } +} + +func savedata_cron() { + T := time.Now().Format("2006-01-02") + stmt, err := db.Prepare("UPDATE satisfaction set good=?, bad=? ,create_time=? where id=(SELECT max(id) FROM satisfaction)") + check(err) + stmt.Exec(&good, &bad, &T) + stmt, err = db.Prepare("UPDATE traffic set traffic=?, create_time=? where id=(SELECT max(id) FROM traffic)") + check(err) + stmt.Exec(&traffic_data.TRAFFIC, &T) +} + +func insertdata() { + T := time.Now().Format("2006-01-02") + stmt, _ := db.Prepare(`INSERT INTO traffic (traffic, create_time) values (?, ?)`) + stmt.Exec(&traffic_data.TRAFFIC, &T) + traffic_data.TRAFFIC = 0 +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..c6319f4 --- /dev/null +++ b/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/robfig/cron" + "log" + "os" +) + +var err error + +func main() { + go gw_router() + go serial_run() + c := cron.New() + c.AddFunc("0, *, *, *, *, *", func() { + send_data(1001) + }) + c.AddFunc("0, */10, *, *, *, *", savedata_cron) + c.AddFunc("0, 0, 0, *, *, *", insertdata) + c.AddFunc("0, 0, *, *, *, *", get_weather) + c.Start() + + select {} + +} + +func check(err error) { + if err != nil { + log.Println(err) + os.Exit(1) + } +} diff --git a/router.go b/router.go new file mode 100644 index 0000000..1e0935e --- /dev/null +++ b/router.go @@ -0,0 +1,312 @@ +package main + +import ( + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + _ "github.com/mattn/go-sqlite3" + "net/http" + "strconv" +) + +var router = gin.Default() + +func gw_router() { + router.Use(cors.Default()) + v1 := router.Group("/api/v1") + { + v1.GET("/weather", gw_weather_api) + v1.POST("/renliu", get_renliu_api) + v1.POST("/feedback", get_pj_api) + v1.GET("/totalWater", sb_api) + v1.GET("/totalElectricity", db_api) + v1.GET("/environmentDate", kq_api) + v1.GET("/traffic", renliu_api) + v1.GET("/stink", aq_api) + v1.GET("/satisfaction", pj_api) + v1.GET("/seat/:id", seat_api) + } + router.Run(":10086") +} + +type EventNotificationAlert struct { + IPADDRESS string `xml:"ipAddress" json:"ipAddress"` + PROTOCOLTYPE string `xml:"protocolType" json:"protocolType"` + MACADDRESS string `xml:"macAddress" json:"macAddress"` + CHANNELID uint16 `xml:"channelID" json:"channelID"` + DATETIME string `xml:"dateTime" json:"dateTime"` + ACTIVEPOSTCOUNT uint16 `xml:"activePostCount" json:"activePostCount"` + EVENTTYPE string `xml:"eventType" json:"eventType"` + EVENTSTATE string `xml:"eventState" json:"eventState"` + EVENTDESCRIPTION string `xml:"eventDescription" json:"eventDescription"` + CHANNELNAME string `xml:"channelName" json:"channelName"` + PEOPLECOUNTING PeopleCounting `xml:"peopleCounting" json:"peopleCounting"` +} + +type PeopleCounting struct { + STATISTICALMETHODS string `xml:"statisticalMethods" json:"statisticalMethods"` + TIMERANGE Timerange `xml:"TimeRange" json:"TimeRange"` + ENTER uint16 `xml:"enter" json:"enter"` + EXIT uint16 `xml:"exit" json:"exit"` + PASS uint16 `xml:"pass" json:"pass"` +} + +type Timerange struct { + STARTTIME string `xml:"startTime" json:"startTime"` + ENDTIME string `xml:"endTime" json:"endTime"` +} + +type FeedBack struct { + FEEDBACK string `json:"feedback"` + ID string `json:"id"` +} + +var rl_data EventNotificationAlert + +func get_renliu_api(c *gin.Context) { + err := c.Bind(&rl_data) + check(err) + send_data(1004) + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": rl_data}) +} + +var pj_data FeedBack + +func get_pj_api(c *gin.Context) { + err = c.Bind(&pj_data) + check(err) + send_data(1006) + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": pj_data}) +} + +func sb_api(c *gin.Context) { + DEVICEDATAS.RLock() + var data = make(map[string]uint16) + for _, i := range conf.DEVICES_LIST { + if i[2] == "yunhorn_sb_c_v1" { + if DEVICEDATAS.DATA[i[2]+"_"+i[3]].DATA["totalWater"] != nil { + data["totalWater"] = data["totalWater"] + DEVICEDATAS.DATA[i[2]+"_"+i[3]].DATA["totalWater"].(uint16) + } + } + } + DEVICEDATAS.RUnlock() + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": data}) +} + +func db_api(c *gin.Context) { + DEVICEDATAS.RLock() + var data = make(map[string]float32) + for _, i := range conf.DEVICES_LIST { + if i[2] == "yunhorn_db_c_v1" { + if DEVICEDATAS.DATA[i[2]+"_"+i[3]].DATA["totalElectric"] != nil { + data["totalElectric"] = data["totalElectric"] + DEVICEDATAS.DATA[i[2]+"_"+i[3]].DATA["totalElectric"].(float32) + } + } + } + DEVICEDATAS.RUnlock() + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": data}) +} + +func kq_api(c *gin.Context) { + DEVICEDATAS.RLock() + var sum float32 + var toinitdata float32 = 0.0 + var data = make(map[string]interface{}) + for _, i := range conf.DEVICES_LIST { + if i[2] == "yunhorn_kq_c_v1" { + sum++ + for k, j := range DEVICEDATAS.DATA[i[2]+"_"+i[3]].DATA { + if j != nil { + data[k] = toinitdata + data[k] = (data[k].(float32) + j.(float32)) / sum + } + } + } + } + DEVICEDATAS.RUnlock() + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": data}) +} + +func aq_api(c *gin.Context) { + DEVICEDATAS.RLock() + var sum uint16 + var toinitdata uint16 = 0 + var data_buf = make(map[string]interface{}) + var data = make(map[string]interface{}) + for _, i := range conf.DEVICES_LIST { + if i[2] == "yunhorn_aq_c_v1" { + sum++ + if DEVICEDATAS.DATA[i[2]+"_"+i[3]].DATA["氨气"] != nil { + data_buf["氨气"] = toinitdata + data_buf["氨气"] = (data_buf["氨气"].(uint16) + DEVICEDATAS.DATA["yunhorn_aq_c_v1_"+i[3]].DATA["氨气"].(uint16)) / sum + } + } + } + if data_buf["氨气"] == nil { + data["stink"] = "无味" + } else { + if data_buf["氨气"].(uint16) <= 1 { + data["stink"] = "无味" + } else if data_buf["氨气"].(uint16) > 1 && data_buf["氨气"].(uint16) <= 10 { + data["stink"] = "有味" + } else if data_buf["氨气"].(uint16) > 10 && data_buf["氨气"].(uint16) <= 40 { + data["stink"] = "恶臭" + } else if data_buf["氨气"].(uint16) > 40 && data_buf["氨气"].(uint16) <= 100 { + data["stink"] = "超级恶臭" + } + } + DEVICEDATAS.RUnlock() + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": data}) +} + +func renliu_api(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": traffic_data}) +} + +func pj_api(c *gin.Context) { + satisfaction_data.SATISFACTION = good / (good + bad) + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": satisfaction_data}) +} + +type Seat_data struct { + POSITIONLIST []interface{} `json:"positionList"` + HASPERSONLIST []interface{} `json:"hasPersonList"` + EMPTYPOSITION uint16 `json:"emptyPosition"` + OCCUPYPOSITION uint16 `json:"occupyPosition"` +} + +func seat_api(c *gin.Context) { + DEVICEDATAS.RLock() + type_id := c.Param("id") + var data Seat_data + var conf_data [][]interface{} + for _, i := range conf.GATEWAY_INFO.CONFIG { + arr := get_seat_device_data(i.ID) + version, num := arr[0], arr[1] + for k, j := range i.DATA { + if DEVICEDATAS.DATA[version+"_"+num].DATA != nil { + if DEVICEDATAS.DATA[version+"_"+num].DATA["data"+strconv.Itoa(k+1)] != nil { + j = append(j, DEVICEDATAS.DATA[version+"_"+num].DATA["data"+strconv.Itoa(k+1)]) + } + } + conf_data = append(conf_data, j) + } + } + switch type_id { + case "1": + data = scan_seat_from_confdata("男厕位", conf_data) + case "2": + data = scan_seat_from_confdata("男便池", conf_data) + case "3": + data = scan_seat_from_confdata("女厕位", conf_data) + case "4": + data = scan_seat_from_confdata("母婴", conf_data) + case "5": + data = scan_seat_from_confdata("残卫", conf_data) + } + DEVICEDATAS.RUnlock() + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": data}) +} + +func get_seat_device_data(id string) []string { + var data = make([]string, 2) + for _, i := range conf.DEVICES_LIST { + if id == i[0] { + data[0], data[1] = i[2], i[3] + break + } else { + data[0], data[1] = "null", "null" + } + } + return data +} + +func scan_seat_from_confdata(T string, conf_data [][]interface{}) Seat_data { + var data Seat_data + for _, i := range conf_data { + if i[0] == T { + data.POSITIONLIST = append(data.POSITIONLIST, i[1]) + if len(i) < 3 { + i = append(i, 0) + } + data.HASPERSONLIST = append(data.HASPERSONLIST, i[2]) + switch i[2] { + case 0: + data.EMPTYPOSITION++ + case 1: + data.OCCUPYPOSITION++ + } + } + } + return data +} + +type WEATHER struct { + // gorm.Model + SKYCON string `json:"skycon"` + ICON_URL string `json:"icon_url"` + O3 float32 `json:"o3"` + CO float32 `json:"co"` + SO2 float32 `json:"so2"` + NO2 float32 `json:"no2"` + TEMPERATURE float32 `json:"temperature"` + HUMIDITY float32 `json:"humidity"` + PM25 float32 `json:"pm25"` + PM10 float32 `json:"pm10"` + CLOUDRATE float32 `json:"cloudrate"` + AQI float32 `json:"aqi"` + DSWRF float32 `json:"dswrf"` + VISIBILITY float32 `json:"visibility"` + ULTRAVIOLET_DESC string `json:"ultraviolet_desc"` + PRES float32 `json:"pres"` + COMFORT_DESC string `json:"comfort_desc"` + WIND_DIRECTION float32 `json:"wind_direction"` + WIND_SPEED float32 `json:"wind_speed"` + TEMPERATURE_INDOOR interface{} `json:"temperature_indoor"` + HUMIDITY_INDOOR interface{} `json:"humidity_indoor"` +} + +func gw_weather_api(c *gin.Context) { + DEVICEDATAS.RLock() + rows, _ := db.Query("SELECT skycon, icon_url, o3, co, so2, no2, temperature, humidity, pm25, pm10, cloudrate, aqi, dswrf, visibility, ultraviolet_desc, pres, comfort_desc, wind_direction, wind_speed FROM weather order by id DESC limit 1") + var data WEATHER + rows.Next() + rows.Scan( + &data.SKYCON, + &data.ICON_URL, + &data.O3, + &data.CO, + &data.SO2, + &data.NO2, + &data.TEMPERATURE, + &data.HUMIDITY, + &data.PM25, + &data.PM10, + &data.CLOUDRATE, + &data.AQI, + &data.DSWRF, + &data.VISIBILITY, + &data.ULTRAVIOLET_DESC, + &data.PRES, + &data.COMFORT_DESC, + &data.WIND_DIRECTION, + &data.WIND_SPEED) + rows.Close() + var sum float32 + var toinitdata float32 = 0.0 + var data_buf = make(map[string]interface{}) + for _, i := range conf.DEVICES_LIST { + if i[2] == "yunhorn_kq_c_v1" { + sum++ + for k, j := range DEVICEDATAS.DATA[i[2]+"_"+i[3]].DATA { + if j != nil { + data_buf[k] = toinitdata + data_buf[k] = (data_buf[k].(float32) + j.(float32)) / sum + } + } + } + } + data.TEMPERATURE_INDOOR = data_buf["温度"] + data.HUMIDITY_INDOOR = data_buf["湿度"] + DEVICEDATAS.RUnlock() + c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": data}) +} diff --git a/serial_init.go b/serial_init.go new file mode 100644 index 0000000..c4c23f2 --- /dev/null +++ b/serial_init.go @@ -0,0 +1,74 @@ +package main + +import ( + "github.com/tarm/serial" + "log" + "time" +) + +func serial_run() { + SERIAL_PORT := make(map[string]*serial.Port) + ports_arr := make([]string, 0) + port_arr := make([]string, 0) + for _, i := range conf.DEVICES_LIST { + ports_arr = append(ports_arr, i[1]) + } + for i := 0; i < len(ports_arr); i++ { + if (i > 0 && ports_arr[i-1] == ports_arr[i]) || len(ports_arr[i]) == 0 { + continue + } + port_arr = append(port_arr, ports_arr[i]) + } + for _, i := range port_arr { + var PARITY serial.Parity + switch conf.SERIALPORTS_LIST[i].PARITY { + case "N": + PARITY = 'N' + case "E": + PARITY = 'E' + } + SERIAL_PORT[i], err = serial.OpenPort(&serial.Config{ + Name: conf.SERIALPORTS_LIST[i].COM, + Baud: conf.SERIALPORTS_LIST[i].BAUDS, + Parity: PARITY, + ReadTimeout: time.Second * time.Duration(conf.SERIALPORTS_LIST[i].READTIMEOUT)}) + check(err) + defer SERIAL_PORT[i].Close() + } + if conf.DEBUG { + log.Println(SERIAL_PORT) + } + for j, k := range SERIAL_PORT { + var the_same_port_devices [][]string + for _, i := range conf.DEVICES_LIST { + if j == i[1] { + the_same_port_devices = append(the_same_port_devices, i) + } + } + go run_port_device(k, the_same_port_devices) + } + select {} +} + +func run_port_device(port *serial.Port, the_same_port_devices [][]string) { + for { + for _, i := range the_same_port_devices { + switch i[2] { + case "yunhorn_sb_c_v1": + yunhorn_sb_c_v1(port, i[0], conf.RS485DEVICES[i[0]].COMMAND, conf.RS485DEVICES[i[0]].NUM) + case "yunhorn_db_c_v1": + yunhorn_db_c_v1(port, i[0], conf.RS485DEVICES[i[0]].COMMAND, conf.RS485DEVICES[i[0]].NUM) + case "yunhorn_kq_c_v1": + yunhorn_kq_c_v1(port, i[0], conf.RS485DEVICES[i[0]].COMMAND, conf.RS485DEVICES[i[0]].NUM) + case "yunhorn_lhq_c_v1": + yunhorn_lhq_c_v1(port, i[0], conf.RS485DEVICES[i[0]].COMMAND, conf.RS485DEVICES[i[0]].NUM) + case "yunhorn_aq_c_v1": + yunhorn_aq_c_v1(port, i[0], conf.RS485DEVICES[i[0]].COMMAND, conf.RS485DEVICES[i[0]].NUM) + case "yunhorn_kgl_c8_v1": + yunhorn_kgl_c8_v1(port, i[0], conf.RS485DEVICES[i[0]].COMMAND, conf.RS485DEVICES[i[0]].NUM) + case "yunhorn_kgl_c16_v1": + yunhorn_kgl_c16_v1(port, i[0], conf.RS485DEVICES[i[0]].COMMAND, conf.RS485DEVICES[i[0]].NUM) + } + } + } +} diff --git a/worker.go b/worker.go new file mode 100644 index 0000000..a1ea283 --- /dev/null +++ b/worker.go @@ -0,0 +1,348 @@ +package main + +import ( + "encoding/binary" + "github.com/tarm/serial" + "log" + "strconv" + "sync" +) + +type Devicedatas struct { + sync.RWMutex + DATA map[string]Device +} + +type Device struct { + sync.RWMutex + ID string `json:"id"` + VERSION string `json:"version"` + ONLINE bool `json:"online"` + DATA map[string]interface{} `json:"data"` +} + +func yunhorn_sb_c_v1(s *serial.Port, device_id string, command [][]byte, num string) { + _, err = s.Write([]byte(command[0])) + check(err) + if conf.DEBUG { + log.Printf("命令:\t%x", []byte(command[0])) + } + buf := make([]byte, 128) + b_buf := make([]byte, 0) + var nn int + for { + n, err := s.Read(buf) + nn = n + nn + b_buf = append(b_buf, buf[:n]...) + if err != nil { + break + } + } + if nn != 39 { + return + } + data_buf := b_buf[17 : nn-20] + + var device Device + device.Lock() + device.ID = device_id + device.VERSION = "yunhorn_sb_c_v1" + device.ONLINE = true + device.DATA = make(map[string]interface{}) + device.DATA["totalWater"] = binary.BigEndian.Uint16(data_buf) + device.Unlock() + DEVICEDATAS.Lock() + DEVICEDATAS.DATA["yunhorn_sb_c_v1_"+num] = device + DEVICEDATAS.Unlock() + + if conf.DEBUG { + log.Println(device) + } +} + +func yunhorn_db_c_v1(s *serial.Port, device_id string, command [][]byte, num string) { + _, err = s.Write([]byte(command[0])) + check(err) + if conf.DEBUG { + log.Printf("命令:\t%x", []byte(command[0])) + } + buf := make([]byte, 128) + bb_buf := make([]byte, 0) + var nn int + for { + n, err := s.Read(buf) + nn = n + nn + bb_buf = append(bb_buf, buf[:n]...) + if err != nil { + break + } + } + if nn != 20 { + return + } + data_buf := bb_buf[nn-6 : nn-2] + for i, x := range data_buf { + data_buf[i] = x - 0x33 + + } + Len := len(data_buf) - 1 + for i, k := 0, Len; i < k; i++ { + data_buf[i], data_buf[k] = data_buf[k], data_buf[i] + k-- + } + U8 := make([]byte, 0) + for i := 0; i < 4; i++ { + U8 = append(U8, data_buf[i]>>4) + U8 = append(U8, data_buf[i]<<4>>4) + } + b_buf := make([]byte, 0) + var db_data uint32 + b_buf = append([]byte{0, 0, 0}, U8[0]) + db_data = binary.BigEndian.Uint32(b_buf) * 10000000 + b_buf = append([]byte{0, 0, 0}, U8[1]) + db_data = db_data + binary.BigEndian.Uint32(b_buf)*1000000 + b_buf = append([]byte{0, 0, 0}, U8[2]) + db_data = db_data + binary.BigEndian.Uint32(b_buf)*100000 + b_buf = append([]byte{0, 0, 0}, U8[3]) + db_data = db_data + binary.BigEndian.Uint32(b_buf)*10000 + b_buf = append([]byte{0, 0, 0}, U8[4]) + db_data = db_data + binary.BigEndian.Uint32(b_buf)*1000 + b_buf = append([]byte{0, 0, 0}, U8[5]) + db_data = db_data + binary.BigEndian.Uint32(b_buf)*100 + b_buf = append([]byte{0, 0, 0}, U8[6]) + db_data = db_data + binary.BigEndian.Uint32(b_buf)*10 + b_buf = append([]byte{0, 0, 0}, U8[7]) + db_data = db_data + binary.BigEndian.Uint32(b_buf) + + var device Device + device.Lock() + device.ID = device_id + device.VERSION = "yunhorn_db_c_v1" + device.ONLINE = true + device.DATA = make(map[string]interface{}) + device.DATA["totalElectric"] = float32(db_data) / 100 + device.Unlock() + DEVICEDATAS.Lock() + DEVICEDATAS.DATA["yunhorn_db_c_v1_"+num] = device + DEVICEDATAS.Unlock() + + if conf.DEBUG { + log.Println(device) + } +} + +func yunhorn_kq_c_v1(s *serial.Port, device_id string, command [][]byte, num string) { + _, err = s.Write([]byte(command[0])) + check(err) + if conf.DEBUG { + log.Printf("命令:\t%x", []byte(command[0])) + } + buf := make([]byte, 128) + b_buf := make([]byte, 0) + var nn int + for { + n, err := s.Read(buf) + nn = n + nn + b_buf = append(b_buf, buf[:n]...) + if err != nil { + break + } + } + if nn != 19 { + return + } + data_buf := b_buf[3 : nn-2] + + var device Device + device.Lock() + device.ID = device_id + device.VERSION = "yunhorn_kq_c_v1" + device.ONLINE = true + device.DATA = make(map[string]interface{}) + device.DATA["二氧化碳"] = float32(binary.BigEndian.Uint16(data_buf[0:2])) + device.DATA["TVOC"] = float32(binary.BigEndian.Uint16(data_buf[2:4])) / 10 + device.DATA["甲醛"] = float32(binary.BigEndian.Uint16(data_buf[4:6])) / 10 + device.DATA["pm2.5"] = float32(binary.BigEndian.Uint16(data_buf[6:8])) + device.DATA["湿度"] = float32(binary.BigEndian.Uint16(data_buf[8:10])) / 10 + device.DATA["温度"] = float32(binary.BigEndian.Uint16(data_buf[10:13])) / 10 + device.DATA["PM10"] = float32(binary.BigEndian.Uint16(data_buf[12:14])) + device.Unlock() + DEVICEDATAS.Lock() + DEVICEDATAS.DATA["yunhorn_kq_c_v1_"+num] = device + DEVICEDATAS.Unlock() + + if conf.DEBUG { + log.Println(device) + } +} + +func yunhorn_aq_c_v1(s *serial.Port, device_id string, command [][]byte, num string) { + _, err = s.Write([]byte(command[0])) + check(err) + if conf.DEBUG { + log.Printf("命令:\t%x", []byte(command[0])) + } + buf := make([]byte, 128) + b_buf := make([]byte, 0) + var nn int + for { + n, err := s.Read(buf) + nn = n + nn + b_buf = append(b_buf, buf[:n]...) + if err != nil { + break + } + } + if nn != 7 { + return + } + data_buf := b_buf[3 : nn-2] + + var device Device + device.Lock() + device.ID = device_id + device.VERSION = "yunhorn_aq_c_v1" + device.ONLINE = true + device.DATA = make(map[string]interface{}) + device.DATA["氨气"] = binary.BigEndian.Uint16(data_buf) + device.Unlock() + DEVICEDATAS.Lock() + DEVICEDATAS.DATA["yunhorn_aq_c_v1_"+num] = device + DEVICEDATAS.Unlock() + + if conf.DEBUG { + log.Println(device) + } +} + +func yunhorn_lhq_c_v1(s *serial.Port, device_id string, command [][]byte, num string) { + _, err = s.Write([]byte(command[0])) + check(err) + if conf.DEBUG { + log.Printf("命令:\t%x", []byte(command[0])) + } + buf := make([]byte, 128) + b_buf := make([]byte, 0) + var nn int + for { + n, err := s.Read(buf) + nn = n + nn + b_buf = append(b_buf, buf[:n]...) + if err != nil { + break + } + } + if nn != 7 { + return + } + data_buf := b_buf[3 : nn-2] + + var device Device + device.Lock() + device.ID = device_id + device.VERSION = "yunhorn_lhq_c_v1" + device.ONLINE = true + device.DATA = make(map[string]interface{}) + device.DATA["硫化氢"] = binary.BigEndian.Uint16(data_buf) + device.Unlock() + DEVICEDATAS.Lock() + DEVICEDATAS.DATA["yunhorn_lhq_c_v1_"+num] = device + DEVICEDATAS.Unlock() + + if conf.DEBUG { + log.Println(device) + } +} + +func yunhorn_kgl_c8_v1(s *serial.Port, device_id string, command [][]byte, num string) { + buf := make([]byte, 128) + b_buf := make([]byte, 0) + for _, i := range command { + _, err = s.Write([]byte(i)) + check(err) + if conf.DEBUG { + log.Printf("命令:\t%x", []byte(i)) + } + _, err = s.Read(buf) + if err != nil { + return + } + b_buf = append(b_buf, buf[3]) + } + DEVICEDATAS.RLock() + var olddata = DEVICEDATAS.DATA["yunhorn_kgl_c8_v1_"+num] + DEVICEDATAS.RUnlock() + var device Device + device.Lock() + device.ID = device_id + device.VERSION = "yunhorn_kgl_c8_v1" + device.ONLINE = true + device.DATA = make(map[string]interface{}) + for i, k := range b_buf { + device.DATA["data"+strconv.Itoa(i+1)] = binary.BigEndian.Uint16(append([]byte{0x00}, k)) + } + device.Unlock() + DEVICEDATAS.Lock() + DEVICEDATAS.DATA["yunhorn_kgl_c8_v1_"+num] = device + DEVICEDATAS.Unlock() + for i, k := range device.DATA { + if k != olddata.DATA[i] { + send_data(1005, device) + break + } + } + if conf.DEBUG { + log.Println(device) + } +} + +func yunhorn_kgl_c16_v1(s *serial.Port, device_id string, command [][]byte, num string) { + buf := make([]byte, 128) + _, err = s.Write([]byte(command[0])) + check(err) + if conf.DEBUG { + log.Printf("命令:\t%x", []byte(command[0])) + } + _, err = s.Read(buf) + if err != nil { + return + } + b_buf := Bytes2Bits(buf[3:5]) + + DEVICEDATAS.RLock() + var olddata = DEVICEDATAS.DATA["yunhorn_kgl_c16_v1_"+num] + DEVICEDATAS.RUnlock() + var device Device + device.Lock() + device.ID = device_id + device.VERSION = "yunhorn_kgl_c16_v1" + device.ONLINE = true + device.DATA = make(map[string]interface{}) + for i, k := range b_buf { + device.DATA["data_buf"+strconv.Itoa(i+1)] = k + } + device.Unlock() + DEVICEDATAS.Lock() + DEVICEDATAS.DATA["yunhorn_kgl_c16_v1_"+num] = device + DEVICEDATAS.Unlock() + for i, k := range device.DATA { + if k != olddata.DATA[i] { + send_data(1005, device) + break + } + } + if conf.DEBUG { + log.Println(device) + } +} + +func Bytes2Bits(data []byte) []uint16 { + dst := make([]uint16, 0) + for _, v := range data { + for i := 0; i < 8; i++ { + move := uint(i) + dst = append(dst, uint16((v>>move)&1)) + } + } + return dst +}