package main

import (
	//"flag"
	"strconv"
	"fmt"
	"os"
	"os/exec"
	"io"
	"io/ioutil"
	"log"
	"log/syslog"
	"net/http"
	"time"
	"strings"
	"container/list"
	"sync"
	"regexp"
	"bytes"
	"encoding/json"
	"github.com/beevik/etree"
	MQTT "github.com/eclipse/paho.mqtt.golang"

	goonvif "github.com/use-go/onvif"
	"github.com/use-go/onvif/device"
	//"github.com/use-go/onvif/gosoap"	
	"github.com/use-go/onvif/xsd/onvif"
	"github.com/use-go/onvif/ptz"
	"github.com/use-go/onvif/media"
	"github.com/use-go/onvif/event"
)

const (
	login    = "admin"
	password = "a12345678"
)

type Ipcamera struct {
	dev		*goonvif.Device
	sn		string
	ip_addr	string
	port		string
	user	string
	password string
	rtsp_url	string
	mediaProfileToken string	
	status_time int64
	status_time_5m int64
	online bool
	push_time	int64
	app_key string
	event_enable float64
}

type MqttMsg struct {
	sn		string
	mi		int
	identifier	string
	timestamp		int
	serverUrl	string
	x 				float64
	y					float64
	z					float64
	perset_name	string
	port int
	gw_id string
}

type CmdRusult struct {
	Result    	int			`json:"result"`
	Error_info	string	`json:"error_info"`
}

type DataReport struct {
	Sn    	string	`json:"sn"`
	Identifier	string	`json:"identifier"`
	Time		int64	`json:"time"`
	Mi		int	`json:"mi"`
	Port	string	`json:"port"`
	Data_type int	`json:"data_type"`
	Tag_node string	`json:"tag_node"`
}

type CmdSetRgltRaw struct {
	Sn    	string	`json:"sn"`
	Identifier	string	`json:"src_identifier"`
	Time		int64	`json:"time"`
	Mi		int	`json:"mi"`
	Port	string	`json:"port"`
	Data_type int	`json:"data_type"`
	Tag_node string	`json:"tag_node"`
}

type StatusReport struct {
	Sn    	string	`json:"sn"`
	Online	bool	`json:"online"`
	Last_rcv		int64	`json:"last_rcv"`
	Login_time	int64	`json:"login_time"`
}

type StatusDevice struct {
	Status    	[1]StatusReport	`json:"status"`
}

type PresetInfo struct {
	Perset_name string `json:"perset_name"`
	X   string `json:"x"`
	Y   string `json:"y"`
}

type PresetsList struct {
	Result    	int			`json:"result"`
	Error_info	string	`json:"error_info"`
	Presets []PresetInfo
}

type RtspUrlCmdRusult struct {
	Result    	int			`json:"result"`
	Error_info	string	`json:"error_info"`
	RtspUrl			string  `json:"rtspUrl"`
}

var pgrep_ffmpeg_fl = "pgrep -fl ffmpeg"
var pgrep_ffmpeg_afl = "pgrep -afl ffmpeg"
var ipc_object [33]Ipcamera// = [5]Ipcamera{}
var ipc_cnt int = 0
var mqtt_msg_q = NewQueue()
var mc MQTT.Client
var sysLog *syslog.Writer
var GW_SN string = "GWSN"
var mqtt_connected int = 0

/*==================================数据处理==================================*/
func get_pulish_topic(data_type int, sn string, identifier string) string {
	var publish_topic string
	if data_type == 0 {
		publish_topic = "ipc/" + GW_SN + "/TCP_CLIENT/device/" + sn + "/data/property/" + identifier
	}	else if data_type == 1 {
		publish_topic = "ipc/" + GW_SN + "/TCP_CLIENT/device/" + sn + "/data/event/" + identifier
	}	else if data_type == 2 {
		publish_topic = "ipc/" + GW_SN + "/TCP_CLIENT/device/" + sn + "/data/service/" + identifier
	}
	return publish_topic
}
//数据上报
func data_report(msgv *MqttMsg, data_type int, result_data string) int {
	dr := &DataReport{}
	dr.Sn = msgv.sn
	dr.Identifier = msgv.identifier
	dr.Time = time.Now().Unix()
	dr.Mi = msgv.mi
	dr.Port = "TCP_CLIENT"
	dr.Tag_node = result_data
	dr.Data_type = data_type

	publish_topic := get_pulish_topic(data_type, msgv.sn, msgv.identifier)

	data_json, _ := json.Marshal(dr)
	mqtt_publish(publish_topic, string(data_json))
	return 0
}

//状态上报
func status_report(ipc_obj *Ipcamera, now_time int64) {
	ipc_status := &StatusDevice{}
	var status [1]StatusReport
	status[0].Sn = ipc_obj.sn
	status[0].Online = ipc_obj.online
	status[0].Last_rcv = now_time
	status[0].Login_time = 0
	ipc_status.Status =  status
	
	publish_topic := "ipc/" + GW_SN + "/TCP_CLIENT/Evt_NodesStatus"
	
	data_json, _ := json.Marshal(ipc_status)
	mqtt_publish(publish_topic, string(data_json))
}

func exec_cmd(cmd_str string) string {
	sysLog.Info(fmt.Sprintf("cmd_str:%s", cmd_str))
	cmd := exec.Command("/bin/sh", "-c", cmd_str)
	bytes,err := cmd.Output()
	if err != nil {
			sysLog.Warning(fmt.Sprintf("exec_cmd err:%v",err))
	}
	resp := string(bytes)
	sysLog.Info(fmt.Sprintf("exec_cmd resp:%s",resp))
	return resp
}
/*==================================视频流处理==================================*/
//获取取当前推流状态,返回pid
func get_push_video_pid(ip_addr string, cmd string) string {
	ffmpeg_start := exec_cmd(cmd)
	//ffmpeg_status = cmd
	sh_index := strings.Index(ffmpeg_start, cmd)
	if sh_index >= 0 && sh_index < 30 {
		ffmpeg_start = string([]byte(ffmpeg_start)[sh_index+len(cmd)+1:])
	}
	ip_index := strings.Index(ffmpeg_start, ip_addr)
	if ip_index > 100 {
		tmp_string := string([]byte(ffmpeg_start)[ip_index-100:])
		tmp_index := strings.Index(tmp_string, "logger -t ffmpeg") + len("logger -t ffmpeg") 
		ffmpeg_start = string([]byte(tmp_string)[tmp_index+1:])
	}
	pid_index := strings.Index(ffmpeg_start, "ffmpeg -rtsp_transport")
	ip_index = strings.Index(ffmpeg_start, ip_addr)
	flv_index := strings.Index(ffmpeg_start, "-f flv")
	if ip_index < 0 || pid_index < 0 || flv_index < 0 {
		sysLog.Info(fmt.Sprintf("get_push_video_status 0"))
		return ""
	}	else {
		sysLog.Info(fmt.Sprintf("pid_index:%d", pid_index))
		ffmpeg_pid := string([]byte(ffmpeg_start)[0:(pid_index-1)])
		sysLog.Info(fmt.Sprintf("get_push_video_status 1 ffmpeg_pid:%s", ffmpeg_pid))
		return ffmpeg_pid
	}
}

//获取当前推流状态,返回pid
func get_push_video_status(ip_addr string) string {
	ffmpeg_pid := get_push_video_pid(ip_addr, pgrep_ffmpeg_afl)
	if ffmpeg_pid == "" {
		ffmpeg_pid = get_push_video_pid(ip_addr, pgrep_ffmpeg_fl)
	}

	return ffmpeg_pid
}

//停止推流	
func stop_video_streaming(ffmpeg_pid string) int {
	stop_video := "kill -9 " + ffmpeg_pid
	exec_cmd(stop_video)
	return 0
}

//推流	
func push_video_streaming(rtsp_url string, server_url string) int {
	push_video := `ffmpeg -rtsp_transport tcp -nostdin -i "` + rtsp_url + `" -vcodec copy -acodec copy -f flv "` + server_url + `" 2>&1 | logger -t 'ffmpeg' &`
	sysLog.Info(fmt.Sprintf("push_video:%s", push_video))
	cmd := exec.Command("/bin/sh", "-c", push_video)
	cmd.Run()
	//log.Println("err:", err)
	//exec_cmd(push_video)
	return 0
}

//存文件
func ptz_video_save_mp4(rtsp_url string, gw_id string) string {
	file:= "/tmp/" + fmt.Sprintf("%s", gw_id) + ".mp4"
	push_video := `ffmpeg -rtsp_transport tcp -i "` + rtsp_url + `" -r 10 -vcodec copy -acodec copy -f mp4 "` + file +`" 2>&1 | logger -t 'ffmpeg' &`
	sysLog.Info(fmt.Sprintf("push_video:%s", push_video))
	cmd := exec.Command("/bin/sh", "-c", push_video)
	cmd.Run()
	//log.Println("err:", err)
	//exec_cmd(push_video)
	return file
}

//停止所有推流	
func stop_all_video_streaming() int {
	stop_video := "killall -9 ffmpeg"
	exec_cmd(stop_video)
	return 0
}

//检查IPC是否在线
func ipcamera_online_check(ip_addr string) bool {
	ping_str := "ping " + ip_addr + " -c 3"
	find_str := "bytes from "+ ip_addr
	ping_status := exec_cmd(ping_str)
	if strings.Index(ping_status, find_str) >= 0 {
		sysLog.Info(fmt.Sprintf("ping %s result 1", ip_addr))
		return true
	}	else {
		sysLog.Info(fmt.Sprintf("ping %s result 0", ip_addr))
		return false
	}
}

/*==================================MQTT消息处理==================================*/
func mqtt_message_handle() {
	mqtt_disconnect_cnt := 0
	for {
		find := 0
		mp4_file := ""
		port := 0
		save_url := ""
		now_time := time.Now().Unix()
		//fmt.Printf("mqtt_message_handle mqtt_msg_q cnt %d\n",mqtt_msg_q.Len())
		var next *list.Element
		for e := mqtt_msg_q.Front(); e != nil; e = next {
			msgv := (e.Value).(*MqttMsg)
			sysLog.Info(fmt.Sprintf("mqtt_msg_q cnt %d sn %s identifier %s",mqtt_msg_q.Len(),msgv.sn,msgv.identifier))

			for i := 0; i < ipc_cnt; i++ {
				if ipc_object[i].sn == msgv.sn {
					result := 0
					error_info := "succeed"//"ERROR"
					if msgv.identifier == "PushVideoStreaming" {
						ffmpeg_pid := get_push_video_status(ipc_object[i].ip_addr)
						if ffmpeg_pid == "" {
							result = push_video_streaming(ipc_object[i].rtsp_url, msgv.serverUrl)
						}
						ipc_object[i].push_time = now_time
					} else if msgv.identifier == "StopVideoStreaming" {
						ffmpeg_pid := get_push_video_status(ipc_object[i].ip_addr)
						if ffmpeg_pid != "" {
							result = stop_video_streaming(ffmpeg_pid)
						}
						ipc_object[i].push_time = 0
					} else if msgv.identifier == "RelativeMove" {
						result = ptz_RelativeMove(&ipc_object[i], msgv)
					} else if msgv.identifier == "ContinuousMove" {
						result = ptz_ContinuousMove(&ipc_object[i], msgv)
					} else if msgv.identifier == "GotoPreset" {
						result = ptz_GotoPreset(&ipc_object[i], msgv)
					} else if msgv.identifier == "MakeAround" {
						result = ptz_MakeAround(&ipc_object[i])
						time.Sleep(time.Duration(15)*time.Second)
						ptz_stop(&ipc_object[i])
					} else if msgv.identifier == "SaveMp4File" {
						save_url = msgv.serverUrl
						mp4_file = ptz_video_save_mp4(ipc_object[i].rtsp_url, GW_SN)
						ipc_object[i].push_time = now_time - 280
					} else if msgv.identifier == "StartTours" {
						log.Printf(fmt.Sprintf("url:%s", msgv.serverUrl))
						ptz_GotoHomePosition(&ipc_object[i], msgv)
						time.Sleep(time.Duration(10)*time.Second)
						save_url = msgv.serverUrl
						port = msgv.port
						mp4_file = ptz_video_save_mp4(ipc_object[i].rtsp_url, msgv.gw_id)
						ipc_object[i].push_time = now_time
						for {
							if checkFileIsExist(mp4_file) == true {
								break
							}
							time.Sleep(time.Duration(1)*time.Second)
						}
						result = ptz_MakeAround(&ipc_object[i])
						time.Sleep(time.Duration(15)*time.Second)
						ptz_stop(&ipc_object[i])
						exec_cmd("killall -2 ffmpeg")
						if save_url != "" && mp4_file != "" {
							var scp_cmd = "scp -P " + fmt.Sprintf("%d ", port) + mp4_file + " " + save_url
							exec_cmd(scp_cmd)
							save_url = ""
						}
						if mp4_file != "" {
							var rm_cmd = "rm " + mp4_file
							exec_cmd(rm_cmd)
							mp4_file = ""
						}
					} else if msgv.identifier == "GetPresets" {
						result = ptz_GetPresets(&ipc_object[i], msgv)
						if result == 0 {
							break
						}
					} else if msgv.identifier == "GetPresetTours" {
						result = ptz_GetPresetTours(&ipc_object[i], msgv)
						if result == 0 {
							break
						}
					} else if msgv.identifier == "SetPreset" {
						result = ptz_SetPreset(&ipc_object[i], msgv)
					} else if msgv.identifier == "RemovePreset" {
						result = ptz_RemovePreset(&ipc_object[i], msgv)
					} else if msgv.identifier == "GotoHomePosition" {
						result = ptz_GotoHomePosition(&ipc_object[i], msgv)
					} else if msgv.identifier == "SetHomePosition" {
						result = ptz_SetHomePosition(&ipc_object[i], msgv)
					} else if msgv.identifier == "GetStreamUri" {
						result = ipc_GetStreamUri(&ipc_object[i], msgv)
						if result == 0 {
							break
						}
					} else if msgv.identifier == "PullMessages" {
						//result = ptz_SetPreset(&ipc_object[i], msgv)
						log.Println("result:", result)
					}					

					rt := &CmdRusult{}
					rt.Result = result
					if result == 1 {
						error_info = "error"
					}
					rt.Error_info = error_info
					data, _ := json.Marshal(rt)
					//data_report(msgv, 0, string(data))
					data_report(msgv, 2, string(data))
					break
				}
			}
			next = e.Next()
			mqtt_msg_q.Remove(e)
			find = 1
			break
		}
		if find == 0 {
			time.Sleep(time.Second)
			//time.Sleep(time.Duration(1)*time.Second)
		}
		if mqtt_connected == 0 {
			mqtt_disconnect_cnt++
			if mqtt_disconnect_cnt >= 5 {
				mqtt_disconnect_cnt = 0
				subscribe()
			}
		}
		for i := 0; i < ipc_cnt; i++ {
			if ipc_object[i].status_time == 0 {
				continue
			}
			if ipc_object[i].push_time  > 0 && now_time - ipc_object[i].push_time  >= 300 {
				//检查是否正在推流
				ffmpeg_pid := get_push_video_status(ipc_object[i].ip_addr)
				//停止推流
				if ffmpeg_pid != "" {
					sysLog.Info(fmt.Sprintf("StopVideoStreaming Auto startTime %d...", ipc_object[i].push_time))
					stop_video_streaming(ffmpeg_pid)

					ipc_object[i].push_time =  0
				}
			}
			//5分钟检测一下设备状态
			if now_time - ipc_object[i].status_time_5m > 300 {
				ipc_object[i].status_time_5m = now_time
				dev_online := ipcamera_online_check(ipc_object[i].ip_addr)
				if dev_online != ipc_object[i].online {
					ipc_object[i].online = dev_online
					ipc_object[i].status_time = now_time
					status_report(&ipc_object[i], now_time)
				}
			}
			//1小时更新一下设备状态
			if now_time - ipc_object[i].status_time > 3600 || ipc_object[i].status_time == 0 {
				ipc_object[i].status_time = now_time
				status_report(&ipc_object[i], now_time)
			}
		}
	}
}

func get_rglt_data(payload string) {
	var v interface{}
	jsonData := []byte(payload)
	json.Unmarshal(jsonData, &v)
	data := v.(map[string]interface{})
	for i := 0; i < ipc_cnt; i++ {
		sysLog.Info(fmt.Sprintf("get_rglt_data %d sn(%s %s)\n",i,data["sn"],ipc_object[i].sn))
		if ipc_object[i].sn == data["sn"] {
			for k, v := range data {
				switch v := v.(type) {
				case string:
						//fmt.Println(k, v, "(string)")
						if k == "data_b64" {
							var data_b64 interface{}
							b64_str := fmt.Sprintln(v)
							b64_slice := []byte(b64_str)
							json.Unmarshal(b64_slice, &data_b64)
							cmd_data := data_b64.(map[string]interface{})
							msg := MqttMsg{}
							msg.sn = cmd_data["sn"].(string)
							msg.identifier = cmd_data["identifier"].(string)
							if msg.identifier == "PushVideoStreaming" || msg.identifier == "StartTours" || msg.identifier == "SaveMp4File" {
								msg.serverUrl = cmd_data["serverUrl"].(string)
							}
							if msg.identifier == "StartTours" {
								msg.port = int(cmd_data["server_port"].(float64))
								msg.gw_id = cmd_data["gw_id"].(string)
							}
							if msg.identifier == "ContinuousMove" {
								msg.x, _ = strconv.ParseFloat(cmd_data["x"].(string),64)
								msg.y, _ = strconv.ParseFloat(cmd_data["y"].(string),64)
								msg.z = 0
								if cmd_data["z"] != nil {
									msg.z, _ = strconv.ParseFloat(cmd_data["z"].(string),64)//cmd_data["z"].(float64)
								}
								sysLog.Info(fmt.Sprintf("ContinuousMove x：%f y:%f v:%f", msg.x, msg.y, msg.z))
							}
							if msg.identifier == "RelativeMove" {
								msg.x, _ = strconv.ParseFloat(cmd_data["x"].(string),64)
								msg.y, _ =  strconv.ParseFloat(cmd_data["y"].(string),64)
								msg.z = 0
								if cmd_data["z"] != nil {
									msg.z, _ = strconv.ParseFloat(cmd_data["z"].(string),64)//cmd_data["z"].(float64)
								}
								sysLog.Info(fmt.Sprintf("RelativeMove x：%f y:%f v:%f", msg.x, msg.y, msg.z))
							}
							if msg.identifier == "GotoPreset" || msg.identifier == "SetPreset" || msg.identifier == "RemovePreset" {
								msg.perset_name = cmd_data["perset_name"].(string)
							}
							msg.mi = int(cmd_data["mi"].(float64))
							msg.timestamp = int(cmd_data["timestamp"].(float64))
							mqtt_msg_q.PushBack(&msg)
							sysLog.Info(fmt.Sprintf("get_rglt_data mqtt_msg_q cnt %d\n",mqtt_msg_q.Len()))
	
							/*var next *list.Element
							for e := mqtt_msg_q.Front(); e != nil; e = next {
									msgv := (e.Value).(*MqttMsg)
									//fmt.Println("------e.msg.sn: len:",msgv.sn, mqtt_msg_q.Len())
									next = e.Next()
							}*/							
						}
				case float64:
						//fmt.Println(k, v, "(float64)")
				case []interface{}:
						//fmt.Println(k, "(array):")
						//for i, u := range v {
								//fmt.Println("    ", i, u)
						//}
				default:
					sysLog.Info(fmt.Sprintf("k %v v %v %s", k, v, "(unknown)"))
				}
			}
			break
		}
	}	
}

/*==================================PTZ控制==================================*/
//PTZ旋转
func ptz_RelativeMove(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("ptz RelativeMove..."))
	result := 1
	//log.Println("dev:",ipc_obj.dev)
	ptzRelativeMove := ptz.RelativeMove{ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		Translation: onvif.PTZVector{
			PanTilt: onvif.Vector2D{
				X:     msgv.x,
				Y:     msgv.y,
			},
			Zoom: onvif.Vector1D{
				X:     msgv.z,  // -1.0 -> 1.0
			},
		},
		Speed: onvif.PTZSpeed{
			PanTilt: onvif.Vector2D{
				X:     0.5,
				Y:     0.5,
			},
			Zoom: onvif.Vector1D{
				X:     0.0,
			},
		},
	}

	ptzRelativeMoveResponse, err := ipc_obj.dev.CallMethod(ptzRelativeMove)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("RelativeMove error %v", err))
	} else {
		tmp := readResponse(ptzRelativeMoveResponse)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		result = 0
	}
	sysLog.Info(fmt.Sprintf("ptz RelativeMove end..."))
	return result
}

/*==================================PTZ控制==================================*/
//PTZ旋转
func ptz_ContinuousMove(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	result := 1
	msgz := strconv.Itoa(int(msgv.z))
	sysLog.Info(fmt.Sprintf("ptz ContinuousMove... %f, %f, %f(%v)",
		msgv.x, msgv.y, msgv.z, msgz))
	//log.Println("dev:",ipc_obj.dev)
	ptzContinuousMove := ptz.ContinuousMove{ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		Velocity: onvif.PTZSpeed{
			PanTilt: onvif.Vector2D{
				X: msgv.x,
				Y: msgv.y,
			},
			Zoom: onvif.Vector1D{
				X:     0.0,
			},
		},
		// Timeout: xsd.Duration(msgz),
	}

	ptzContinuousMoveRsp, err := ipc_obj.dev.CallMethod(ptzContinuousMove)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("ContinuousMove error %v", err))
	} else {
		tmp := readResponse(ptzContinuousMoveRsp)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		result = 0
	}
	sysLog.Info(fmt.Sprintf("ptz ContinuousMove end..."))
	return result
}

//PTZ旋转
/* 
{\"identifier\":\"RelativeMove\",\"x\":\"0.15\",\"y\":\"0\",\"sn\":\"21880A0000F2_Camera\",\"mi\":55357322,\"timestamp\":1639824114,\"ip_addr\":\"172.16.1.71\",\"port\":80,
\"ext_data\":\"{\\\"password\\\":\\\"guanmai168\\\",\\\"user\\\":\\\"admin\\\",\\\"rtsp_url\\\":\\\"rtsp://admin:guanmai168@172.16.1.71:554/mpeg4\\\"}\",\"term_addr\":\"\"}
*/
func ptz_MakeAround(ipc_obj *Ipcamera) int {
	sysLog.Info(fmt.Sprintf("ptz make a round..."))
	result := 1
	//log.Println("dev:",ipc_obj.dev)
	ptzContinuousMove := ptz.ContinuousMove{ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		Velocity : onvif.PTZSpeed{
			PanTilt: onvif.Vector2D{
				X:     0.5,
				Y:     0.0,
			},
			Zoom: onvif.Vector1D{
				X:     0.0,
			},
		},
		// Timeout: xsd.Duration(string("1")),
	}

	ptzContinuousMoveResponse, err := ipc_obj.dev.CallMethod(ptzContinuousMove)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("RelativeMove error %v", err))
		log.Printf(fmt.Sprintf("RelativeMove error %v", err))
	} else {
		tmp := readResponse(ptzContinuousMoveResponse)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		log.Printf("response %v",tmp)
		result = 0
	}

	sysLog.Info(fmt.Sprintf("ptz RelativeMove end..."))
	return result
}

func ptz_stop(ipc_obj *Ipcamera) int {
	sysLog.Info(fmt.Sprintf("ptz stop..."))
	result := 1
	//log.Println("dev:",ipc_obj.dev)
	ptzStop := ptz.Stop{ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		PanTilt  : true,
		Zoom : true,
	}

	ptzStopResponse, err := ipc_obj.dev.CallMethod(ptzStop)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("RelativeMove error %v", err))
		log.Printf(fmt.Sprintf("RelativeMove error %v", err))
	} else {
		tmp := readResponse(ptzStopResponse)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		log.Printf("response %v",tmp)
		result = 0
	}

	sysLog.Info(fmt.Sprintf("ptz RelativeMove end..."))
	return result
}

//定位到预置位
func ptz_GotoPreset(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	log.Println("ptz GotoPreset...")
	result := 1
	m_ptz_preset_req := ptz.GotoPreset{
		ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		PresetToken:  onvif.ReferenceToken(msgv.perset_name),
		Speed: onvif.PTZSpeed{
			PanTilt: onvif.Vector2D{
				X:     0.5,
				Y:     0.5,
			},
			Zoom: onvif.Vector1D{
				X:     0.0,
			},
		},
	}
	m_ptz_preset_resp, err := ipc_obj.dev.CallMethod(m_ptz_preset_req)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("GotoPreset error %v", err))
	} else {
		tmp := readResponse(m_ptz_preset_resp)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		result = 0
	}
	sysLog.Info(fmt.Sprintf("ptz GotoPreset end..."))
	return result
}

//获取预置点处理
func ptz_GetPresetsHandle(ipc_obj *Ipcamera, msgv *MqttMsg, resp *http.Response) int {
	doc := etree.NewDocument()
	data, _ := ioutil.ReadAll(resp.Body)
	if err := doc.ReadFromBytes(data); err != nil {
		return 1
	}
	var presets PresetsList
	presets.Result = 0
	presets.Error_info = "succeed"

	sysLog.Info(fmt.Sprintf("PullMessages:%s", string(data)))
	Preset := doc.FindElement("./Envelope/Body/GetPresetsResponse/Preset")
	if Preset != nil {		
		GetPresetsResponse := doc.FindElements("./Envelope/Body/GetPresetsResponse/Preset")
		for _, j := range GetPresetsResponse {
			perset_name := j.SelectAttrValue("token", "null")
			PanTilt := j.FindElement("./PTZPosition/PanTilt")
			if PanTilt == nil || perset_name == "null" {
				goto flag_1
			}
			x := PanTilt.SelectAttrValue("x", "null")
			y := PanTilt.SelectAttrValue("y", "null")
			sysLog.Info(fmt.Sprintf("perset_name:%s x:%s y:%s", x, y, perset_name))
			presets.Presets = append(presets.Presets, PresetInfo{Perset_name: perset_name, X: x, Y:y})
		}
	} else {
		sysLog.Info(fmt.Sprintf("##ignore con't find Preset"))
		goto flag_1
	}
flag_1:
	b, err := json.Marshal(presets)
	if err != nil {
		sysLog.Info(fmt.Sprintf("json err:", err))
	}
	data_report(msgv, 2, string(b))
	return 0
}

//获取预置位
func ptz_GetPresets(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("ptz GetPresets..."))
	result := 1
	m_ptz_preset_req := ptz.GetPresets{
		ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
	}
	m_ptz_preset_req_resp, err := ipc_obj.dev.CallMethod(m_ptz_preset_req)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("GetPresets error %v", err))
	} else {
		result = ptz_GetPresetsHandle(ipc_obj, msgv, m_ptz_preset_req_resp)
	}
	sysLog.Info(fmt.Sprintf("ptz GetPresets end..."))
	return result
}

//获取预置点处理
func ptz_GetPresetToursHandle(ipc_obj *Ipcamera, msgv *MqttMsg, resp *http.Response) int {
	doc := etree.NewDocument()
	data, _ := ioutil.ReadAll(resp.Body)
	if err := doc.ReadFromBytes(data); err != nil {
		return 1
	}
	var presets PresetsList
	presets.Result = 0
	presets.Error_info = "succeed"

	sysLog.Info(fmt.Sprintf("PullMessages:%s", string(data)))
	// Preset := doc.FindElement("./Envelope/Body/GetPresetsResponse/Preset")
	// if Preset != nil {
	// 	GetPresetsResponse := doc.FindElements("./Envelope/Body/GetPresetsResponse/Preset")
	// 	for _, j := range GetPresetsResponse {
	// 		perset_name := j.SelectAttrValue("token", "null")
	// 		PanTilt := j.FindElement("./PTZPosition/PanTilt")
	// 		if PanTilt == nil || perset_name == "null" {
	// 			goto flag_1
	// 		}
	// 		x := PanTilt.SelectAttrValue("x", "null")
	// 		y := PanTilt.SelectAttrValue("y", "null")
	// 		sysLog.Info(fmt.Sprintf("perset_name:%s x:%s y:%s", x, y, perset_name))
	// 		presets.Presets = append(presets.Presets, PresetInfo{Perset_name: perset_name, X: x, Y:y})
	// 	}
	// } else {
	// 	sysLog.Info(fmt.Sprintf("##ignore con't find Preset"))
	// 	goto flag_1
	// }

	b, err := json.Marshal(presets)
	if err != nil {
		sysLog.Info(fmt.Sprintf("json err:", err))
	}
	data_report(msgv, 2, string(b))
	return 0
}

//获取预置位巡视
func ptz_GetPresetTours(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("ptz GetPresets..."))
	result := 1
	m_ptz_preset_tours_req := ptz.GetPresetTours{
		ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
	}
	m_ptz_preset_tours_req_resp, err := ipc_obj.dev.CallMethod(m_ptz_preset_tours_req)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("GetPresetTours error %v", err))
	} else {
		result = ptz_GetPresetToursHandle(ipc_obj, msgv, m_ptz_preset_tours_req_resp)
	}
	sysLog.Info(fmt.Sprintf("ptz GetPresets end..."))
	return result
}


//设置预置位
func ptz_SetPreset(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("ptz SetPreset..."))
	result := 1
	m_ptz_preset_req := ptz.SetPreset{
		ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		PresetToken:  onvif.ReferenceToken(msgv.perset_name),
	}
	m_ptz_preset_req_resp, err := ipc_obj.dev.CallMethod(m_ptz_preset_req)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("SetPreset error %v", err))
	}	else {
		tmp := readResponse(m_ptz_preset_req_resp)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		result = 0		
	}
	sysLog.Info(fmt.Sprintf("ptz SetPreset end..."))
	return result
}

//删除预置位
func ptz_RemovePreset(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("ptz RemovePreset..."))
	result := 1
	m_ptz_preset_req := ptz.RemovePreset{
		ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		PresetToken:  onvif.ReferenceToken(msgv.perset_name),//PresetTokenStr,//"2",//*perset_name,
	}
	m_ptz_preset_req_resp, err := ipc_obj.dev.CallMethod(m_ptz_preset_req)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("RemovePreset error %v", err))
	} else {
		tmp := readResponse(m_ptz_preset_req_resp)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		result = 0
	}
	sysLog.Info(fmt.Sprintf("ptz RemovePreset end..."))
	return result
}

//定位到Home预置位
func ptz_GotoHomePosition(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("ptz GotoHomePosition..."))
	result := 1
	m_ptz_preset_req := ptz.GotoHomePosition{
		ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
		Speed: onvif.PTZSpeed{
			PanTilt: onvif.Vector2D{
				X:     0.0,
				Y:     0.0,
			},
			Zoom: onvif.Vector1D{
				X:     0.0,
			},
		},
	}
	m_ptz_preset_resp, err := ipc_obj.dev.CallMethod(m_ptz_preset_req)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("GotoHomePosition error %v", err))
	} else {
		tmp := readResponse(m_ptz_preset_resp)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		result = 0
	}
	sysLog.Info(fmt.Sprintf("ptz GotoHomePosition end..."))
	return result
}

//重置Home预置位
func ptz_SetHomePosition(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("ptz SetHomePosition..."))
	result := 1
	m_ptz_preset_req := ptz.SetHomePosition{
		ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken),
	}
	m_ptz_preset_req_resp, err := ipc_obj.dev.CallMethod(m_ptz_preset_req)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("SetHomePosition error %v", err))
	} else {
		tmp := readResponse(m_ptz_preset_req_resp)
		sysLog.Info(fmt.Sprintf("response %v",tmp))
		result = 0
	}
	sysLog.Info(fmt.Sprintf("ptz SetHomePosition end..."))
	return result
}

//获取RTSP地址
func ipc_GetStreamUri(ipc_obj *Ipcamera, msgv *MqttMsg) int {
	sysLog.Info(fmt.Sprintf("=======================GetStreamUri...============================================"))
	result := 1
	getStreamUri := media.GetStreamUri{ProfileToken: onvif.ReferenceToken(ipc_obj.mediaProfileToken)}
	getStreamUriResponse, err := ipc_obj.dev.CallMethod(getStreamUri)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("GetStreamUri error:%s", err))
	} else {
		//log.Printf("getStreamUriResponse:%s", readResponse(getStreamUriResponse))
		doc := etree.NewDocument()
		data, _ := ioutil.ReadAll(getStreamUriResponse.Body)		
		sysLog.Info(fmt.Sprintf("GetStreamUri:",string(data)))
		if err := doc.ReadFromBytes(data); err != nil {
			sysLog.Warning(fmt.Sprintf("ReadFromBytes error %v", err.Error()))
		} else {
			rtsp_url := doc.FindElement("./Envelope/Body/GetStreamUriResponse/MediaUri/Uri").Text()//SubscriptionReference
			if rtsp_url == "" {
				sysLog.Info(fmt.Sprintf("##ignore con't find Topic"))
			} else {
				result = 0
				rtspResult := &RtspUrlCmdRusult{}
				rtspResult.Result = result
				rtspResult.Error_info = "succeed"
				rtspResult.RtspUrl = rtsp_url
				b, err := json.Marshal(rtspResult)
				if err != nil {
					sysLog.Info(fmt.Sprintf("json err:", err))
				}
				data_report(msgv, 2, string(b))
			}
		}		
	}
	sysLog.Info(fmt.Sprintf("=======================GetStreamUri end...============================================"))
	return result
}


/*==================================事件处理==================================*/
type EventData struct {
	Topic_value    	string	`json:"Topic_value"`
	Name	string	`json:"_Name"`
	PropertyOperation		string	`json:"_PropertyOperation"`
	UtcTime	string	`json:"_UtcTime"`
	Value		string `json:"_Value"`
}

//IPC事件处理消息处理
func event_report(sn string, envdata *EventData) int {
	dr := &DataReport{}
	dr.Sn = sn
	dr.Identifier = "IpcAlarm"
	dr.Time = time.Now().Unix()
	dr.Mi = 0
	dr.Port = "TCP_CLIENT"
	envdata_json, _ := json.Marshal(envdata)
	dr.Tag_node = string(envdata_json)
	dr.Data_type = 1

	publish_topic := get_pulish_topic(1, sn, "IpcAlarm")

	data_json, _ := json.Marshal(dr)
	//sysLog.Info(fmt.Sprintf("data_json:%s", string(data_json)))
	mqtt_publish(publish_topic, string(data_json))
	return 0
}

//IPC事件处理消息处理
func ipalarm_MessageHandle(ipc_obj *Ipcamera, envdata *EventData) int {
	if strings.Index(envdata.Topic_value, "AlarmIn") >= 0 {
		identifier := "handup"
		if envdata.Value == "true" {
			identifier = "ring"
		} else if envdata.Value == "false" {
			identifier = "handup"
		} else {
			return 1
		}
		dr := &CmdSetRgltRaw{}
		dr.Sn = ipc_obj.sn
		dr.Identifier = identifier
		dr.Time = time.Now().Unix()

		publish_topic := "ipc/" + GW_SN + "/" + ipc_obj.app_key + "/device/" + ipc_obj.sn + "/data/Set_Rglt_Raw"
		data_json, _ := json.Marshal(dr)
		//sysLog.Info(fmt.Sprintf("data_json:%s", string(data_json)))
		mqtt_publish(publish_topic, string(data_json))
	}
	return 0
}

func event_PullMessagesHandle(ipc_obj *Ipcamera, resp *http.Response) {
	doc := etree.NewDocument()

	data, _ := ioutil.ReadAll(resp.Body)
	if err := doc.ReadFromBytes(data); err != nil {
		return
	}
	//sysLog.Info(fmt.Sprintf("PullMessages:%s", string(data)))
	NotificationMessage := doc.FindElement("./Envelope/Body/PullMessagesResponse/NotificationMessage")//SubscriptionReference
	if NotificationMessage == nil {
		//sysLog.Info(fmt.Sprintf("##ignore con't find NotificationMessage"))
		return
	}

	Topic_value := doc.FindElement("./Envelope/Body/PullMessagesResponse/NotificationMessage/Topic").Text()//SubscriptionReference
	if Topic_value == "" {
		sysLog.Info(fmt.Sprintf("##ignore con't find Topic"))
		return
	}
	if strings.Index(Topic_value, "Monitoring/OperatingTime") >= 0 {
		sysLog.Info(fmt.Sprintf("##ignore by OperatingTime"))
		return
	}
	envdata := &EventData{}
	envdata.Topic_value = Topic_value

	Message := doc.FindElement("./Envelope/Body/PullMessagesResponse/NotificationMessage/Message/Message")//SubscriptionReference
	if Message == nil {
		sysLog.Info(fmt.Sprintf("##ignore con't find Message"))
		return
	}
	envdata.UtcTime = Message.SelectAttrValue("UtcTime", "null")
	envdata.PropertyOperation = Message.SelectAttrValue("PropertyOperation", "null")
	if strings.Index(envdata.PropertyOperation, "Initialized") >= 0 {
		sysLog.Info(fmt.Sprintf("##ignore Initialized opt"))
		return
	}

	SimpleItem := doc.FindElement("./Envelope/Body/PullMessagesResponse/NotificationMessage/Message/Message/Data/SimpleItem")//SubscriptionReference
	if SimpleItem == nil {
		sysLog.Info(fmt.Sprintf("##ignore con't find SimpleItem msg:%s", string(data)))
		return
	}
	envdata.Name = SimpleItem.SelectAttrValue("Name", "null")
	envdata.Value = SimpleItem.SelectAttrValue("Value", "null")
	sysLog.Info(fmt.Sprintf("Topic_value:%s UtcTime:%s PropertyOperation:%s Name:%s Value:%s", envdata.Topic_value, envdata.UtcTime, envdata.PropertyOperation, envdata.Name, envdata.Value))

	if ipc_obj.app_key != "" {
		ipalarm_MessageHandle(ipc_obj, envdata)
	} else {
		event_report(ipc_obj.sn, envdata)
	}
	/*
	for index in range(len(pull_msg["NotificationMessage"])):
		syslog.syslog("----------------------index %d len %d start----------------------" %(index,len(pull_msg["NotificationMessage"])))
		syslog.syslog("Topic value:%s" %pull_msg["NotificationMessage"][index]["Topic"].value)
		syslog.syslog("Message:%s" %pull_msg["NotificationMessage"][index]["Message"].Message)
		event_info["Topic_value"] = pull_msg["NotificationMessage"][index]["Topic"].value
		if pull_msg["NotificationMessage"][index]["Topic"].value.find("AudioAnalytics") < 0:
			event_info["_UtcTime"] = pull_msg["NotificationMessage"][index]["Message"].Message["_UtcTime"]
			event_info["_PropertyOperation"] = pull_msg["NotificationMessage"][index]["Message"].Message["_PropertyOperation"]
			event_info["_Name"] = pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem["_Name"]
			event_info["_Value"] = pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem["_Value"]
			syslog.syslog("_UtcTime:%s _PropertyOperation:%s" %(pull_msg["NotificationMessage"][index]["Message"].Message["_UtcTime"],pull_msg["NotificationMessage"][index]["Message"].Message["_PropertyOperation"]))
			syslog.syslog("_Name:%s _Value:%s" %(pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem["_Name"],pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem["_Value"]))
		else:
			event_info["_UtcTime"] = pull_msg["NotificationMessage"][index]["Message"].Message["_UtcTime"]
			event_info["_Name"] = pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem[0]["_Name"]
			event_info["_Value"] = pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem[0]["_Value"]
			syslog.syslog("_UtcTime:%s" %pull_msg["NotificationMessage"][index]["Message"].Message["_UtcTime"])
			syslog.syslog("==_Name:%s _Value:%s" %(pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem[0]["_Name"], pull_msg["NotificationMessage"][index]["Message"].Message["Data"].SimpleItem[0]["_Value"]))
		event_msg_ctrl(self.host, event_info)
		syslog.syslog("----------------------index %d end----------------------" %index)*/
}

//事件监听
func event_PullMessages(ipc_obj *Ipcamera) int {
	sysLog.Info(fmt.Sprintf("=======================event_PullMessages start...============================================"))
	result := 1
	/*eventGetServiceCapabilities := event.GetServiceCapabilities{}
	eventGetServiceCapabilitiesResponse, err := ipc_obj.dev.CallMethod(eventGetServiceCapabilities, "")
	if err != nil {
		sysLog.Info(fmt.Sprintf("GetServiceCapabilities error %v", err))
		return 1
	} else {
		bs, _ := ioutil.ReadAll(eventGetServiceCapabilitiesResponse.Body)
		//log.Printf("GetServiceCapabilities: %+v %s", eventGetServiceCapabilitiesResponse.StatusCode, bs)
		sysLog.Info(fmt.Sprintf("GetServiceCapabilities: %+v %s", eventGetServiceCapabilitiesResponse.StatusCode, bs))
	}*/

	eventCreatePullPointSubscription := event.CreatePullPointSubscription{
		/*Filter: event.FilterType{
			TopicExpression: event.TopicExpressionType{
				Dialect:		xsd.AnyURI("http://www.onvif.org/ver10/tev/topicExpression/ConcreteSet"),
				TopicKinds:	"tns1:.",//"tns1:RuleEngine/CellMotionDetector/Motion",
			},
		},
		InitialTerminationTime: event.AbsoluteOrRelativeTimeType{
			//"2006-01-02 15:04:05",//xsd.DateTime.NewDateTime(nil, loginTime),//xsd.DateTime.NewDateTime(time_t),
			DateTime: "PT10S",//2021/03/1028 16:51:59",
		},
		SubscriptionPolicy: event.SubscriptionPolicy{
			//ChangedOnly: false,//true,//false,
		},*/
	}
	//eventPullMessages := event.GetEventProperties{}

	_, err := ipc_obj.dev.CallMethod(eventCreatePullPointSubscription)
	if err != nil {
		sysLog.Info(fmt.Sprintf("CreatePullPointSubscription error %v", err))
		return 1
	/* } else {
		ipc_obj.dev.Authenticate("", "") */
	}
	sysLog.Info(fmt.Sprintf("=======================CreatePullPointSubscription end...============================================"))
	for {
		time.Sleep(time.Duration(1)*time.Second)
		//sysLog.Info(fmt.Sprintf("=======================eventPullMessages...============================================"))
		eventPullMessages := event.PullMessages{MessageLimit: 1}
		eventPullMessagesResponse, err := ipc_obj.dev.CallMethod(eventPullMessages)
		if err != nil {
			sysLog.Warning(fmt.Sprintf("PullMessages error %v", err))
		} else {
			event_PullMessagesHandle(ipc_obj, eventPullMessagesResponse)			
			//fmt.Println(readResponse(systemDateAndTymeResponse))
		}
	}
	sysLog.Info(fmt.Sprintf("ptz event end..."))
	return result
}

//事件监听
func event_HFW5443F1(ipc_obj *Ipcamera) int {
	subscribe_url := fmt.Sprintf("http://%s/onvif/event_service", ipc_obj.ip_addr)

	sub_body := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://www.onvif.org/ver10/events/wsdl/EventPortType/CreatePullPointSubscriptionRequest</a:Action><a:MessageID>urn:uuid:42c68618-3415-4112-9b42-49c1a8d7110f</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand="1">http://%s/onvif/event_service</a:To></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><CreatePullPointSubscription xmlns="http://www.onvif.org/ver10/events/wsdl"/></s:Body></s:Envelope>`, ipc_obj.ip_addr)

	trans := http.DefaultTransport.(*http.Transport).Clone()
	trans.MaxIdleConns = 20
	trans.MaxConnsPerHost = 100
	trans.MaxIdleConnsPerHost = 100
	netcli := &http.Client{ Timeout: time.Second * 15, Transport: trans }
again:
	/* subscribe to IPCamera events */
	resp, err := netcli.Post(subscribe_url,
		"application/soap+xml; charset=utf-8", bytes.NewBufferString(sub_body))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error, failed to subscribe to events: %v\n", err)
		time.Sleep(time.Duration(10) * time.Second)
		goto again;
	}

	/* read from response buffer */
	hbuf, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error, failed to read from response: %v\n", err)
		time.Sleep(time.Duration(10) * time.Second)
		goto again;
	}

	resp.Body.Close()
	if len(hbuf) == 0 {
		fmt.Fprintf(os.Stderr, "Error, invalid empty response!\n")
		time.Sleep(time.Duration(10) * time.Second)
		goto again;
	}
	/* convert event subscription response to a string */
	ebuf := string(hbuf[:])
	reg_exp, _ := regexp.Compile(`Subscription\?Idx=\d+`)
	subidx := reg_exp.FindString(ebuf)
	if len(subidx) == 0 {
		fmt.Fprintf(os.Stderr, "Error, subidx not found in =>\n%s\n", ebuf)
		time.Sleep(time.Duration(10) * time.Second)
		goto again;
	}

	// count := int32(0)
	reg_exp, _ = regexp.Compile(`SmokeDetection`)
	fmt.Fprintf(os.Stderr, "WTF subscription index: %s\n", subidx)
	event_url := fmt.Sprintf("http://%s/onvif/%s", ipc_obj.ip_addr, subidx)
	fetch_body := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"><s:Header><a:Action s:mustUnderstand="1">http://www.onvif.org/ver10/events/wsdl/PullPointSubscription/PullMessagesRequest</a:Action><a:MessageID>urn:uuid:060390fe-5103-483d-aa3e-e802af40d803</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand="1">%s</a:To></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><PullMessages xmlns="http://www.onvif.org/ver10/events/wsdl"><Timeout>PT10S</Timeout><MessageLimit>2</MessageLimit></PullMessages></s:Body></s:Envelope>`, event_url)
	for {
		time.Sleep(1 * time.Second)
		ev_resp, ev_err := netcli.Post(event_url,
			"application/soap+xml; charset=utf-8", bytes.NewBufferString(fetch_body))
		if ev_err != nil {
			netcli.CloseIdleConnections()
			netcli = &http.Client{ Timeout: time.Second * 15, Transport: trans }
			fmt.Fprintf(os.Stderr, "Error, failed to get events: %v\n", ev_err)
			time.Sleep(time.Duration(10) * time.Second)
			goto again;
		}

		ev_buf, ev_err := ioutil.ReadAll(ev_resp.Body)
		if ev_err != nil {
			fmt.Fprintf(os.Stderr, "Error, failed to fetch events: %v\n", ev_err)
			time.Sleep(time.Duration(10) * time.Second)
			goto again;
		}

		ev_resp.Body.Close()
		if len(ev_buf) == 0 {
			fmt.Fprintf(os.Stderr, "Error, invalid empty events reply.\n")
			time.Sleep(time.Duration(10) * time.Second)
			continue
		}

		if reg_exp.Match(ev_buf) {
			mmsg := MqttMsg{ sn: ipc_obj.sn, mi: 0, identifier: "AlarmStatus" }
			data_report(&mmsg, 1, `{"status":1}`)
			/* evbuf := string(ev_buf[:])
			fmt.Fprintf(os.Stderr, "WTF -> Received events: %s\n", evbuf)
		} else {
			count += 1;
			fmt.Fprintf(os.Stderr, "WTF -> Received events, not found: %d\n", count) */
		}
	}
	return 0
}
 
func readResponse(resp *http.Response) string {
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	return string(b)
}

//获取profileToken
func GetMediaProfileToken(resp *http.Response) string {
	doc := etree.NewDocument()
	data, _ := ioutil.ReadAll(resp.Body)
	sysLog.Info(fmt.Sprintf("=======================GetMediaProfileToken start...============================================"))
	sysLog.Info(fmt.Sprintf("GetMediaProfileToken:",string(data)))

	if err := doc.ReadFromBytes(data); err != nil {
		sysLog.Warning(fmt.Sprintf("ReadFromBytes error %v", err.Error()))
		return ""
	}
	services := doc.FindElements("./Envelope/Body/GetProfilesResponse/Profiles")//./Envelope/Body/GetProfilesResponse/Profiles
	for _, j := range services {
		token := j.SelectAttrValue("token", "MediaProfile000")
		mediaProfileToken := token
		return mediaProfileToken
	}
	return ""
}

//获取能力集
func GetCapabilities(ipc_obj *Ipcamera) {
	log.Printf("=======================GetCapabilities start...============================================")
	getCapabilities := device.GetCapabilities{Category: "All"}
	getCapabilitiesResponse, err := ipc_obj.dev.CallMethod(getCapabilities)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("GetCapabilities error:%s", err))
	} else {
		log.Printf("GetCapabilities:%s", readResponse(getCapabilitiesResponse))
	}

	log.Printf("=======================GetServiceCapabilities start...============================================")
	getServiceCapabilities := device.GetServiceCapabilities{}
	getServiceCapabilitiesResponse, err := ipc_obj.dev.CallMethod(getServiceCapabilities)
	if err != nil {
		sysLog.Warning(fmt.Sprintf("GetServiceCapabilities error:%s", err))
	} else {
		log.Printf("GetServiceCapabilities:%s", readResponse(getServiceCapabilitiesResponse))
	}
}

/*==================================配置文件信息获取==================================*/
//判断文件是否存在  存在返回 true 不存在返回false
func checkFileIsExist(filename string) bool {
	var exist = true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		exist = false
	}
	return exist
}

//读取文件内容
func get_file_data(filename string) []byte {
	var bytes []byte// 该字节切片用于存放文件所有字节
	if checkFileIsExist(filename) == false {
		return bytes
	}
	file, _ := os.Open(filename)
  defer file.Close()	
	buf := make([]byte, 1024000)// 字节切片缓存 存放每次读取的字节

	for {
			count, err := file.Read(buf)//// 返回本次读取的字节数			
			if err == io.EOF {// 检测是否到了文件末尾
					break;
			}
			currBytes := buf[:count]// 取出本次读取的数据
			bytes = append(bytes, currBytes...)// 将读取到的数据 追加到字节切片中
	}
	// 将字节切片转为字符串 最后打印出来文件内容
	//sysLog.Info(fmt.Sprintf("filename:%s data:%s", filename,string(bytes)))
	return bytes
}

//获取GW_SN
func get_sn(filename string) {
	file_ini := get_file_data(filename)
	file_data := string(file_ini)
	sn_index := strings.Index(file_data, "sn")
	if sn_index >= 0 {
		GW_SN = string([]byte(file_data)[sn_index+5:sn_index+5+12])
		sysLog.Info(fmt.Sprintf("GW_SN:%s", GW_SN))
	}
}

//获取摄像机配置信息
func get_node(filename string) {
	jsonData := get_file_data(filename)//[]byte(`{"nodes_cfg":[{"connect_port":"RS485_2","depth":0,"product_key":"558763928","sn":"W4c5hOz9","template_id":"558763928","term_addr":"1"},{"connect_port":"TCP_CLIENT","depth":0,"ext_data":"{\"user\":\"admin\",\"password\":\"a12345678\",\"rtsp_url\":\"rtsp://admin:a12345678@172.16.254.65:554/cam/realmonitor?channel=1&subtype=2&unicast=true&proto=Onvif\",\"ftp_config\":\"dywlftp:VhAJP4_mdkPs!7zx!X3DLdi7kZ@122.224.150.210:50197\"}","product_key":"249713985","sn":"OwVzNHEj","tcp_ip_addr":"172.16.254.65","tcp_port":"80","template_id":"249713985"}],"version":"VVZo9E2xTbvn"}`)
	//jsonData := []byte(`{"Name":"Eve","Age":6,"Parents":["Alice","Bob"]}`)
	var node_cfg interface{}
	json.Unmarshal(jsonData, &node_cfg)
	nodes := node_cfg.(map[string]interface{})
	for k, v := range nodes {
			switch v := v.(type) {
			case string:
					//fmt.Println(k, v, "(string)")
			case float64:
					//fmt.Println(k, v, "(float64)")
			case []interface{}:
					//fmt.Println(k, "(array):")
					for _, u := range v {
							node_v := u.(map[string]interface{})
							if k == "nodes_cfg" && node_v["connect_port"] == "TCP_CLIENT" && node_v["ext_data"] != nil {
								var ext_cfg interface{}
								ext_str := fmt.Sprintln(node_v["ext_data"])
								ext_slice := []byte(ext_str)
								json.Unmarshal(ext_slice, &ext_cfg)
								ext_data := ext_cfg.(map[string]interface{})
								sysLog.Info(fmt.Sprintf("--ipc_cnt %d sn:%s", ipc_cnt, node_v["sn"].(string)))
								if ext_data["user"] != nil && ext_data["password"] != nil {
									ipc_object[ipc_cnt].sn = node_v["sn"].(string)//fmt.Sprintln(node_v["sn"])
									ipc_object[ipc_cnt].ip_addr = node_v["tcp_ip_addr"].(string)
									ipc_object[ipc_cnt].port = node_v["tcp_port"].(string)
									ipc_object[ipc_cnt].user = ext_data["user"].(string)
									ipc_object[ipc_cnt].password = ext_data["password"].(string)
									ipc_object[ipc_cnt].rtsp_url = ext_data["rtsp_url"].(string)
									if ext_data["app_key"] != nil {
										ipc_object[ipc_cnt].app_key = ext_data["app_key"].(string)
									}
									if ext_data["event_enable"] != nil {
										sysLog.Info(fmt.Sprintf("--ipc_cnt %d event_enable:%v", ipc_cnt, ext_data["event_enable"]))
										ipc_object[ipc_cnt].event_enable = ext_data["event_enable"].(float64)
									}		
									sysLog.Info(fmt.Sprintf("ipc_object[%d].sn:%s", ipc_cnt, ipc_object[ipc_cnt].sn))
									ipc_cnt = ipc_cnt + 1
								}
							}
					}
			default:
				sysLog.Info(fmt.Sprintf("k %v v %v %s", k, v, "(unknown)"))
			}
	}
}

func main() {
	var err error
	sysLog, err = syslog.Dial("", "",syslog.LOG_USER, "GOIpcamera")
	if err != nil {
		log.Fatal(err)
	}
	stop_all_video_streaming()
	get_sn("/app/config/fac.ini")
	get_node("/app/node/nodes_cfg.json")
	//test_json1()
	subscribe()
	//publish()
	go mqtt_message_handle()

	for i := 0; i < ipc_cnt; i++ {
		ipaddr := ipc_object[i].ip_addr + ":" + ipc_object[i].port
		sysLog.Info(fmt.Sprintf("%d-%d ipc_object[i].sn: %s ipaddr:%s", ipc_cnt, i, ipc_object[i].sn, ipaddr))
		//刚启动检测一下设备状态
		if ipc_object[i].status_time == 0 {
			ipc_object[i].online = ipcamera_online_check(ipc_object[i].ip_addr)
			ipc_object[i].status_time = 1
		}
		ipc_object[i].dev, err = goonvif.NewDevice(goonvif.DeviceParams{Xaddr: ipaddr, Username: ipc_object[i].user, Password: ipc_object[i].password})
		if err != nil {
			//panic(err)
			sysLog.Warning(fmt.Sprintf("sn:%s does not support onvif !!!",ipc_object[i].sn))
			continue
		}
		//Authorization
		//ipc_object[i].dev.Authenticate(ipc_object[i].user, ipc_object[i].password)

		mediaGetProfiles := media.GetProfiles{}
		mediaGetProfilesResponse, err := ipc_object[i].dev.CallMethod(mediaGetProfiles)
		if err != nil {
			sysLog.Err(fmt.Sprintf("mediaGetProfiles resp error! %v",err))
		} else {
			ipc_object[i].online = true
			ipc_object[i].mediaProfileToken = GetMediaProfileToken(mediaGetProfilesResponse)
			sysLog.Info(fmt.Sprintf("sn:%s mediaProfileToken:%v",ipc_object[i].sn, ipc_object[i].mediaProfileToken))
			GetCapabilities(&ipc_object[i])
			if ipc_object[i].event_enable != 0 {
				evfunc := os.Getenv("IPCAMERA_EVENT_FUNC")
				if evfunc == "HFW5443F1" {
					go event_HFW5443F1(&ipc_object[i]);
				} else {
					go event_PullMessages(&ipc_object[i])
				}
			}
		}
	}

	for {
		//sysLog.Info(fmt.Sprintf("ipcamera ..."))
		time.Sleep(time.Duration(600)*time.Second)
	}
}

/*==================================MQTT连接==================================*/
// 订阅回调
func subCallBackFunc(client MQTT.Client, msg MQTT.Message) {
	sysLog.Info(fmt.Sprintf("Subscribe: Topic is [%s]; msg is [%s]\n", msg.Topic(), string(msg.Payload())))
	get_rglt_data(string(msg.Payload()))
	
}

var connectHandler MQTT.OnConnectHandler = func(client MQTT.Client) {
	sysLog.Info(fmt.Sprintf("MQTT Connected"))
	mqtt_connected = 1
}

var connectLostHandler MQTT.ConnectionLostHandler = func(client MQTT.Client, err error) {
	sysLog.Info(fmt.Sprintf("MQTT Connect lost: %v\n", err))
	client.Disconnect(250)
	mqtt_connected = 0
}
 
// 连接MQTT服务
func connMQTT(broker, user, passwd string) (bool, MQTT.Client) {
	opts := MQTT.NewClientOptions()
	opts.AddBroker(broker)
	opts.SetUsername(user)
	opts.SetPassword(passwd)
	opts.SetClientID("go_ipcamera_mqtt_client")
	opts.OnConnect = connectHandler
  opts.OnConnectionLost = connectLostHandler
 
	mc := MQTT.NewClient(opts)
	if token := mc.Connect(); token.Wait() && token.Error() != nil {
		return false, mc
	}
 
	return true, mc
}
 
// 订阅消息
func subscribe() {
	// sub的用户名和密码
	b := true
	b, mc = connMQTT("tcp://127.0.0.1:1883", "A", "B")
	if !b {
		sysLog.Info(fmt.Sprintf("sub connMQTT failed"))
		return
	}
	mc.Subscribe("ipc/+/TCP_CLIENT/device/+/data/Set_Rglt_Raw", 0x00, subCallBackFunc)
	sysLog.Info(fmt.Sprintf("Subscribe ipc/+/TCP_CLIENT/device/+/data/Set_Rglt_Raw..."))
}
 
// 发布消息
func mqtt_publish(topic string, payload string) int {
	mc.Publish(topic, 0x00, true, payload)
	log.Printf("topic:%s payload:%s", topic, payload)
	sysLog.Info(fmt.Sprintf("Publish topic:%s payload:%s", topic, payload))
	return 0
}

/*==================================消息队列==================================*/
type Queue struct {
	l *list.List
	m sync.Mutex
}

func NewQueue() *Queue {
	return &Queue{l: list.New()}
}

func (q *Queue) PushBack(v interface{}) {
	if v == nil {
			return
	}
	q.m.Lock()
	defer q.m.Unlock()
	q.l.PushBack(v)
}

func (q *Queue) Front() *list.Element {
	q.m.Lock()
	defer q.m.Unlock()
	return q.l.Front()
}

func (q *Queue) Remove(e *list.Element) {
	if e == nil {
			return
	}
	q.m.Lock()
	defer q.m.Unlock()
	q.l.Remove(e)
}

func (q *Queue) Len() int {
	q.m.Lock()
	defer q.m.Unlock()
	return q.l.Len()
}
