2024-10-30 18:51:51 +08:00
|
|
|
|
package goonvif
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"net"
|
|
|
|
|
"net/http"
|
2024-11-08 00:17:42 +08:00
|
|
|
|
"net/url"
|
2024-10-30 18:51:51 +08:00
|
|
|
|
"strings"
|
2024-10-31 18:22:47 +08:00
|
|
|
|
"time"
|
2024-10-30 18:51:51 +08:00
|
|
|
|
|
2024-10-31 18:22:47 +08:00
|
|
|
|
"git.pyer.club/kingecg/goonvif/onvif"
|
|
|
|
|
device "git.pyer.club/kingecg/goonvif/onvif/device"
|
|
|
|
|
"git.pyer.club/kingecg/goonvif/onvif/media"
|
2024-11-01 16:53:22 +08:00
|
|
|
|
ptz "git.pyer.club/kingecg/goonvif/onvif/ptz"
|
2024-10-31 18:22:47 +08:00
|
|
|
|
sdk "git.pyer.club/kingecg/goonvif/onvif/sdk/device"
|
|
|
|
|
media_sdk "git.pyer.club/kingecg/goonvif/onvif/sdk/media"
|
2024-11-01 16:53:22 +08:00
|
|
|
|
ptz_sdk "git.pyer.club/kingecg/goonvif/onvif/sdk/ptz"
|
2024-10-31 18:22:47 +08:00
|
|
|
|
wsdiscovery "git.pyer.club/kingecg/goonvif/onvif/ws-discovery"
|
|
|
|
|
"git.pyer.club/kingecg/goonvif/onvif/xsd"
|
|
|
|
|
onvifmodel "git.pyer.club/kingecg/goonvif/onvif/xsd/onvif"
|
|
|
|
|
"git.pyer.club/kingecg/goonvif/replay"
|
|
|
|
|
replay_sdk "git.pyer.club/kingecg/goonvif/sdk/replay"
|
2024-10-30 18:51:51 +08:00
|
|
|
|
search_sdk "git.pyer.club/kingecg/goonvif/sdk/search"
|
|
|
|
|
"git.pyer.club/kingecg/goonvif/search"
|
|
|
|
|
"github.com/beevik/etree"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Device struct {
|
|
|
|
|
onvif.DeviceParams
|
2024-10-31 18:22:47 +08:00
|
|
|
|
device *onvif.Device
|
|
|
|
|
ctx context.Context
|
|
|
|
|
mediaCapablities *media.Capabilities
|
2024-10-30 18:51:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 16:53:22 +08:00
|
|
|
|
const (
|
|
|
|
|
panTiltSpace string = "http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace"
|
|
|
|
|
zoomSpace string = "http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace"
|
|
|
|
|
)
|
|
|
|
|
|
2024-10-30 18:51:51 +08:00
|
|
|
|
var ErrAuthRequired = errors.New("auth required")
|
|
|
|
|
|
|
|
|
|
func NewDevice(params onvif.DeviceParams) *Device {
|
|
|
|
|
return &Device{DeviceParams: params, ctx: context.Background()}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) check() error {
|
|
|
|
|
if d.device != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if d.Username == "" || d.Password == "" {
|
|
|
|
|
return ErrAuthRequired
|
|
|
|
|
}
|
|
|
|
|
if d.HttpClient == nil {
|
|
|
|
|
d.HttpClient = new(http.Client)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
device, err := onvif.NewDevice(d.DeviceParams)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
d.device = device
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
func (d *Device) GetCapabilities() (interface{}, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
cap := device.GetCapabilities{Category: "All"}
|
|
|
|
|
|
|
|
|
|
getCapabilitiesResponse, err := sdk.Call_GetCapabilities(d.ctx, d.device, cap)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
r := getCapabilitiesResponse.Capabilities
|
|
|
|
|
return r, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetRecordingSummary() (search.RecordingSummary, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return search.RecordingSummary{}, err
|
|
|
|
|
}
|
|
|
|
|
resp, rerr := search_sdk.Call_GetRecordingSummary(d.ctx, d.device, search.GetRecordingSummary{})
|
|
|
|
|
if rerr != nil {
|
|
|
|
|
return search.RecordingSummary{}, rerr
|
|
|
|
|
}
|
|
|
|
|
return resp.Summary, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetEndpoints() (map[string]string, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return d.device.GetServices(), nil
|
|
|
|
|
}
|
2024-10-31 18:22:47 +08:00
|
|
|
|
|
|
|
|
|
func (d *Device) FindRecording(start time.Time, end time.Time) ([]string, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
// recordFilterTemplate := "not (boolean(//EarliestRecording > %s) or boolean(//LatestRecording < %s) "
|
|
|
|
|
// filter := fmt.Sprintf(recordFilterTemplate, end.Format("2006-01-02T15:04:05Z"), start.Format("2006-01-02T15:04:05Z"))
|
|
|
|
|
fr, ferr := search_sdk.Call_FindRecordings(d.ctx, d.device, search.FindRecordings{})
|
|
|
|
|
if ferr != nil {
|
|
|
|
|
return nil, ferr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
searchToken := fr.SearchToken
|
|
|
|
|
resultlist := make([]search.RecordingInformation, 0)
|
|
|
|
|
for {
|
|
|
|
|
gr, gerr := search_sdk.Call_GetRecordingSearchResults(d.ctx, d.device, search.GetRecordingSearchResults{SearchToken: searchToken})
|
|
|
|
|
if gerr != nil {
|
|
|
|
|
return nil, gerr
|
|
|
|
|
}
|
|
|
|
|
if len(gr.ResultList.RecordingInformation) > 0 {
|
|
|
|
|
resultlist = append(resultlist, gr.ResultList.RecordingInformation...)
|
|
|
|
|
}
|
|
|
|
|
if gr.ResultList.SearchState == "Completed" {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
recordingTokes := make([]string, 0)
|
|
|
|
|
for _, r := range resultlist {
|
|
|
|
|
rstartStr := string(xsd.DateTime(r.EarliestRecording))
|
|
|
|
|
rendStr := string(xsd.DateTime(r.LatestRecording))
|
|
|
|
|
rstart, _ := time.Parse("2006-01-02T15:04:05Z", rstartStr)
|
|
|
|
|
rend, _ := time.Parse("2006-01-02T15:04:05Z", rendStr)
|
|
|
|
|
if rstart.After(end) || rend.Before(start) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
recordingTokes = append(recordingTokes, string(r.RecordingToken))
|
|
|
|
|
}
|
|
|
|
|
return recordingTokes, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetReplayUriTcp(token string) (string, error) {
|
|
|
|
|
return d.GetReplayUri(token, TcpUnicastStreamSetup)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetReplayUriUdpUnicast(token string) (string, error) {
|
|
|
|
|
return d.GetReplayUri(token, UdpUnicastStreamSetup)
|
|
|
|
|
}
|
|
|
|
|
func (d *Device) GetReplayUriUdpMulticast(token string) (string, error) {
|
|
|
|
|
return d.GetReplayUri(token, RtspMulticastStreamSetup)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetMediaCapablities() (media.Capabilities, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return media.Capabilities{}, err
|
|
|
|
|
}
|
|
|
|
|
if d.mediaCapablities != nil {
|
|
|
|
|
return *d.mediaCapablities, nil
|
|
|
|
|
}
|
|
|
|
|
resp, err := media_sdk.Call_GetServiceCapabilities(d.ctx, d.device, media.GetServiceCapabilities{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return media.Capabilities{}, err
|
|
|
|
|
}
|
|
|
|
|
d.mediaCapablities = &resp.Capabilities
|
|
|
|
|
return resp.Capabilities, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetReplayUri 获取设备回放的URI。
|
|
|
|
|
// 参数:
|
|
|
|
|
//
|
|
|
|
|
// token: 用于标识回放的令牌。
|
|
|
|
|
// transport: 传输协议,UDP=RTP/UDP, RTSP=RTP/RTSP/TCP or HTTP=RTP/RTSP/HTTP/TCP。
|
|
|
|
|
// multiCast: 布尔值,指示是否使用多播。
|
|
|
|
|
//
|
|
|
|
|
// 返回值:
|
|
|
|
|
//
|
|
|
|
|
// string: 回放的URI。
|
|
|
|
|
// error: 错误信息,如果执行操作时发生错误。
|
|
|
|
|
func (d *Device) GetReplayUri(token string, stream onvifmodel.StreamSetup) (string, error) {
|
|
|
|
|
// 检查设备状态
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
// 调用SDK方法获取回放URI
|
|
|
|
|
resp, err := replay_sdk.Call_GetReplayUri(d.ctx, d.device, replay.GetReplayUri{StreamSetup: stream, RecordingToken: onvifmodel.ReferenceToken(token)})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回获取到的URI
|
2024-11-08 00:17:42 +08:00
|
|
|
|
// return string(resp.Uri), nil
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
urlobj, _ := url.Parse(string(resp.Uri))
|
|
|
|
|
urlobj.User = url.UserPassword(d.Username, d.Password)
|
|
|
|
|
return urlobj.String(), nil
|
2024-10-31 18:22:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetStreamUri(stream onvifmodel.StreamSetup, profileToken string) (string, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
getStreamUri := media.GetStreamUri{StreamSetup: stream}
|
|
|
|
|
if profileToken != "" {
|
|
|
|
|
getStreamUri.ProfileToken = onvifmodel.ReferenceToken(profileToken)
|
|
|
|
|
}
|
|
|
|
|
resp, rerr := media_sdk.Call_GetStreamUri(d.ctx, d.device, getStreamUri)
|
|
|
|
|
// resp, rerr := media_sdk.Call_GetStreamUri(d.ctx, d.device, media.GetStreamUri{})
|
2024-11-08 00:17:42 +08:00
|
|
|
|
// return string(resp.MediaUri.Uri), rerr
|
|
|
|
|
if rerr != nil {
|
|
|
|
|
return "", rerr
|
|
|
|
|
}
|
|
|
|
|
urlobj, _ := url.Parse(string(resp.MediaUri.Uri))
|
|
|
|
|
urlobj.User = url.UserPassword(d.Username, d.Password)
|
|
|
|
|
return urlobj.String(), nil
|
2024-10-31 18:22:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetStreamUriTcp(profileToken string) (string, error) {
|
|
|
|
|
return d.GetStreamUri(TcpUnicastStreamSetup, profileToken)
|
|
|
|
|
}
|
|
|
|
|
func (d *Device) GetStreamUriUdp(profileToken string) (string, error) {
|
|
|
|
|
return d.GetStreamUri(UdpUnicastStreamSetup, profileToken)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) GetSnapshotUri(profileToken string) (string, error) {
|
|
|
|
|
return "", nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-06 19:58:33 +08:00
|
|
|
|
func (d *Device) GetVideoSources() ([]onvifmodel.VideoSource, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := media_sdk.Call_GetVideoSources(d.ctx, d.device, media.GetVideoSources{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp.VideoSources, nil
|
|
|
|
|
}
|
|
|
|
|
func (d *Device) GetVideoSourceConfigurations() ([]onvifmodel.VideoSourceConfiguration, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := media_sdk.Call_GetVideoSourceConfigurations(d.ctx, d.device, media.GetVideoSourceConfigurations{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp.Configurations, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) RelateVideoSource(profileToken, videoSourceToken string) error {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
profileResp, err := media_sdk.Call_GetProfile(d.ctx, d.device, media.GetProfile{ProfileToken: onvifmodel.ReferenceToken(profileToken)})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
profile := profileResp.Profile
|
|
|
|
|
profile.VideoSourceConfiguration.SourceToken = onvifmodel.ReferenceToken(videoSourceToken)
|
|
|
|
|
// profile.VideoSourceConfiguration.Name = onvifmodel.Name(videoSourceToken)
|
|
|
|
|
_, serr := media_sdk.Call_SetVideoSourceConfiguration(d.ctx, d.device, media.SetVideoSourceConfiguration{Configuration: profile.VideoSourceConfiguration, ForcePersistence: xsd.Boolean(true)})
|
|
|
|
|
if serr != nil {
|
|
|
|
|
return serr
|
|
|
|
|
}
|
|
|
|
|
_, aerr := media_sdk.Call_AddVideoSourceConfiguration(d.ctx, d.device, media.AddVideoSourceConfiguration{ProfileToken: onvifmodel.ReferenceToken(profileToken), ConfigurationToken: profile.VideoSourceConfiguration.Token})
|
|
|
|
|
return aerr
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 18:22:47 +08:00
|
|
|
|
func (d *Device) GetProfiles() ([]onvifmodel.Profile, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := media_sdk.Call_GetProfiles(d.ctx, d.device, media.GetProfiles{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp.Profiles, nil
|
|
|
|
|
}
|
2024-11-06 19:58:33 +08:00
|
|
|
|
func (d *Device) GetVideoEncoderConfigurations() ([]onvifmodel.VideoEncoderConfiguration, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := media_sdk.Call_GetVideoEncoderConfigurations(d.ctx, d.device, media.GetVideoEncoderConfigurations{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp.Configurations, nil
|
|
|
|
|
}
|
2024-11-01 16:53:22 +08:00
|
|
|
|
|
|
|
|
|
func (d *Device) GetDeviceInformation() (interface{}, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := sdk.Call_GetDeviceInformation(d.ctx, d.device, device.GetDeviceInformation{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) PTZNodes() ([]onvifmodel.PTZNode, error) {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp, err := ptz_sdk.Call_GetNodes(d.ctx, d.device, ptz.GetNodes{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp.PTZNode, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) continueMove(panTilt onvifmodel.Vector2D, zoom float64) error {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
v := onvifmodel.PTZSpeed{}
|
|
|
|
|
panTilt.Space = xsd.AnyURI(panTiltSpace)
|
|
|
|
|
v.PanTilt = panTilt
|
|
|
|
|
v.Zoom = onvifmodel.Vector1D{
|
|
|
|
|
Space: xsd.AnyURI(zoomSpace),
|
|
|
|
|
X: zoom,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, perr := ptz_sdk.Call_ContinuousMove(d.ctx, d.device, ptz.ContinuousMove{
|
|
|
|
|
ProfileToken: "Profile_1",
|
|
|
|
|
Velocity: v,
|
|
|
|
|
})
|
|
|
|
|
return perr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) StopMove() error {
|
|
|
|
|
err := d.check()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, perr := ptz_sdk.Call_Stop(d.ctx, d.device, ptz.Stop{})
|
|
|
|
|
return perr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pan 执行设备的水平和垂直移动。
|
|
|
|
|
// 此函数通过指定的x和y坐标来控制设备的移动方向和距离。
|
|
|
|
|
// 参数:
|
|
|
|
|
//
|
|
|
|
|
// x - 水平移动的坐标值。range: -1 to 1
|
|
|
|
|
// y - 垂直移动的坐标值。range: -1 to 1
|
|
|
|
|
//
|
|
|
|
|
// 返回值:
|
|
|
|
|
//
|
|
|
|
|
// 如果移动操作成功,返回nil;否则返回错误。
|
|
|
|
|
func (d *Device) Pan(x, y float64) error {
|
|
|
|
|
// 创建一个二维向量来表示移动的方向和距离。
|
|
|
|
|
v := onvifmodel.Vector2D{X: x, Y: y}
|
|
|
|
|
// 调用continueMove函数来执行实际的移动操作,移动速度设置为0。
|
|
|
|
|
return d.continueMove(v, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *Device) Zoom(zoom float64) error {
|
|
|
|
|
return d.continueMove(onvifmodel.Vector2D{}, zoom)
|
|
|
|
|
}
|
2024-10-30 18:51:51 +08:00
|
|
|
|
func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) ([]Device, error) {
|
|
|
|
|
// Call a ws-discovery Probe Message to Discover NVT type Devices
|
|
|
|
|
devices, err := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + onvif.NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nvtDevicesSeen := make(map[string]bool)
|
|
|
|
|
nvtDevices := make([]Device, 0)
|
|
|
|
|
|
|
|
|
|
for _, j := range devices {
|
|
|
|
|
doc := etree.NewDocument()
|
|
|
|
|
if err := doc.ReadFromString(j); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, xaddr := range doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") {
|
|
|
|
|
xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2]
|
|
|
|
|
if !nvtDevicesSeen[xaddr] {
|
|
|
|
|
dev := NewDevice(onvif.DeviceParams{Xaddr: strings.Split(xaddr, " ")[0]})
|
|
|
|
|
nvtDevicesSeen[xaddr] = true
|
|
|
|
|
nvtDevices = append(nvtDevices, *dev)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nvtDevices, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Discovery() ([]Device, error) {
|
|
|
|
|
ifaces, err := listLocalNetworkInterfaces()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
devices := make([]Device, 0)
|
|
|
|
|
|
|
|
|
|
for _, iface := range ifaces {
|
|
|
|
|
idevices, err := GetAvailableDevicesAtSpecificEthernetInterface(iface)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
devices = append(devices, idevices...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return devices, nil
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func listLocalNetworkInterfaces() ([]string, error) {
|
|
|
|
|
|
|
|
|
|
interfaces, err := net.Interfaces()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ifaceNames []string
|
|
|
|
|
for _, iface := range interfaces {
|
|
|
|
|
// 判断是否为局域网
|
|
|
|
|
if (iface.Flags&net.FlagUp) == 0 || (iface.Flags&net.FlagRunning) == 0 || (iface.Flags&net.FlagLoopback) != 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ifaceNames = append(ifaceNames, iface.Name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ifaceNames, nil
|
|
|
|
|
}
|