First open source version.
commit
7ce577bb2f
@ -0,0 +1,8 @@
|
||||
build
|
||||
dist
|
||||
dist_installer
|
||||
docs/*.html
|
||||
docs/index.htm
|
||||
*.pyc
|
||||
#*#
|
||||
*~
|
@ -0,0 +1,88 @@
|
||||
Net2Scripting
|
||||
===
|
||||
|
||||
Python scripting for the Paxton Net2 SDK.
|
||||
|
||||
This project builds a setup file, that will install a Python scripting environment
|
||||
that can communicate with Paxton Net2, using the SDK (or a direct DB connection).
|
||||
|
||||
|
||||
### **Read this first**
|
||||
The scripts in this package are currently quite inflexible, in that they assume
|
||||
that packages are installed in particular directories. For future versions this
|
||||
should be changed to maybe an environment setting or a 'user' file containg the
|
||||
definitions. (this file should be in .gitignore)
|
||||
|
||||
For now, the install directories are specificly marked in the requirements.
|
||||
|
||||
### Requirements
|
||||
The NetScripting project relies on the following:
|
||||
* An x86 Windows environment (64-bit did not work). Windows7-32bit should be ok.
|
||||
* DotNet 4.0
|
||||
- Installing Paxton Net2 V5.x should take care of that
|
||||
- The Paxton Net2 OEMClientLibrary dlls (v5.01) are included in this package.
|
||||
* Python x86 3.4.4
|
||||
- Is the Python environment
|
||||
- Download from http://www.python.org
|
||||
- Install in C:\WinPrg\Python34\ !!!
|
||||
* pythonnet-2.3.0-cp34-cp34m-win32.whl
|
||||
- Is Python .net extension
|
||||
- Download from http://pythonnet.sourceforge.net
|
||||
- Install with pip install <wheel file>
|
||||
* py2exe-0.9.2.2-py33.py34-none-any.whl
|
||||
- To create a python executable
|
||||
- Download from http://www.py2exe.org
|
||||
- Install with pip install <wheel file>
|
||||
- Remove 'clr' in c:/WinPrg/Python34/lib/site-packages/py2exe/hooks.py !!!
|
||||
- Note that py2exe does/did not support 3.5 and up yet
|
||||
* Inno Setup 5.4.2 (higher versions most likely also work)
|
||||
- To create an installer (uses the inno_script.iss)
|
||||
- Download from http://www.jrsoftware.org
|
||||
- Install in C:\Program Files\Inno Setup 5\ !!!
|
||||
|
||||
The following convenience batch files are available:
|
||||
* run.bat
|
||||
- To run de scripting tool while testing sample code
|
||||
- It will wait after execution and run again after pressing enter (ctrl C=exit)
|
||||
- Do no just click it in explorer; this will not work
|
||||
- Create a sendTo shortcut to it, to execute python scripts from the explorer.
|
||||
+ type "shell:sento" in the file explorer address bar to access the
|
||||
SentTo folder
|
||||
+ Create shortcut to run.bat and call it "Test with Net2Scripting"
|
||||
* make.bat
|
||||
- Creates an exe-based distro in ./dist
|
||||
- You can click it in explorer
|
||||
* build_installer.bat
|
||||
- Requires make.bat to run first
|
||||
- Creates a 'SetupNet2Scripting.exe' in ./dist_installer
|
||||
- You can click it in explorer
|
||||
|
||||
Beware of the following:
|
||||
* That the doc's generated, need to be defined in src/docgen.py
|
||||
* That for py2exe, new modules must be defined in 'Net2Scripting.py',
|
||||
to have py2exe include them in the exe generation.
|
||||
* That the makefile.bat will show warnings (which we ignore for now):
|
||||
```
|
||||
8 missing Modules
|
||||
------------------
|
||||
? Paxton imported from net2xs
|
||||
? _dbm imported from dbm.ndbm
|
||||
? log4net imported from pylog4net
|
||||
? multiprocessing.SimpleQueue imported from concurrent.futures.process
|
||||
? netbios imported from uuid
|
||||
? win32evtlog imported from logging.handlers
|
||||
? win32evtlogutil imported from logging.handlers
|
||||
? win32wnet imported from uuid
|
||||
```
|
||||
|
||||
### Configuration
|
||||
Runtime configuration file is: src/Net2Scripting.exe.config
|
||||
(complies to the dotnet standard)
|
||||
|
||||
### Version updates
|
||||
To update the version number, adjust the following files:
|
||||
* inno_script.iss
|
||||
* src/settings.py
|
||||
|
||||
### License
|
||||
MIT
|
@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
del dist_installer\* /s/q > nul
|
||||
|
||||
rem taskkill /IM explorer.exe /F
|
||||
rem cd /d %userprofile%\AppData\Local
|
||||
rem del IconCache.db /a
|
||||
rem start explorer.exe
|
||||
|
||||
"C:\Program Files\Inno Setup 5\ISCC.exe" inno_script.iss
|
||||
pause
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,121 @@
|
||||
Net2Scripting
|
||||
=============
|
||||
|
||||
The Net2Scripting program is an add-on for the Paxton Net2 software.
|
||||
It offers the possibility to run Python scripts against the Net2 SDK or
|
||||
straight against the Net2 database.
|
||||
Using the SDK, you can access remote Net2 servers. Using the direct database
|
||||
interface, only local access is possible.
|
||||
|
||||
Scripting allows ad-hoc functionality to be created, without compilation.
|
||||
Useful for proof of concepts, filling Net2 with test data, simple door
|
||||
control, Net2 data exports, etc.
|
||||
|
||||
REQUIREMENTS
|
||||
* A PC running a recent Windows version. The software has been tested on
|
||||
WinXP(SP3), Win7 and Win8.
|
||||
* Administrator rights.
|
||||
* The Net2 V5.1 or higher software package.
|
||||
|
||||
INSTALLATION
|
||||
* Run the installer program, which will create an IntoAccess folder in the
|
||||
Programs menu with a shortcut to Net2Scripting, install the executable,
|
||||
libraries, samples and documentation.
|
||||
* Optionally edit the Net2Scripting.exe.config file that can be found in the
|
||||
program installation folder.
|
||||
(by default it contains a reference to a sample script
|
||||
'samples/user_script.py')
|
||||
|
||||
USAGE
|
||||
* Option 1: Edit the Net2Scripting.exe.config file, changing the
|
||||
'user_script' setting to point to your script. Run the Net2Scripting
|
||||
program.
|
||||
* Option 2: Edit the Net2Scripting.exe.config file, adding more entries to
|
||||
the 'appSetting' section. Run the Net2Scripting program adding the parameter
|
||||
/k=<keyword>, where <keyword> points to the script you want to execute.
|
||||
* Option 3: Run the Net2Scripting program, adding the path to the script you
|
||||
wish to execute as parameter.
|
||||
* If you run the Net2Scripting using the explorer, you may want the window
|
||||
containing the script result to remain open. Set the appSetting
|
||||
'confirm_wait' to 'true' to have it wait for the <enter> key before closing.
|
||||
If you want to run a script using a scheduler, set this option to 'false'.
|
||||
* By default, the application logs fairly much and both to the console as
|
||||
well as to a (rolling) file. Change the root 'level' value from DEBUG to
|
||||
INFO, WARN, ERROR or FATAL to make it less verbose. You can add or remove
|
||||
any log4net conpatible log appenders to suit your needs.
|
||||
Beware that the scripting program locks the log file, so if you run more
|
||||
than one instance, it will complain about not being able to log.
|
||||
|
||||
DOCUMENTATION / HELP
|
||||
* Some pydoc generated 'help' for the API is placed in the 'docs'
|
||||
subdirectory of the installation folder. It does not hurt however to also
|
||||
keep the Net2 API doc at hand while scripting.
|
||||
* A number of script samples are provided, meant for those with some basic
|
||||
knowledge of Python. For a full Python language reference, check out
|
||||
http://www.python.org/doc/
|
||||
Not all Net2 SDK functions are available through the python api yet.
|
||||
If you would like to add stuff however, check out the'inheritance.py'
|
||||
sample to get you started.
|
||||
* The (free) Net2Query tool, available at http://www.intoaccess.com, can
|
||||
also of use to get some insight on available tables, column names and
|
||||
data types.
|
||||
|
||||
BACKGROUND
|
||||
* Originally created by CRC Value.
|
||||
* Maintained (sort of) by IntoAccess from July 2017 - May 2018.
|
||||
* Open source from May 2018 onward.
|
||||
* The package websites:
|
||||
- http://www.net2scripting.nl
|
||||
- https://github.com/cor-kalis/Net2Scripting
|
||||
|
||||
ACKNOWLEDGEMENTS
|
||||
* The Net2Scripting program is created using the following libraries/tools:
|
||||
- Python => http://www.python.org
|
||||
- Python.Net => http://pythonnet.sourceforge.net
|
||||
- Py2exe => http://www.py2exe.org
|
||||
- Paxton.Net2.OEMClientLibrary => http://www.paxton.co.uk
|
||||
- Inno Setup => http://www.jrsoftware.org
|
||||
|
||||
LICENSE
|
||||
* MIT
|
||||
|
||||
DISCLAIMER
|
||||
* You will use this software at your own risk. No responsibility is accepted for
|
||||
any damage it may accidentally cause.
|
||||
|
||||
HISTORY
|
||||
* V1.0 was published in April 2013. Initial version
|
||||
* V1.1 was published in July 2013.
|
||||
- Fixes a module scope issue (imported modules not recognized in functions).
|
||||
- User image manipulation.
|
||||
- Confirm wait, even on a sys.exit().
|
||||
- Allow remote native db access.
|
||||
- Net2Plus module detection using udp broadcasts.
|
||||
- SQLServer detection using udp broadcasts.
|
||||
- Default values for add_user and modify_user (less hassle).
|
||||
* V1.2 was published in July 2013.
|
||||
- Enable linecache.getline by default, so 'list' in pdb.set_trace() shows
|
||||
something useful. (disable in config by setting 'linecache_enable' to false)
|
||||
- Added __scripting_version__ var, to test script compatibility.
|
||||
- Fixed bug in Net2XS.deactivate_user fuction.
|
||||
* V1.3 was published in September 2013.
|
||||
- Fix / implement acu monitoring
|
||||
- Fix get_users / get_user_by_id bug
|
||||
* V1.4 was published in October 2013.
|
||||
- Fix password encryption on remote native database connection
|
||||
* V2.0 was published in April 2014.
|
||||
- Net2 V5 compatible.
|
||||
* V2.1 was published in July 2014.
|
||||
- Fix hard coded event and subtype bug in add_event_record.
|
||||
* V2.2 was published in October 2014.
|
||||
- Removed misplaced delete_image parameter in add_user
|
||||
* V2.3 was published in December 2015.
|
||||
- Upgraded to Python 2.7.10 and included most standard Python modules.
|
||||
* V2.4 was published in August 2016.
|
||||
- Added the option to use either a dotnet or python date time objects for
|
||||
the add_user and modify_user methods.
|
||||
- Added delete_card and get_cards methods.
|
||||
* V3.0 was published in August 2017.
|
||||
- Upgrade to Python 3.4.4
|
||||
* V4.0 was published in May 2018.
|
||||
- Made open source.
|
@ -0,0 +1,47 @@
|
||||
; Script generated by the Inno Setup Script Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "Net2Scripting"
|
||||
#define MyAppVersion "4.0"
|
||||
#define MyAppPublisher "Net2Scripting"
|
||||
#define MyAppURL "http://www.net2scripting.nl/"
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application.
|
||||
; Do not use the same AppId value in installers for other applications.
|
||||
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||
AppId={{3940C8D4-5D5F-43CF-B92E-4086ED2198B2}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
;AppVerName={#MyAppName} {#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={pf}\Paxton Access/{#MyAppName}
|
||||
DefaultGroupName=Paxton Access
|
||||
InfoAfterFile=.\dist\docs\README.txt
|
||||
OutputDir=.\dist_installer
|
||||
OutputBaseFilename=SetupNet2Scripting
|
||||
SetupIconFile=.\dist\resources\Net2Scripting.ico
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
Source: ".\dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\Net2Scripting"; Filename: "{app}\{#MyAppName}.exe"
|
||||
Name: "{group}\Net2Scripting API"; Filename: "{app}\docs\index.htm"
|
||||
Name: "{commondesktop}\Net2Scripting"; Filename: "{app}\{#MyAppName}.exe"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
|
||||
[UninstallRun]
|
@ -0,0 +1,21 @@
|
||||
@echo off
|
||||
|
||||
echo --- Cleaning...
|
||||
del build\* /s/q > nul
|
||||
del dist\* /s/q > nul
|
||||
del docs\*.html /s/q > nul
|
||||
echo --- Cleaning done
|
||||
|
||||
echo --- Generating docs
|
||||
cd src
|
||||
c:/WinPrg/Python34/python docgen.py
|
||||
cd ..
|
||||
echo --- Generating docs done
|
||||
|
||||
echo --- Building executable
|
||||
cd src
|
||||
c:/WinPrg/Python34/python setup.py py2exe
|
||||
cd ..
|
||||
echo --- Building executable done
|
||||
|
||||
pause
|
Binary file not shown.
After Width: | Height: | Size: 320 KiB |
@ -0,0 +1,6 @@
|
||||
@echo off
|
||||
cd src
|
||||
:again
|
||||
c:\WinPrg\Python34\python Net2Scripting.py %1
|
||||
pause
|
||||
goto again
|
Binary file not shown.
@ -0,0 +1,43 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
|
||||
</configSections>
|
||||
<appSettings>
|
||||
<add key="user_script" value="samples/user_script.py" />
|
||||
<add key="confirm_wait" value="true" />
|
||||
<add key="log_stacktrace" value="true" />
|
||||
<add key="enable_linecache" value="true" />
|
||||
</appSettings>
|
||||
<log4net>
|
||||
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
|
||||
<param name="File" value="${ALLUSERSPROFILE}/Application Data/IntoAccess/Net2Scripting/Net2Scripting.log"/>
|
||||
<param name="AppendToFile" value="true"/>
|
||||
<param name="rollingStyle" value="Size"/>
|
||||
<param name="maxSizeRollBackups" value="10"/>
|
||||
<param name="maximumFileSize" value="10MB"/>
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<param name="Header" value=""/>
|
||||
<param name="Footer" value=""/>
|
||||
<param name="ConversionPattern" value="%d [%c,%t] %-5p %m%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<param name="Header" value="[Header]\r\n" />
|
||||
<param name="Footer" value="[Footer]\r\n" />
|
||||
<param name="ConversionPattern" value="%d [%c,%t] %-5p %m%n" />
|
||||
</layout>
|
||||
</appender>
|
||||
<root>
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="RollingFileAppender" />
|
||||
<appender-ref ref="ConsoleAppender" />
|
||||
</root>
|
||||
<logger name="Paxton">
|
||||
<level value="ERROR" />
|
||||
<appender-ref ref="RollingFileAppender" />
|
||||
<appender-ref ref="ConsoleAppender" />
|
||||
</logger>
|
||||
</log4net>
|
||||
</configuration>
|
@ -0,0 +1,329 @@
|
||||
"""
|
||||
Main program for Net2Scripting.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import settings
|
||||
import traceback
|
||||
|
||||
# This is just to force py2exe to include these packages
|
||||
if False == True:
|
||||
import net2xs
|
||||
import net2dbxs
|
||||
import network
|
||||
# Not required but nice to have
|
||||
import abc
|
||||
import aifc
|
||||
import antigravity
|
||||
import argparse
|
||||
import ast
|
||||
import asynchat
|
||||
import asyncio
|
||||
import asyncore
|
||||
import base64
|
||||
import bdb
|
||||
import binhex
|
||||
import bisect
|
||||
import bz2
|
||||
import cProfile
|
||||
import calendar
|
||||
import cgi
|
||||
import cgitb
|
||||
import chunk
|
||||
import cmd
|
||||
import code
|
||||
import codecs
|
||||
import codeop
|
||||
import collections
|
||||
import colorsys
|
||||
import compileall
|
||||
import concurrent
|
||||
import configparser
|
||||
import contextlib
|
||||
import copy
|
||||
import copyreg
|
||||
# import crypt
|
||||
import csv
|
||||
import ctypes
|
||||
# import curses
|
||||
import datetime
|
||||
# import dbm
|
||||
import decimal
|
||||
import difflib
|
||||
import dis
|
||||
import distutils
|
||||
import doctest
|
||||
import threading
|
||||
import email
|
||||
import encodings
|
||||
# import ensurepip
|
||||
import enum
|
||||
import filecmp
|
||||
import fileinput
|
||||
import fnmatch
|
||||
import formatter
|
||||
import fractions
|
||||
import ftplib
|
||||
import functools
|
||||
import genericpath
|
||||
import getopt
|
||||
import getpass
|
||||
import gettext
|
||||
import glob
|
||||
import gzip
|
||||
import hashlib
|
||||
import heapq
|
||||
import hmac
|
||||
import html
|
||||
import http
|
||||
import idlelib
|
||||
import imaplib
|
||||
import imghdr
|
||||
import imp
|
||||
import importlib
|
||||
import inspect
|
||||
import io
|
||||
import ipaddress
|
||||
import json
|
||||
import keyword
|
||||
import lib2to3
|
||||
import linecache
|
||||
import locale
|
||||
import logging
|
||||
import lzma
|
||||
# import macpath
|
||||
# import macurl2path
|
||||
import mailbox
|
||||
import mailcap
|
||||
import mimetypes
|
||||
import modulefinder
|
||||
import msilib
|
||||
# import multiprocessing
|
||||
import netrc
|
||||
import nntplib
|
||||
import ntpath
|
||||
import nturl2path
|
||||
import numbers
|
||||
import opcode
|
||||
import operator
|
||||
import optparse
|
||||
import os
|
||||
import pathlib
|
||||
import pdb
|
||||
import pickle
|
||||
import pickletools
|
||||
import pipes
|
||||
import pkgutil
|
||||
import platform
|
||||
import plistlib
|
||||
import poplib
|
||||
import posixpath
|
||||
import pprint
|
||||
import profile
|
||||
import pstats
|
||||
import pty
|
||||
import pyclbr
|
||||
import pydoc
|
||||
import pydoc_data
|
||||
import queue
|
||||
import quopri
|
||||
import random
|
||||
import re
|
||||
import reprlib
|
||||
import rlcompleter
|
||||
import runpy
|
||||
import sched
|
||||
import selectors
|
||||
import shelve
|
||||
import shlex
|
||||
import shutil
|
||||
import site
|
||||
# import smtpd
|
||||
import smtplib
|
||||
import sndhdr
|
||||
import socket
|
||||
import socketserver
|
||||
import sqlite3
|
||||
# import compile
|
||||
# import constants
|
||||
# import parse
|
||||
import ssl
|
||||
import stat
|
||||
import statistics
|
||||
import string
|
||||
import stringprep
|
||||
import struct
|
||||
import subprocess
|
||||
import sunau
|
||||
import symbol
|
||||
import symtable
|
||||
import sysconfig
|
||||
import tabnanny
|
||||
import tarfile
|
||||
import telnetlib
|
||||
import tempfile
|
||||
import test
|
||||
import textwrap
|
||||
import this
|
||||
import threading
|
||||
import timeit
|
||||
import tkinter
|
||||
import token
|
||||
import tokenize
|
||||
import trace
|
||||
import traceback
|
||||
import tracemalloc
|
||||
import tty
|
||||
import turtle
|
||||
import turtledemo
|
||||
import types
|
||||
import unittest
|
||||
import urllib
|
||||
import uu
|
||||
import uuid
|
||||
import venv
|
||||
import warnings
|
||||
import wave
|
||||
import weakref
|
||||
import webbrowser
|
||||
import wsgiref
|
||||
import xdrlib
|
||||
import xml
|
||||
import xmlrpc
|
||||
import zipfile
|
||||
|
||||
|
||||
# Setup logging first
|
||||
from pylog4net import Log4Net
|
||||
try:
|
||||
Log4Net.read_config(settings.CONFIG_FILE)
|
||||
except Exception as e:
|
||||
print('Log error: %s' % (str(e)))
|
||||
sys.exit(1)
|
||||
|
||||
from config import Config
|
||||
try:
|
||||
cfg = Config(settings.CONFIG_FILE)
|
||||
except Exception as e:
|
||||
print('Config error: %s' % (str(e)))
|
||||
sys.exit(1)
|
||||
|
||||
# Running as py2exe: check if linecache should be enabled
|
||||
# if hasattr(sys, 'frozen'):
|
||||
# if cfg.get("enable_linecache", True, bool):
|
||||
# # Monkey patch it back to the original value
|
||||
# import linecache
|
||||
# linecache.getline = linecache.orig_getline
|
||||
# del linecache
|
||||
|
||||
# Log4net logger
|
||||
logger = Log4Net.get_logger('Net2Scripting')
|
||||
|
||||
|
||||
def run(script_file):
|
||||
"""Run script file
|
||||
"""
|
||||
try:
|
||||
if not os.path.isfile(script_file):
|
||||
raise Exception("User script '%s' does not exist!" % (script_file))
|
||||
|
||||
# Get script dir and add it to the module search path
|
||||
sys.path.insert(0, os.path.dirname(script_file))
|
||||
|
||||
print("Press '<ctrl> C' to quit...")
|
||||
logger.Debug("Calling user script '%s'" % (script_file))
|
||||
try:
|
||||
# Strip Net2Scripting param from arguments
|
||||
sys.argv = sys.argv[1:]
|
||||
# Explicitly provide dict, to prevent scope issues
|
||||
with open(script_file) as f:
|
||||
code = compile(f.read(), script_file, 'exec')
|
||||
exec(code,
|
||||
{"__name__": "__main__",
|
||||
"__file__": script_file,
|
||||
"__scripting_version__": settings.VERSION})
|
||||
except KeyboardInterrupt:
|
||||
logger.Debug("User interrupt")
|
||||
|
||||
except Exception as ex:
|
||||
|
||||
msg = "Fatal error: " + str(ex)
|
||||
if cfg.get("log_stacktrace", default=False, vtype=bool):
|
||||
msg += "\n" + traceback.format_exc()
|
||||
logger.Fatal(msg)
|
||||
return 1
|
||||
|
||||
|
||||
def run_key(script_key):
|
||||
"""Run file referenced by setting keyword
|
||||
"""
|
||||
try:
|
||||
script_key = script_key.strip()
|
||||
if not script_key:
|
||||
raise "The provided script keyword is empty!"
|
||||
|
||||
# Fetch setting from config
|
||||
cfg.check_required([(script_key, str)])
|
||||
|
||||
return run(cfg.get(script_key))
|
||||
|
||||
except Exception as ex:
|
||||
logger.Fatal("Fatal error: " + str(ex))
|
||||
return 1
|
||||
|
||||
|
||||
def confirm_wait():
|
||||
"""Wait for user confirmation
|
||||
"""
|
||||
# Fetch setting from config
|
||||
wait = cfg.get("confirm_wait", default=False, vtype=bool)
|
||||
if wait:
|
||||
print()
|
||||
input("Press enter to continue: ")
|
||||
|
||||
|
||||
# Main program entry
|
||||
if __name__ == '__main__':
|
||||
|
||||
ret_val = 0
|
||||
|
||||
try:
|
||||
if len(sys.argv) > 1:
|
||||
arg = sys.argv[1]
|
||||
|
||||
if arg == "/?":
|
||||
print()
|
||||
print("Net2Scripting V%s, by Net2 Solutions" % (settings.VERSION))
|
||||
print()
|
||||
print(" /? = help")
|
||||
print(" /k:keyword = run script referenced by appSettings keyword")
|
||||
print(" <script file path> = run given script")
|
||||
print()
|
||||
print(" By default, the script referenced by the 'user_script' setting is run.")
|
||||
print(" Settings are located in the 'Net2Scripting.exe.config' file.")
|
||||
print(" Also modify this file if you prefer different log settings.")
|
||||
print
|
||||
ret_val = 1
|
||||
|
||||
# Run by keyword
|
||||
elif arg.startswith("/k:"):
|
||||
ret_val = run_key(arg.split(":", 1)[1])
|
||||
|
||||
# Run as file
|
||||
elif not arg.startswith("/"):
|
||||
ret_val = run(arg)
|
||||
|
||||
# Unknown option
|
||||
else:
|
||||
print("Unknown parameter: " + arg)
|
||||
ret_val = 1
|
||||
else:
|
||||
# Run as default
|
||||
ret_val = run_key("user_script")
|
||||
|
||||
finally:
|
||||
# Wait for user confirmation when required
|
||||
confirm_wait()
|
||||
|
||||
# Return with value
|
||||
sys.exit(ret_val)
|
@ -0,0 +1,99 @@
|
||||
"""
|
||||
Module to read dot type config settings from python
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
|
||||
from datetime import datetime
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
|
||||
class ConfigError(Exception):
|
||||
"""Config exception class
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Config(object):
|
||||
"""Class for reading and writing to the config file
|
||||
"""
|
||||
|
||||
def __init__(self, config_file):
|
||||
"""Read config file
|
||||
"""
|
||||
self.config_file = config_file
|
||||
|
||||
self._app_settings = {}
|
||||
|
||||
if not os.path.isfile(config_file):
|
||||
raise ConfigError('Failed to find config file "%s"' % config_file)
|
||||
|
||||
# Read document
|
||||
tree = ET.ElementTree()
|
||||
|
||||
tree.parse(config_file)
|
||||
|
||||
# Obtain appSettings
|
||||
element = tree.find('appSettings')
|
||||
if element is None:
|
||||
raise ConfigError(
|
||||
'Required "appSettings" section is missing in config file' %
|
||||
self.config_file)
|
||||
for item in element.findall('add'):
|
||||
if 'key' in item.attrib and 'value' in item.attrib:
|
||||
key = item.attrib['key']
|
||||
val = item.attrib['value']
|
||||
self._app_settings[key] = val
|
||||
else:
|
||||
raise ConfigError(
|
||||
'Encountered appSetting with missing attributes')
|
||||
|
||||
def _parse_as_datetime(self, val):
|
||||
"""Attempt to parse val as datetime
|
||||
"""
|
||||
if re.match(r'\d+:\d+:\d+', val):
|
||||
fmt = '%H:%M:%S'
|
||||
elif re.match(r'\d\d\d\d-\d+-\d+', val):
|
||||
fmt = '%Y-%m-%d'
|
||||
elif re.match(r'\d\d\d\d-\d+-\d+ \d+:\d+:\d+', val):
|
||||
fmt = '%Y-%m-%d %H:%M:%S'
|
||||
else:
|
||||
raise ValueError('Illegal datetime format')
|
||||
|
||||
return datetime.strptime(val, fmt)
|
||||
|
||||
def check_required(self, item_list):
|
||||
for key, vtype in item_list:
|
||||
val = self._app_settings.get(key)
|
||||
if val is None:
|
||||
raise ConfigError('Required setting for "%s" is missing' % key)
|
||||
if vtype != str:
|
||||
try:
|
||||
if vtype == datetime:
|
||||
self._parse_as_datetime(val)
|
||||
else:
|
||||
vtype(val)
|
||||
except ValueError:
|
||||
raise ConfigError(
|
||||
'Required setting for "%s" (%s), is no %s type' %
|
||||
(key, val, vtype.__name__))
|
||||
|
||||
def get(self, key, default=None, vtype=str):
|
||||
"""Obtain typecasted value
|
||||
"""
|
||||
val = self._app_settings.get(key, default)
|
||||
if val is None:
|
||||
return None
|
||||
if vtype == str:
|
||||
return val
|
||||
if vtype == datetime:
|
||||
try:
|
||||
return self._parse_as_datetime(val)
|
||||
except ValueError:
|
||||
return default
|
||||
if vtype == bool:
|
||||
return val.lower() == 'true'
|
||||
try:
|
||||
return vtype(val)
|
||||
except ValueError:
|
||||
return default
|
@ -0,0 +1,78 @@
|
||||
"""
|
||||
Helper module to generate the pydoc based
|
||||
"""
|
||||
import os
|
||||
import settings
|
||||
import sys
|
||||
|
||||
from glob import glob
|
||||
from pydoc import writedoc
|
||||
|
||||
# So we can find the libs
|
||||
sys.path.append('libs')
|
||||
|
||||
PACKAGES = ["net2base",
|
||||
"net2xs", "net2xs.conversions", "net2xs.deftypes",
|
||||
"net2dbxs",
|
||||
"network", "network.net2plus", "network.sqlserver"]
|
||||
|
||||
INDEX = "index.htm"
|
||||
|
||||
|
||||
def read_file(file_name):
|
||||
"""Read file content
|
||||
"""
|
||||
with open(file_name) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def alter_doc(content):
|
||||
"""Change doc content
|
||||
- Strip reference to local python file
|
||||
- Replace index with our own index file
|
||||
"""
|
||||
pos1 = content.find("<a href=\"file:///")
|
||||
if pos1 >= 0:
|
||||
pos2 = content.find("</a>", pos1)
|
||||
content = content[:pos1] + content[pos2 + len("</a>"):]
|
||||
|
||||
content = content.replace('<a href=".">index</a>',
|
||||
'<a href="index.htm">index</a>')
|
||||
return content
|
||||
|
||||
|
||||
def alter_index(content):
|
||||
"""Change index content
|
||||
- Replace version place holder with actual version
|
||||
"""
|
||||
content = content.replace('#VERSION#', settings.VERSION)
|
||||
return content
|
||||
|
||||
|
||||
def write_file(file_name, content):
|
||||
"""Write content as file
|
||||
"""
|
||||
with open(file_name, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Create docs
|
||||
for p in PACKAGES:
|
||||
writedoc(p)
|
||||
|
||||
# For each html, generated into src, write altered to docs
|
||||
for src in glob("*.html"):
|
||||
dest = '../docs/' + src
|
||||
content = read_file(src)
|
||||
content = alter_doc(content)
|
||||
|
||||
write_file(dest, content)
|
||||
os.remove(src)
|
||||
|
||||
# Write index with correct version to docs
|
||||
content = read_file(INDEX)
|
||||
dest = '../docs/' + INDEX
|
||||
content = alter_index(content)
|
||||
write_file(dest, content)
|
@ -0,0 +1,29 @@
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
body {
|
||||
color: #142313;
|
||||
font-family: Tahoma, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
color: darkSlateGrey;
|
||||
background-color: #f0f0f8;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h2>Net2Scripting V#VERSION# API documentation</h2>
|
||||
<br>
|
||||
<img src="Net2Scripting.png">
|
||||
<br>
|
||||
<br>
|
||||
<p><a href="net2xs.html">Net2XS for access through the SDK</a></p>
|
||||
<p><a href="net2dbxs.html">Net2DBXS for direct database access</a></p>
|
||||
<p><a href="network.html">Network tools related to Net2 access</a></p>
|
||||
<p><a href="README.txt">Application README</a></p>
|
||||
<br>
|
||||
<p><h5><a href="http://www.net2scripting.nl">www.net2scripting.nl</a></h5></p>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,43 @@
|
||||
"""
|
||||
Module offering base class for net2 access
|
||||
"""
|
||||
|
||||
|
||||
class Net2Base(object):
|
||||
"""Net2 access base class
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def safe_get_row_val(cls, row, col):
|
||||
"""Null aware fetching of row data
|
||||
|
||||
Returns None if the database column is NULL
|
||||
"""
|
||||
|
||||
if row.IsNull(col):
|
||||
return None
|
||||
return row[col]
|
||||
|
||||
|
||||
@classmethod
|
||||
def dataset_to_str(cls, dataset):
|
||||
"""Convert dataset to string presentation
|
||||
"""
|
||||
|
||||
if not dataset:
|
||||
return "None"
|
||||
if dataset.Tables.Count < 1:
|
||||
return "Empty"
|
||||
|
||||
res = []
|
||||
for table in dataset.Tables:
|
||||
res.append("Table: %s" % (table.TableName))
|
||||
cols = []
|
||||
for col in table.Columns:
|
||||
cols.append(col.ColumnName)
|
||||
for row in table.Rows:
|
||||
row_res = []
|
||||
for i in range(len(cols)):
|
||||
row_res.append("%s=%s" % (cols[i], row.get_Item(i)))
|
||||
res.append(", ".join(row_res))
|
||||
return "\n".join(res)
|
@ -0,0 +1,146 @@
|
||||
"""
|
||||
The module that offers direct access to the Paxton Net2 database,
|
||||
not using the SDK.
|
||||
"""
|
||||
import clr
|
||||
|
||||
from threading import RLock
|
||||
|
||||
clr.AddReference('System.Data')
|
||||
from System.Data.SqlClient import SqlConnection, SqlDataAdapter, SqlCommand
|
||||
from System.Data import DataSet, CommandType, ParameterDirection
|
||||
|
||||
from net2base import Net2Base
|
||||
from pylog4net import Log4Net
|
||||
|
||||
|
||||
class Net2DBXSException(Exception):
|
||||
"""Exception class for net2 database xs
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class DBCommand(object):
|
||||
"""With wrapper for SqlCommand
|
||||
"""
|
||||
def __init__(self, query, con):
|
||||
self._cmd = SqlCommand(query, con)
|
||||
|
||||
def __enter__(self):
|
||||
return self._cmd
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if (self._cmd):
|
||||
self._cmd.Dispose()
|
||||
|
||||
|
||||
class Net2DBXS(Net2Base):
|
||||
"""Net2 direct database access class
|
||||
"""
|
||||
|
||||
# Class variables
|
||||
_logger = Log4Net.get_logger('Net2DBXS')
|
||||
_lock = RLock()
|
||||
|
||||
def __init__(self):
|
||||
"""Class constructor
|
||||
"""
|
||||
self._con = None
|
||||
self._connected = False
|
||||
|
||||
def __enter__(self):
|
||||
"""With enter
|
||||
"""
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
"""With exit
|
||||
"""
|
||||
self.dispose()
|
||||
|
||||
def _check_connect(self):
|
||||
"""Check database connection
|
||||
Raises Net2DBXSException if not connected.
|
||||
"""
|
||||
if not self._connected:
|
||||
raise Net2DBXSException("Not connected to the database")
|
||||
|
||||
def connect(self,
|
||||
user_name="sdk_user",
|
||||
password="E56ABED4-2918-44F9-A110-71B61B47142A",
|
||||
db='Net2',
|
||||
server=None):
|
||||
"""Connect to database
|
||||
|
||||
If the server parameter is None, named pipes are used.
|
||||
For remote connections, the server parameter should contain the
|
||||
windows name.
|
||||
"""
|
||||
self.dispose()
|
||||
|
||||
with Net2DBXS._lock:
|
||||
if not server:
|
||||
Net2DBXS._logger.Debug('Connecting through named pipes')
|
||||
self._con = SqlConnection(
|
||||
r"server=\\.\pipe\MSSQL$NET2\sql\query;database=%s;uid=%s;password=%s" %
|
||||
(db, user_name, password))
|
||||
else:
|
||||
Net2DBXS._logger.Debug('Connecting over the network')
|
||||
self._con = SqlConnection(
|
||||
r"server=%s\NET2;database=%s;uid=%s;password=%s" %
|
||||
(server, db, user_name, password))
|
||||
self._con.Open()
|
||||
self._connected = True
|
||||
Net2DBXS._logger.Debug('Connected')
|
||||
|
||||
def query_db(self, query):
|
||||
"""Perform database query and return data in dataset
|
||||
|
||||
Returns a dataset.
|
||||
"""
|
||||
self._check_connect()
|
||||
|
||||
with DBCommand(query, self._con) as cmd:
|
||||
adapter = SqlDataAdapter()
|
||||
adapter.SelectCommand = cmd
|
||||
data = DataSet()
|
||||
adapter.Fill(data)
|
||||
|
||||
return data
|
||||
|
||||
def run_stored_procedure(self, sp_name, **params):
|
||||
"""Run stored procedured
|
||||
|
||||
Returns the stored procedure result value.
|
||||
"""
|
||||
with DBCommand(sp_name, self._con) as cmd:
|
||||
cmd.CommandType = CommandType.StoredProcedure
|
||||
# Add parameters
|
||||
for name, value in params.iteritems():
|
||||
cmd.Parameters.AddWithValue("@%s" % name, value)
|
||||
# Add return code
|
||||
cmd.Parameters.AddWithValue("@result", -1)
|
||||
cmd.Parameters["@result"].Direction = ParameterDirection.ReturnValue
|
||||
# Execute
|
||||
cmd.ExecuteNonQuery()
|
||||
return cmd.Parameters["@result"].Value
|
||||
|
||||
def get_all_tables(self):
|
||||
"""Convenience function to retrieve all known tables / views
|
||||
|
||||
Returns a dataset.
|
||||
"""
|
||||
return self.query_db(
|
||||
"select * from INFORMATION_SCHEMA.Tables order by TABLE_NAME")
|
||||
|
||||
def dispose(self):
|
||||
"""Dispose database connection
|
||||
"""
|
||||
with Net2DBXS._lock:
|
||||
if self._con:
|
||||
try:
|
||||
self._con.Close()
|
||||
self._con = None
|
||||
Net2DBXS._logger.Debug('Disposed')
|
||||
except:
|
||||
pass
|
@ -0,0 +1,999 @@
|
||||
"""
|
||||
The module that offers access to the Paxton Net2 server
|
||||
"""
|
||||
import clr
|
||||
import os
|
||||
import sys
|
||||
|
||||
import settings
|
||||
from net2xs.conversions import date_time_to_net, flex_date_time_to_net, \
|
||||
time_zones_to_py, access_levels_to_py, access_level_detail_to_py
|
||||
from datetime import datetime
|
||||
from net2base import Net2Base
|
||||
from pylog4net import Log4Net
|
||||
from threading import RLock
|
||||
from System import Array
|
||||
from System.Data import DataSet
|
||||
from System.Reflection import Assembly
|
||||
|
||||
# Minimal required net2 version
|
||||
MIN_NET2_VERSION = 501
|
||||
# Paxton assembly
|
||||
PAXTON_ASSEMBLY = 'Paxton.Net2.OEMClientLibrary'
|
||||
|
||||
|
||||
def readable_min_version():
|
||||
"""Show a user readable minimum version
|
||||
"""
|
||||
major = MIN_NET2_VERSION / 100
|
||||
minor = MIN_NET2_VERSION - major * 100
|
||||
return "%d.%d" % (major, minor)
|
||||
|
||||
|
||||
class Net2XSException(Exception):
|
||||
"""Exception class for net2 xs
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# The code below is required to determine if the already installed
|
||||
# Net2 version can be used, or that the packaged version is required.
|
||||
try:
|
||||
# Obtain paxton assembly reference
|
||||
asm = Assembly.LoadWithPartialName(PAXTON_ASSEMBLY)
|
||||
|
||||
# Found: check the version
|
||||
if asm:
|
||||
ver = asm.GetName().Version
|
||||
if ver.Major * 100 + ver.Minor < MIN_NET2_VERSION:
|
||||
raise Net2XSException(
|
||||
'Only Net2 V%s or higher is supported' %
|
||||
readable_min_version())
|
||||
|
||||
# Not found: enable the packaged paxton libs
|
||||
else:
|
||||
# Add path lib path to search path
|
||||
PAXTON_LIB_DIR = os.path.join(settings.LIB_DIR, 'paxton')
|
||||
if PAXTON_LIB_DIR not in sys.path:
|
||||
sys.path.append(PAXTON_LIB_DIR)
|
||||
|
||||
try:
|
||||
Assembly.LoadWithPartialName(PAXTON_ASSEMBLY)
|
||||
clr.AddReference(PAXTON_ASSEMBLY)
|
||||
from Paxton.Net2.OemClientLibrary import OemClient as OC
|
||||
from Paxton.Net2.OemClientLibrary import AccessLevelDetailSet
|
||||
from Paxton.Net2.OemClientLibrary import TimezonesSet
|
||||
from Paxton.Net2.OemClientLibrary import EventViewEnums
|
||||
except:
|
||||
raise Net2XSException('Failed to load the library')
|
||||
|
||||
except Exception as e:
|
||||
Log4Net.get_logger('Net2XS').Fatal('Paxton error: %s' % str(e))
|
||||
sys.exit(1)
|
||||
# end of paxton loading
|
||||
|
||||
|
||||
class Net2XS(Net2Base):
|
||||
"""Net2 Access class
|
||||
"""
|
||||
|
||||
# Class variables
|
||||
_logger = Log4Net.get_logger('Net2XS')
|
||||
_lock = RLock()
|
||||
|
||||
def __init__(self, host='localhost', port=8025):
|
||||
"""Class constructor
|
||||
"""
|
||||
self._client = None
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._connected = False
|
||||
self._on_acu_event = None
|
||||
|
||||
def __enter__(self):
|
||||
"""With enter handler
|
||||
"""
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
"""With exit handler
|
||||
"""
|
||||
self.dispose()
|
||||
|
||||
def _check_client(self):
|
||||
"""Check is client connection is valid
|
||||
"""
|
||||
if not self._client or not self._connected:
|
||||
raise Net2XSException('Not connected')
|
||||
|
||||
def authenticate(self, user_id, password):
|
||||
"""Authenticate to Net2
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
# Save for re authentication
|
||||
self._user_id = user_id
|
||||
self._password = password
|
||||
|
||||
self.dispose()
|
||||
|
||||
Net2XS._logger.Debug('Connecting to net2 server on %s:%d' %
|
||||
(self._host, self._port))
|
||||
self._client = OC(self._host, self._port)
|
||||
methods = self._client.AuthenticateUser(user_id, password)
|
||||
if not methods:
|
||||
raise Net2XSException('Authentication failed: ' +
|
||||
self._client.LastErrorMessage)
|
||||
else:
|
||||
self._connected = True
|
||||
|
||||
# Add disconnect reconnect handlers
|
||||
self._client.Net2ServerReconnected += (
|
||||
OC.Net2ServerReconnectedHandler(self._reconnected))
|
||||
self._client.Net2ServerDisconnected += (
|
||||
OC.Net2ServerDisconnectedHandler(self._disconnected))
|
||||
|
||||
# Add acu event handler
|
||||
self._client.Net2AccessEvent += (
|
||||
OC.Net2AcuEventHandler(self._acu_event))
|
||||
|
||||
Net2XS._logger.Debug('Authenticated')
|
||||
|
||||
@property
|
||||
def client_version(self):
|
||||
"""Client version
|
||||
"""
|
||||
asm = Assembly.GetAssembly(OC)
|
||||
ver = asm.GetName().Version
|
||||
return (ver.Major, ver.Minor)
|
||||
|
||||
def query_db(self, query):
|
||||
"""Perform a db query
|
||||
|
||||
Returns a dataset
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.QueryDb(query)
|
||||
|
||||
def are_doors_being_synchronised(self):
|
||||
"""Return if synchronization is taking place
|
||||
|
||||
Returns a boolean
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.AreDoorsBeingSynchronised()
|
||||
|
||||
def get_doors(self, device_address=-1):
|
||||
"""Get all doors (ViewDoors)
|
||||
|
||||
Device_address is an optional integer
|
||||
Returns a dataset
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
if device_address < 0:
|
||||
return self._client.ViewDoors().DoorsDataSource
|
||||
else:
|
||||
return self._client.ViewDoors(device_address).DoorsDataSource
|
||||
|
||||
def get_door_name(self, device_address):
|
||||
"""Obtain door name
|
||||
|
||||
Returns a string
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
dataset = self._client.ViewDoors(device_address).DoorsDataSource
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count == 0):
|
||||
return None
|
||||
return dataset.Tables[0].Rows[0].Name
|
||||
|
||||
def get_departments(self):
|
||||
"""Get all departments (ViewDepartments)
|
||||
|
||||
Returns a dataset
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.ViewDepartments().DepartmentsDataSource
|
||||
|
||||
def close_door(self, device_address):
|
||||
"""Close a door
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.CloseDoor(device_address)
|
||||
|
||||
def hold_door_open(self, device_address):
|
||||
"""Hold a door open
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.HoldDoorOpen(device_address)
|
||||
|
||||
def control_door(self, device_address, relay, function, door_open_time, led_flash):
|
||||
"""Control a door
|
||||
|
||||
Relay is 0 or 1, for relay 1 or 2.
|
||||
Function 0 Close, 1 Timed open, 2 Hold Open.
|
||||
Door_open_time in ms.
|
||||
Led_flash see Net2 API.
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.ControlDoorEx(
|
||||
device_address, relay, function, door_open_time, led_flash)
|
||||
|
||||
def get_department_name(self, department_id):
|
||||
"""Get department name by id
|
||||
|
||||
Returns a string or None if department was not found
|
||||
"""
|
||||
dataset = self.query_db(
|
||||
'select DepartmentName from sdk.Departments'
|
||||
' where DepartmentID=%d' % department_id)
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count == 0):
|
||||
return None
|
||||
return dataset.Tables[0].Rows[0][0]
|
||||
|
||||
def get_users_ex(self, name=None):
|
||||
"""Get users, from the UsersEx view.
|
||||
|
||||
Name param is optional tuple (first_name, sur_name)
|
||||
Returns a dataset
|
||||
"""
|
||||
query = 'select * from sdk.UsersEx'
|
||||
if name and len(name) == 2:
|
||||
first_name, sur_name = name
|
||||
query = ("%s where FirstName='%s' and Surname='%s'" %
|
||||
(query, first_name, sur_name))
|
||||
return self.query_db(query)
|
||||
|
||||
def get_user_id_by_name(self, name):
|
||||
"""Get user id by name
|
||||
|
||||
Name is a (first_name, sur_name) tuple
|
||||
Returns the id or -1 if not found.
|
||||
"""
|
||||
|
||||
dataset = self.get_users(name)
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count == 0):
|
||||
return -1
|
||||
return dataset.Tables[0].Rows[0].get_Item("UserID")
|
||||
|
||||
def get_users(self, name=None):
|
||||
"""Get users, from the ViewUserRecords call
|
||||
|
||||
Name param is optional tuple (first_name, sur_name)
|
||||
Returns a dataset
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
wheres = ['Active=1']
|
||||
if name and len(name) == 2:
|
||||
first_name, sur_name = name
|
||||
if first_name:
|
||||
wheres.append("FirstName='%s'" % first_name)
|
||||
if sur_name:
|
||||
wheres.append("Surname='%s'" % sur_name)
|
||||
|
||||
return self._client.ViewUserRecords(
|
||||
' and '.join(wheres)).UsersDataSource
|
||||
|
||||
def get_user_record(self, user_id):
|
||||
"""Get user record, from the ViewUserRecords call
|
||||
|
||||
Returns an IUserView user record or None if not found
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
|
||||
# Fetch current user info
|
||||
users = self._client.ViewUserRecords(
|
||||
'UserID=%d' % user_id).UsersList()
|
||||
|
||||
if users.Count != 1 or not users.ContainsKey(user_id):
|
||||
return None
|
||||
|
||||
return users[user_id]
|
||||
|
||||
def get_user_name(self, user_id):
|
||||
"""Get user name
|
||||
|
||||
Returns a string or None when user is not found
|
||||
"""
|
||||
dataset = self.query_db(
|
||||
'select Username from sdk.UsersEx where UserID=%d' % user_id)
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count == 0):
|
||||
return None
|
||||
return dataset.Tables[0].Rows[0][0]
|
||||
|
||||
def add_user(
|
||||
self,
|
||||
access_level_id=1,
|
||||
department_id=0,
|
||||
anti_passback_ind=False,
|
||||
alarm_user_ind=False,
|
||||
first_name=None,
|
||||
middle_name=None,
|
||||
sur_name=None,
|
||||
telephone_no=None,
|
||||
telephone_extension=None,
|
||||
pin_code=None,
|
||||
activation_date=None,
|
||||
active=True,
|
||||
fax_no=None,
|
||||
expiry_date=None,
|
||||
custom_fields=None,
|
||||
user_picture=None):
|
||||
"""Add user record
|
||||
|
||||
DateTime fields can be either python or dotnet objects.
|
||||
If activation date is None, the current date will be used.
|
||||
If expiry date is None, the user entry will not expire.
|
||||
Custom_fields is a string array (15) of which the first element is
|
||||
ignored.
|
||||
Returns True on success.
|
||||
"""
|
||||
|
||||
# If no user name is given at all: create one (required by Net2)
|
||||
if not first_name and not sur_name and not middle_name:
|
||||
first_name = "New"
|
||||
sur_name = "User"
|
||||
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.AddUserRecord(
|
||||
access_level_id,
|
||||
department_id,
|
||||
anti_passback_ind,
|
||||
alarm_user_ind,
|
||||
first_name,
|
||||
middle_name,
|
||||
sur_name,
|
||||
telephone_no,
|
||||
telephone_extension,
|
||||
pin_code,
|
||||
None,
|
||||
flex_date_time_to_net(activation_date) or self.now_date,
|
||||
0,
|
||||
0,
|
||||
active,
|
||||
fax_no,
|
||||
flex_date_time_to_net(expiry_date) or self.no_expiration_date,
|
||||
custom_fields,
|
||||
user_picture)
|
||||
|
||||
def modify_user(
|
||||
self,
|
||||
user_id,
|
||||
access_level_id=None,
|
||||
department_id=None,
|
||||
anti_passback_ind=None,
|
||||
alarm_user_ind=None,
|
||||
first_name=None,
|
||||
middle_name=None,
|
||||
sur_name=None,
|
||||
telephone_no=None,
|
||||
telephone_extension=None,
|
||||
pin_code=None,
|
||||
activation_date=None,
|
||||
active=None,
|
||||
fax_no=None,
|
||||
expiry_date=None,
|
||||
custom_fields=None,
|
||||
user_picture=None,
|
||||
delete_image=False):
|
||||
"""Modify user record
|
||||
|
||||
Fields omitted keep their original value.
|
||||
Custom_fields is a string array (15) of which the first element is
|
||||
ignored.
|
||||
Providing None custom values in the array, leaves the original value
|
||||
unchanged.
|
||||
Returns True on success.
|
||||
"""
|
||||
# Fetch current user info
|
||||
uview = self.get_user_record(user_id)
|
||||
if not uview:
|
||||
return False
|
||||
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.UpdateUserRecord(
|
||||
user_id,
|
||||
uview.AccessLevelId if access_level_id is None else access_level_id,
|
||||
uview.DepartmentId if department_id is None else department_id,
|
||||
uview.AntiPassbackUser if anti_passback_ind is None else anti_passback_ind,
|
||||
uview.AlarmUser if alarm_user_ind is None else alarm_user_ind,
|
||||
first_name,
|
||||
middle_name,
|
||||
sur_name,
|
||||
telephone_no,
|
||||
telephone_extension,
|
||||
pin_code,
|
||||
None,
|
||||
flex_date_time_to_net(activation_date) or uview.ActivationDate,
|
||||
uview.Active if active is None else active,
|
||||
fax_no,
|
||||
flex_date_time_to_net(expiry_date) or uview.ExpiryDate,
|
||||
custom_fields,
|
||||
user_picture,
|
||||
delete_image)
|
||||
|
||||
def delete_user(self, user_id):
|
||||
"""Delete user record
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.PurgeUser(user_id)
|
||||
|
||||
def deactivate_user(self, user_id):
|
||||
"""Deactivate user. Preferred method iso delete.
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
return self.modify_user(
|
||||
user_id=user_id,
|
||||
access_level_id=0,
|
||||
department_id=0,
|
||||
pin_code="",
|
||||
active=False)
|
||||
|
||||
def modify_user_access_level(self, user_id, access_level_id):
|
||||
"""Alter user access level
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
return self.modify_user(
|
||||
user_id=user_id,
|
||||
access_level_id=access_level_id)
|
||||
|
||||
def modify_user_picture(self, user_id, user_picture):
|
||||
"""Alter user picture. If user_picture is None, remove the picture.
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
return self.modify_user(
|
||||
user_id=user_id,
|
||||
user_picture=user_picture,
|
||||
delete_image=True if not user_picture else False)
|
||||
|
||||
def get_area_ids(self, device_address):
|
||||
"""Get area ids of a device
|
||||
|
||||
Returns a tuple with area id's (in, out)
|
||||
"""
|
||||
dataset = self.query_db(
|
||||
'select b.ToAreaID from sdk.PeripheralNames a'
|
||||
' inner join sdk.AreaGateways b on'
|
||||
' a.PeripheralID=b.PeripheralID and'
|
||||
' a.SerialNumber=%d order by a.SubAddress' %
|
||||
(device_address))
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count != 2):
|
||||
return None
|
||||
return (dataset.Tables[0].Rows[0][0], dataset.Tables[0].Rows[1][0])
|
||||
|
||||
def get_device_addr_info(self):
|
||||
"""Obtain all relevant device address info
|
||||
|
||||
Returns a dataset
|
||||
"""
|
||||
return self.query_db(
|
||||
'select a.SerialNumber, a.SubAddress, a.PeripheralID, b.ToAreaID'
|
||||
' from sdk.PeripheralNames a'
|
||||
' inner join AreaGateways b on a.PeripheralID=b.PeripheralID'
|
||||
' order by a.SerialNumber')
|
||||
|
||||
def get_time_slots(self, access_level_id, area_id):
|
||||
"""Obtain allocated timeslots
|
||||
|
||||
Returns a dataset
|
||||
"""
|
||||
return self.query_db(
|
||||
'select * from sdk.Timeslots'
|
||||
' where TimezoneID='
|
||||
'(select TimezoneID from AccessLevelMembers'
|
||||
' where AccessLevelID=%d and AreaID=%d)' %
|
||||
(access_level_id, area_id or -1))
|
||||
|
||||
def get_time_zones(self, time_zone_id=-1):
|
||||
"""Obtain timezones, including slot data
|
||||
|
||||
Time_zone_id is optional. If not provided, all timezones are returned.
|
||||
Returns a dataset
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
if time_zone_id < 0:
|
||||
return self._client.ViewTimezones().TimezonesDataSource
|
||||
else:
|
||||
return self._client.ViewTimezones(
|
||||
time_zone_id, False).TimezonesDataSource
|
||||
|
||||
def get_py_time_zones(self, time_zone_id=-1):
|
||||
"""Like get_time_zones, but returns a python array of TimeZone objects
|
||||
|
||||
Returns an array of TimeZone objects
|
||||
"""
|
||||
dataset = self.get_time_zones(time_zone_id)
|
||||
return time_zones_to_py(dataset)
|
||||
|
||||
def add_time_zone(self, time_zone):
|
||||
"""Add new time zone
|
||||
|
||||
Time_zone is a python TimeZone object type.
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
tz_set = TimezonesSet()
|
||||
for slot in time_zone.slots:
|
||||
tz_set.Tables[1].AddTimeslotsRow(
|
||||
slot.id,
|
||||
time_zone.id,
|
||||
date_time_to_net(slot.start_time),
|
||||
date_time_to_net(slot.end_time),
|
||||
slot.day)
|
||||
|
||||
# To array
|
||||
ts_array = Array[TimezonesSet.TimeslotsRow](
|
||||
len(time_zone.slots) * [None])
|
||||
for i, row in enumerate(tz_set.Tables[1].Rows):
|
||||
ts_array[i] = row
|
||||
|
||||
# Dispose timezones set
|
||||
tz_set.Dispose()
|
||||
|
||||
return self._client.AddTimezone(time_zone.name, ts_array)
|
||||
|
||||
def delete_time_zone(self, time_zone_id):
|
||||
"""Remove time zone with given id
|
||||
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.DeleteTimezone(time_zone_id)
|
||||
|
||||
def update_time_zone(self, time_zone):
|
||||
"""Update existing time zone
|
||||
|
||||
Time_zone is a python TimeZone object type.
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
tz_set = TimezonesSet()
|
||||
nr_slots = len(time_zone.slots)
|
||||
|
||||
if nr_slots:
|
||||
for slot in time_zone.slots:
|
||||
tz_set.Tables[1].AddTimeslotsRow(
|
||||
slot.id,
|
||||
time_zone.id,
|
||||
date_time_to_net(slot.start_time),
|
||||
date_time_to_net(slot.end_time),
|
||||
slot.day)
|
||||
|
||||
# Work around for bug "Update Timezone with empty timeslot" (forum)
|
||||
# At least 1 row is required for the update to work correctly.
|
||||
# Should be fixed in V4.21
|
||||
# TODO: only perform for lower versions?
|
||||
else:
|
||||
nr_slots = 1
|
||||
tz_set.Tables[1].AddTimeslotsRow(
|
||||
-1,
|
||||
time_zone.id,
|
||||
date_time_to_net(datetime(1899, 12, 30, 23, 59, 59)),
|
||||
date_time_to_net(datetime(1899, 12, 30, 23, 59, 59)),
|
||||
0)
|
||||
|
||||
# To array (bit awkward in Python.Net)
|
||||
ts_array = Array[TimezonesSet.TimeslotsRow](nr_slots * [None])
|
||||
for i, row in enumerate(tz_set.Tables[1].Rows):
|
||||
ts_array[i] = row
|
||||
|
||||
# Dispose timezones set
|
||||
tz_set.Dispose()
|
||||
|
||||
return self._client.UpdateTimezone(time_zone.id, ts_array)
|
||||
|
||||
def get_time_zone_id_by_name(self, name):
|
||||
"""Get time zone id of time zone with given name
|
||||
|
||||
Returns a string or None when the timezone could not be found.
|
||||
"""
|
||||
dataset = self.query_db(
|
||||
"select TimezoneID from sdk.Timezones"
|
||||
" where Name='%s'" % name)
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count == 0):
|
||||
return None
|
||||
return dataset.Tables[0].Rows[0][0]
|
||||
|
||||
def get_access_levels(self, prefix=None):
|
||||
"""Obtain access levels, optinally profiding a name prefix filter
|
||||
|
||||
Prefix is an optional parameter to obtain only access levels with the
|
||||
given prefix.
|
||||
Details need to be fetched separately.
|
||||
Returns a dataset.
|
||||
"""
|
||||
query = 'select * from sdk.AccessLevels'
|
||||
if prefix:
|
||||
query = "%s where AccessLevelName like '%s%%'" % (query, prefix)
|
||||
return self.query_db(query)
|
||||
|
||||
def get_access_level_details(self, access_level_id):
|
||||
"""Obtain access level details
|
||||
|
||||
Returns a dataset.
|
||||
"""
|
||||
query = 'select * from sdk.AccessLevelMembers where AccessLevelID=%d' % (access_level_id)
|
||||
return self.query_db(query)
|
||||
|
||||
def get_py_access_levels(self, prefix=None):
|
||||
"""Like get_access_level, but returning a python array of AccessLevel
|
||||
objects including details!
|
||||
|
||||
Returns an array of AccessLevel objects.
|
||||
"""
|
||||
dataset = self.get_access_levels(prefix)
|
||||
if not dataset:
|
||||
return []
|
||||
access_levels = access_levels_to_py(dataset)
|
||||
for al in access_levels:
|
||||
dataset = self.get_access_level_details(al.id)
|
||||
if dataset:
|
||||
access_level_detail_to_py(al, dataset)
|
||||
|
||||
return access_levels
|
||||
|
||||
def add_access_level(self, access_level):
|
||||
"""Add new access level
|
||||
|
||||
Access_level is a python AccessLevel object type.
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
detail_set = AccessLevelDetailSet()
|
||||
if access_level.details:
|
||||
for detail in access_level.details:
|
||||
detail_set.Tables[0].AddAccessLevelDetailRow(
|
||||
None,
|
||||
detail[0], # time zone id
|
||||
None,
|
||||
detail[1], # area id
|
||||
None,
|
||||
-1, # address
|
||||
-1, # sub address
|
||||
-1) # access level id
|
||||
|
||||
return self._client.AddAccessLevel(access_level.name, detail_set)
|
||||
|
||||
def delete_access_level(self, access_level_id):
|
||||
"""Delete access level
|
||||
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.DeleteAccessLevel(access_level_id)
|
||||
|
||||
def update_access_level(self, access_level):
|
||||
"""Update access level
|
||||
|
||||
Access_level is a python AccessLevel object type.
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
|
||||
if access_level.details:
|
||||
detail_set = AccessLevelDetailSet()
|
||||
for detail in access_level.details:
|
||||
detail_set.Tables[0].AddAccessLevelDetailRow(
|
||||
None,
|
||||
detail[0], # time zone id
|
||||
None,
|
||||
detail[1], # area id
|
||||
None,
|
||||
-1, # address
|
||||
-1, # sub address
|
||||
-1) # access level id
|
||||
|
||||
# Work around for similar bug as with the time zone slots
|
||||
# When details are empty, existing data will not be cleared
|
||||
# Therefore set TimezoneID to 0 for any existing entries
|
||||
else:
|
||||
ald = self._client.ViewAccessLevelDetail(access_level.id)
|
||||
detail_set = ald.AccessLevelDetailsDataSource
|
||||
for row in detail_set.Tables[0].Rows:
|
||||
row.TimezoneID = 0
|
||||
|
||||
return self._client.UpdateAccessLevel(
|
||||
access_level.id, access_level.name, detail_set)
|
||||
|
||||
def get_access_level_id_by_user(self, user_id):
|
||||
"""Get access level id of given user
|
||||
|
||||
Returns an integer or None if the user could not be found
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
dataset = self._client.ViewUserRecords(
|
||||
'UserID=%d' % (user_id)).UsersDataSource
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count == 0):
|
||||
return None
|
||||
return dataset.Tables[0].Rows[0].AccessLevelID
|
||||
|
||||
def get_access_level_id_by_name(self, name):
|
||||
"""Get access level id of level with given name
|
||||
|
||||
Returns a string or None if the accesslevel could not be found.
|
||||
"""
|
||||
dataset = self.query_db(
|
||||
"select AccessLevelID from sdk.AccessLevels"
|
||||
" where AccessLevelName='%s'" % name)
|
||||
if (not dataset or
|
||||
dataset.Tables.Count < 1 or
|
||||
dataset.Tables[0].Rows.Count == 0):
|
||||
return None
|
||||
return dataset.Tables[0].Rows[0][0]
|
||||
|
||||
def get_operator_level(self, user_id):
|
||||
"""Get operator level of the given user id
|
||||
|
||||
Returns an integer.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.GetOperatorLevel(user_id)
|
||||
|
||||
def add_card(self, card_nr, type_id, user_id):
|
||||
"""Add a new card to the given user
|
||||
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.AddCard(card_nr, type_id, user_id)
|
||||
|
||||
def delete_card(self, card_nr):
|
||||
"""Delete a card with the given card_nr
|
||||
|
||||
Returns True on success (even if card_nr did not exist).
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.DeleteCard(card_nr)
|
||||
|
||||
def get_cards(self, user_id=None):
|
||||
"""Obtain all 'cards' of the given user id
|
||||
|
||||
If no user_id is given, all cards are returned.
|
||||
Returns a dataset.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
# Setup dataset to return
|
||||
dataset = DataSet()
|
||||
table = dataset.Tables.Add()
|
||||
table.Columns.Add("UserID")
|
||||
table.Columns.Add("CardNumber")
|
||||
table.Columns.Add("LostCard")
|
||||
table.Columns.Add("CardTypeID")
|
||||
|
||||
# Fetch normal cards
|
||||
query = ("select * from sdk.Cards" +
|
||||
" where CardTypeID is not null" +
|
||||
" and CardTypeID <> 7" +
|
||||
" and CardNumber <> 100000000 + UserID")
|
||||
if type(user_id) is int:
|
||||
query += " and UserID=%d" % (user_id)
|
||||
card_dataset = self.query_db(query)
|
||||
if card_dataset and card_dataset.Tables.Count > 0:
|
||||
for row in card_dataset.Tables[0].Rows:
|
||||
table.Rows.Add(row['UserID'],
|
||||
row['CardNumber'],
|
||||
row['LostCard'],
|
||||
row['CardTypeID'])
|
||||
|
||||
# Fetch vehicle registrations
|
||||
query = "select * from sdk.UserVehicleIndex"
|
||||
if type(user_id) is int:
|
||||
query += " where UserID=%d" % (user_id)
|
||||
vehicle_dataset = self.query_db(query)
|
||||
if vehicle_dataset and vehicle_dataset.Tables.Count > 0:
|
||||
for row in vehicle_dataset.Tables[0].Rows:
|
||||
table.Rows.Add(row['UserID'],
|
||||
row['VehicleIndex'],
|
||||
False,
|
||||
7)
|
||||
return dataset
|
||||
|
||||
def add_event_record(
|
||||
self,
|
||||
#CK: check!
|
||||
#event_type, event_subtype=EventViewEnums.Net2EventSubTypes.None,
|
||||
event_type, event_subtype=None,
|
||||
address=0, subaddress=0, user_id=None, card_nr=0,
|
||||
event_detail=None,
|
||||
linked_event_id=0, ioboard_id=0, input_id=0, output_id=0):
|
||||
"""Add an event record
|
||||
|
||||
Returns True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.AddEventRecord(
|
||||
event_type,
|
||||
event_subtype,
|
||||
address, subaddress, user_id, card_nr, event_detail,
|
||||
linked_event_id, ioboard_id, input_id, output_id)
|
||||
|
||||
def add_acu(self, address, acu_type):
|
||||
"""Add new acu device
|
||||
|
||||
Acu_types: Classic = 1, Nano = 2, Plus = 3, EasyProxNano = 4, PaxLoc = 7, PaxLocMifare = 8
|
||||
Returns True on success. (up till 4.27, there is a bug in the SDK causing a False on success)
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.AddACU(address, acu_type)
|
||||
|
||||
def delete_acu(self, address):
|
||||
"""Delete acu device
|
||||
|
||||
Return True on success.
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.DeleteACU(address)
|
||||
|
||||
@property
|
||||
def last_error_message(self):
|
||||
"""Last error message
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.LastErrorMessage
|
||||
|
||||
@property
|
||||
def no_expiration_date(self):
|
||||
"""The 'special' no expiration date of Net2 (1753-01-01)
|
||||
|
||||
Returns dotnet DateTime object
|
||||
"""
|
||||
return date_time_to_net(datetime(year=1753, month=1, day=1))
|
||||
|
||||
@property
|
||||
def now_date(self):
|
||||
"""The now datetime
|
||||
|
||||
Returns dotnet DateTime object
|
||||
"""
|
||||
return date_time_to_net(datetime.now())
|
||||
|
||||
def dispose(self):
|
||||
"""Dispose Net2 client
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
if self._client:
|
||||
try:
|
||||
self._client.Dispose()
|
||||
self._client = None
|
||||
Net2XS._logger.Debug('Disposed')
|
||||
except:
|
||||
pass
|
||||
|
||||
def _reconnected(self, sender, event_args):
|
||||
"""Reconnect handler
|
||||
"""
|
||||
if event_args.ReAuthenticationRequired:
|
||||
try:
|
||||
self.authenticate(self._user_id, self._password)
|
||||
self._connected = True
|
||||
Net2XS._logger.Debug('Reconnected')
|
||||
except Exception as e:
|
||||
Net2XS._logger.Debug('Reconnect error: %s' % str(e))
|
||||
else:
|
||||
self._connected = True
|
||||
|
||||
def _disconnected(self, sender, event_args):
|
||||
"""Disconnect handler
|
||||
"""
|
||||
Net2XS._logger.Debug('Disconnected')
|
||||
self._connected = False
|
||||
|
||||
@property
|
||||
def on_acu_event(self):
|
||||
return self._on_acu_event
|
||||
|
||||
@on_acu_event.setter
|
||||
def on_acu_event(self, value):
|
||||
self._on_acu_event = value
|
||||
|
||||
def _acu_event(self, sender, event_view):
|
||||
"""Acu event handler
|
||||
"""
|
||||
if self.on_acu_event:
|
||||
try:
|
||||
self.on_acu_event(sender, event_view)
|
||||
except Exception as e:
|
||||
Net2XS._logger.Debug('Acu even handler error: %s' % str(e))
|
||||
|
||||
def monitor_acu(self, address):
|
||||
"""Monitor events of given acu
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.MonitorAcu(address)
|
||||
|
||||
def stop_monitoring_acu(self, address):
|
||||
"""Stop monitoring events of given acu
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.StopMonitoringAcu(address)
|
||||
|
||||
def activate_ioboard_relays(self, ioboard_id, settings):
|
||||
"""Control ioboard relays
|
||||
|
||||
Returns True on success
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return self._client.ActivateIoBoardRelays(
|
||||
ioboard_id, settings)
|
||||
|
||||
@classmethod
|
||||
def door_status_str(cls, status):
|
||||
"""Translate door status to human readable string
|
||||
|
||||
Returns a csv string with the combined status bits.
|
||||
"""
|
||||
ret = []
|
||||
if status & OC.DoorStatusFlags.PSUIsOK:
|
||||
ret.append('psu ok')
|
||||
if status & OC.DoorStatusFlags.IntruderAlarm:
|
||||
ret.append('intruder')
|
||||
if not status & OC.DoorStatusFlags.TamperStatusGood:
|
||||
ret.append('tampered')
|
||||
if status & OC.DoorStatusFlags.DoorContactClosed:
|
||||
ret.append('door contact closed')
|
||||
if status & OC.DoorStatusFlags.AlarmTripped:
|
||||
ret.append('alarm')
|
||||
if status & OC.DoorStatusFlags.DoorOpen:
|
||||
ret.append('door open')
|
||||
# When psu not ok, other state bits are irrelevant
|
||||
else:
|
||||
ret.append('psu not ok')
|
||||
return ', '.join(ret)
|
@ -0,0 +1,151 @@
|
||||
"""
|
||||
Module holding conversion definitions
|
||||
"""
|
||||
from datetime import datetime
|
||||
from System import DateTime
|
||||
from . deftypes import TimeZone, TimeSlot, AccessLevel
|
||||
|
||||
|
||||
def date_time_to_py(val):
|
||||
"""Convert dotnet date time format to python presentation
|
||||
|
||||
Returns python datetime object.
|
||||
"""
|
||||
return datetime(
|
||||
year=val.Year, month=val.Month, day=val.Day,
|
||||
hour=val.Hour, minute=val.Minute, second=val.Second)
|
||||
|
||||
|
||||
def date_time_to_net(val):
|
||||
"""Convert python to dotnet date time presentation
|
||||
|
||||
Returns dotnet DateTime object.
|
||||
"""
|
||||
return DateTime(
|
||||
val.year, val.month, val.day,
|
||||
val.hour, val.minute, val.second)
|
||||
|
||||
|
||||
def flex_date_time_to_net(val):
|
||||
"""Convert python to dotnet date time presentation when required
|
||||
|
||||
Returns dotnet DateTime object or None.
|
||||
"""
|
||||
if val is None:
|
||||
return val
|
||||
|
||||
if type(val) is datetime:
|
||||
return date_time_to_net(val)
|
||||
|
||||
if not type(val) is DateTime:
|
||||
raise TypeError(
|
||||
("The time value supplied (%s), " +
|
||||
"can not be interpreted as a dotnet DateTime object") %
|
||||
(str(val)))
|
||||
return val
|
||||
|
||||
|
||||
def time_zones_to_py(dataset):
|
||||
"""Convert Net2 time zones dataset to a python presentation
|
||||
|
||||
Returns array of TimeZone objects.
|
||||
"""
|
||||
if not dataset or dataset.Tables.Count != 2:
|
||||
raise Exception('Timezone dataset is invalid')
|
||||
|
||||
result = []
|
||||
|
||||
for tz_row in dataset.Tables[0].Rows:
|
||||
time_zone = TimeZone(
|
||||
id=tz_row.TimezoneID,
|
||||
name=tz_row.Name)
|
||||
|
||||
ts_rows = dataset.Tables[1].Select('TimezoneID=%d' % time_zone.id)
|
||||
for ts_row in ts_rows:
|
||||
time_slot = TimeSlot(
|
||||
id=ts_row.SlotID,
|
||||
day=ts_row.Day,
|
||||
start_time=date_time_to_py(ts_row.StartTime),
|
||||
end_time=date_time_to_py(ts_row.EndTime))
|
||||
time_zone.slots.append(time_slot)
|
||||
|
||||
result.append(time_zone)
|
||||
return result
|
||||
|
||||
|
||||
def access_levels_to_py(dataset):
|
||||
"""Convert Net2 access_level dataset to a python presentation
|
||||
|
||||
Returns array of AccessLevel objects.
|
||||
"""
|
||||
if not dataset or dataset.Tables.Count != 1:
|
||||
raise Exception('AccessLevel dataset is invalid')
|
||||
|
||||
result = []
|
||||
|
||||
for al_row in dataset.Tables[0].Rows:
|
||||
access_level = AccessLevel(
|
||||
id=al_row.get_Item("AccessLevelID"),
|
||||
name=al_row.get_Item("AccessLevelName"))
|
||||
result.append(access_level)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def access_level_detail_to_py(access_level, dataset):
|
||||
"""Convert Net2 access_level detail dataset
|
||||
time_zones is a net2-id key based lookup dict.
|
||||
"""
|
||||
if not dataset or dataset.Tables.Count != 1:
|
||||
raise Exception('AccessLevelDetail dataset is invalid')
|
||||
|
||||
# Clear existing
|
||||
access_level.details = []
|
||||
# Add details
|
||||
for ald_row in dataset.Tables[0].Rows:
|
||||
time_zone_id = ald_row.get_Item("TimezoneID")
|
||||
area_id = ald_row.get_Item("AreaID")
|
||||
access_level.details.append((time_zone_id, area_id))
|
||||
|
||||
|
||||
def user_view_to_py(user_view):
|
||||
"""Convert a Net2 UserView object (partially) to a dict
|
||||
"""
|
||||
res = {}
|
||||
res["user_id"] = user_view.UserId
|
||||
res["access_level_id"] = user_view.AccessLevelId
|
||||
res["department_id"] = user_view.DepartmentId
|
||||
res["anti_passback_user"] = user_view.AntiPassbackUser
|
||||
res["alarm_user"] = user_view.AlarmUser
|
||||
res["first_name"] = user_view.FirstName
|
||||
res["middle_name"] = user_view.MiddleName
|
||||
res["sur_name"] = user_view.Surname
|
||||
res["telephone"] = user_view.Telephone
|
||||
res["extension"] = user_view.Extension
|
||||
res["pin"] = user_view.PIN
|
||||
res["activation_date"] = date_time_to_py(user_view.ActivationDate)
|
||||
res["active"] = user_view.Active
|
||||
res["fax"] = user_view.Fax
|
||||
res["expiry_date"] = date_time_to_py(user_view.ExpiryDate)
|
||||
res["custom_fields"] = [
|
||||
None,
|
||||
user_view.Field1_100,
|
||||
user_view.Field2_100,
|
||||
user_view.Field3_50,
|
||||
user_view.Field4_50,
|
||||
user_view.Field5_50,
|
||||
user_view.Field6_50,
|
||||
user_view.Field7_50,
|
||||
user_view.Field8_50,
|
||||
user_view.Field9_50,
|
||||
user_view.Field10_50,
|
||||
user_view.Field11_50,
|
||||
user_view.Field12_50,
|
||||
user_view.Field13_Memo,
|
||||
user_view.Field14_50]
|
||||
res["user_guid"] = user_view.UserGuid.ToString()
|
||||
res["last_area_id"] = user_view.LastAreaId
|
||||
res["last_access_time"] = date_time_to_py(user_view.LastAccessTime)
|
||||
res["last_updated"] = date_time_to_py(user_view.LastUpdated)
|
||||
|
||||
return res
|
@ -0,0 +1,120 @@
|
||||
"""
|
||||
Module holding definition data types
|
||||
"""
|
||||
|
||||
|
||||
class TimeSlot(object):
|
||||
"""Time zone slot definition
|
||||
|
||||
Member id: Timeslot id
|
||||
Member day: Timeslot day number
|
||||
Member start_time: Start time of slot
|
||||
Member start_time: End time of slot
|
||||
"""
|
||||
def __init__(self, id=-1, day=-1, start_time=None, end_time=None):
|
||||
self.id = id
|
||||
self.start_time = start_time
|
||||
self.end_time = end_time
|
||||
self.day = day
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compare
|
||||
"""
|
||||
return (
|
||||
self.start_time == other.start_time and
|
||||
self.end_time == other.end_time and
|
||||
self.day == other.day)
|
||||
|
||||
def __ne__(self, other):
|
||||
"""Compare negative
|
||||
"""
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __str__(self):
|
||||
return ('%s id=%d, day=%d, start_time=%s, end_time=%s' %
|
||||
(self.__class__.__name__,
|
||||
self.id, self.day, self.start_time, self.end_time))
|
||||
|
||||
|
||||
class TimeZone(object):
|
||||
"""Time zone definition
|
||||
|
||||
Member id: Timezone id
|
||||
Member name: Timezone name
|
||||
Member slots: Array of TimeSlot objects
|
||||
"""
|
||||
def __init__(self, id=-1, name=None):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.slots = []
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compare
|
||||
"""
|
||||
if (type(self) != type(other) or
|
||||
len(self.slots) != len(other.slots)):
|
||||
return False
|
||||
|
||||
for s_slot in self.slots:
|
||||
for o_slot in other.slots:
|
||||
if s_slot == o_slot:
|
||||
break
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
"""Compare negative
|
||||
"""
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __str__(self):
|
||||
out = []
|
||||
out.append('%s id=%d, name=%s' %
|
||||
(self.__class__.__name__, self.id, self.name))
|
||||
for slot in self.slots:
|
||||
out.append('-%s' % slot)
|
||||
return '\n'.join(out)
|
||||
|
||||
|
||||
class AccessLevel(object):
|
||||
"""Access level definition
|
||||
|
||||
Member id: Accesslevel id
|
||||
Member name: Accesslevel name
|
||||
Member details: Array of (timezone id, area id) tuples
|
||||
"""
|
||||
def __init__(self, id, name):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.details = []
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compare
|
||||
"""
|
||||
if (type(self) != type(other) or
|
||||
len(self.details) != len(other.details)):
|
||||
return False
|
||||
|
||||
for s_detail in self.details:
|
||||
for o_detail in other.details:
|
||||
if s_detail == o_detail:
|
||||
break
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
"""Compare negative
|
||||
"""
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __str__(self):
|
||||
out = []
|
||||
out.append('%s id=%d, name=%s' % (
|
||||
self.__class__.__name__, self.id, self.name))
|
||||
for tz_id, area_id in self.details:
|
||||
out.append('-tz=%d, area=%d' % (tz_id, area_id))
|
||||
return '\n'.join(out)
|
@ -0,0 +1,8 @@
|
||||
"""
|
||||
The module that offers several Net2 related network utilities
|
||||
"""
|
||||
|
||||
# This is just to force py2exe to include these packages
|
||||
if False == True:
|
||||
import network.net2plus
|
||||
import network.sqlserver
|
@ -0,0 +1,66 @@
|
||||
"""
|
||||
Net2Plus network utilities
|
||||
"""
|
||||
|
||||
import socket
|
||||
|
||||
|
||||
class Net2PlusFinder(object):
|
||||
"""Class to help find Net2Plus modules
|
||||
"""
|
||||
|
||||
def __init__(self, port=30718, timeout=4, ip="0.0.0.0"):
|
||||
self._port = port
|
||||
self._timeout = timeout
|
||||
self._ip = ip
|
||||
|
||||
def _broadcast(self):
|
||||
"""Broadcast on udp
|
||||
"""
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.bind((self._ip, self._port))
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
# Send twice
|
||||
s.sendto(bytearray([0x00, 0x00, 0x00, 0xf6]), ("255.255.255.255", self._port))
|
||||
s.sendto(bytearray([0x00, 0x00, 0x00, 0xf6]), ("255.255.255.255", self._port))
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
def find(self):
|
||||
"""Find Net2Plus modules responsing to a Broadcast
|
||||
|
||||
Returns an array of dicts with info about each module
|
||||
"""
|
||||
res = []
|
||||
|
||||
# Broadcast
|
||||
self._broadcast()
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.settimeout(self._timeout)
|
||||
s.bind((self._ip, self._port))
|
||||
|
||||
# Wait for result
|
||||
try:
|
||||
while True:
|
||||
data, addr = s.recvfrom(1024)
|
||||
if len(data) >= 30:
|
||||
mac_nrs = tuple(ord(i) for i in data[24:30])
|
||||
mac = "%02x:%02x:%02x:%02x:%02x:%02x" % mac_nrs
|
||||
mod_nr = (mac_nrs[3] << 16) + (mac_nrs[4] << 8) + mac_nrs[5]
|
||||
version = "%d.%02d (%d.%02d)" % (ord(data[19]), ord(data[18]), ord(data[21]), ord(data[20]))
|
||||
dev_dict = dict(
|
||||
ip= addr[0],
|
||||
mac=mac,
|
||||
module=mod_nr,
|
||||
version=version)
|
||||
res.append(dev_dict)
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
return res
|
||||
finally:
|
||||
s.close()
|
@ -0,0 +1,54 @@
|
||||
"""
|
||||
SQLServer network utilities
|
||||
"""
|
||||
|
||||
import socket
|
||||
|
||||
|
||||
class SqlServerFinder(object):
|
||||
"""Class to help find SQLServer instances
|
||||
"""
|
||||
|
||||
def __init__(self, port=1434, timeout=4, ip="0.0.0.0"):
|
||||
self._port = port
|
||||
self._timeout = timeout
|
||||
self._ip = ip
|
||||
|
||||
def find(self):
|
||||
"""Find SQLServer instances responsing to a Broadcast
|
||||
|
||||
Returns an array of dicts with info about each instance
|
||||
"""
|
||||
res = []
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.settimeout(self._timeout)
|
||||
s.bind((self._ip, 0))
|
||||
self._local_port = s.getsockname()[1]
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
s.sendto(bytearray([0x02]), ("255.255.255.255", self._port))
|
||||
|
||||
# Wait for result
|
||||
try:
|
||||
while True:
|
||||
data, addr = s.recvfrom(1024)
|
||||
if len(data) > 3:
|
||||
data = data.decode()
|
||||
entry = {}
|
||||
parts = data[3:].split(";")
|
||||
nr_values = int(len(parts) / 2)
|
||||
for i in range(nr_values):
|
||||
key = parts[i * 2].strip()
|
||||
value = parts[1 + i * 2].strip()
|
||||
if (key):
|
||||
entry[key] = value
|
||||
res.append(entry)
|
||||
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
return res
|
||||
finally:
|
||||
s.close()
|
@ -0,0 +1,64 @@
|
||||
"""
|
||||
Module to use log4net from python
|
||||
"""
|
||||
import os
|
||||
import clr
|
||||
import sys
|
||||
|
||||
import settings
|
||||
|
||||
# Add path lib to search path
|
||||
LOG4NET_LIB_DIR = os.path.join(settings.LIB_DIR, 'log4netdll')
|
||||
if LOG4NET_LIB_DIR not in sys.path:
|
||||
sys.path.append(LOG4NET_LIB_DIR)
|
||||
|
||||
clr.AddReference('log4net')
|
||||
import log4net
|
||||
from log4net.Config import XmlConfigurator
|
||||
clr.AddReference('System.Xml')
|
||||
from System.Xml import XmlDocument
|
||||
|
||||
|
||||
class Log4NetError(Exception):
|
||||
"""Log4Net exception class
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Log4Net(object):
|
||||
"""Class that offers log4net usage, taking its config from an xml file
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def read_config(cls, config_file):
|
||||
"""Read and process config file
|
||||
"""
|
||||
if not os.path.isfile(config_file):
|
||||
raise Log4NetError('Failed to find config file "%s"' % config_file)
|
||||
|
||||
# Read document
|
||||
doc = XmlDocument()
|
||||
try:
|
||||
doc.Load(config_file)
|
||||
except Exception as e:
|
||||
raise Log4NetError(str(e))
|
||||
|
||||
# Obtain 1st element with log4net tag
|
||||
for element in doc.GetElementsByTagName('log4net'):
|
||||
XmlConfigurator.Configure(element)
|
||||
break
|
||||
|
||||
@classmethod
|
||||
def get_logger(cls, name):
|
||||
"""Obtain logger instance
|
||||
"""
|
||||
return log4net.LogManager.GetLogger(name)
|
||||
|
||||
@classmethod
|
||||
def get_file_appender_logfile(cls):
|
||||
"""Obtain file appender log file
|
||||
"""
|
||||
for appender in log4net.LogManager.GetRepository().GetAppenders():
|
||||
if isinstance(appender, log4net.Appender.FileAppender):
|
||||
return str(appender.File)
|
||||
return None
|
@ -0,0 +1,51 @@
|
||||
"""
|
||||
Sample script to add a new user
|
||||
"""
|
||||
|
||||
from net2xs import Net2XS
|
||||
|
||||
# Uncomment to use dotnet DateTime objects
|
||||
# from System import DateTime
|
||||
|
||||
# Uncomment to use python datetime objects
|
||||
# from datetime import datetime
|
||||
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
# Add new user
|
||||
res = net2.add_user(
|
||||
access_level_id=1, # Always, all doors
|
||||
department_id=0, # No department
|
||||
anti_passback_ind=False,
|
||||
alarm_user_ind=False,
|
||||
first_name="John",
|
||||
middle_name=None,
|
||||
sur_name="Doe",
|
||||
telephone_no="12345678",
|
||||
telephone_extension=None,
|
||||
pin_code=None,
|
||||
activation_date=None, # Now
|
||||
# Or supply a dotnet DateTime object (also see import)
|
||||
# activation_date=DateTime(2018, 1, 2),
|
||||
# Or supply a python datetime object (also see import)
|
||||
# activation_date=datetime(2018, 1, 2),
|
||||
active=True,
|
||||
fax_no=None,
|
||||
expiry_date=None) # Never expire (also see activation_date)
|
||||
|
||||
if res:
|
||||
print("Success")
|
||||
else:
|
||||
print("Failure")
|
@ -0,0 +1,46 @@
|
||||
"""
|
||||
Sample script to add many users with cards
|
||||
"""
|
||||
from net2xs import Net2XS
|
||||
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
|
||||
# Add 1000 users
|
||||
for i in range(1, 1001):
|
||||
first_name = "Test"
|
||||
sur_name = "User #%04d" % (i)
|
||||
card_nr = 77770000 + i
|
||||
|
||||
# Add user
|
||||
res = net2.add_user(
|
||||
first_name=first_name,
|
||||
sur_name=sur_name)
|
||||
if not res:
|
||||
print("Failed to add user %s %s: %s" %
|
||||
(first_name, sur_name, net2.last_error_message))
|
||||
continue
|
||||
|
||||
# Get user id
|
||||
user_id = net2.get_user_id_by_name((first_name, sur_name))
|
||||
if user_id < 0:
|
||||
print("Failed to find user %s %s" % (first_name, sur_name))
|
||||
continue
|
||||
|
||||
# Create card
|
||||
res = net2.add_card(card_nr, 1, user_id)
|
||||
if not res:
|
||||
print("Failed to add card to user %s %s: %s" %
|
||||
(first_name, sur_name, net2.last_error_message))
|
@ -0,0 +1,35 @@
|
||||
"""
|
||||
Sample script to query the database directly
|
||||
"""
|
||||
|
||||
from net2dbxs import Net2DBXS
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2DBXS() as net2db:
|
||||
# Connect
|
||||
net2db.connect()
|
||||
# Get last log entry relating a device
|
||||
dataset = net2db.query_db(
|
||||
"select * from EventsEx "
|
||||
"where EventID = "
|
||||
"(select max(EventID) from sdk.EventsEx "
|
||||
" where SerialNumber is not NULL)")
|
||||
|
||||
# Non result
|
||||
if (not dataset
|
||||
or dataset.Tables.Count < 1
|
||||
or dataset.Tables[0].Rows.Count < 1):
|
||||
|
||||
print("Nothing relevant found")
|
||||
else:
|
||||
# Just to demonstrate how to get values from a dataset
|
||||
|
||||
# Typically Table[0]
|
||||
table = dataset.Tables[0]
|
||||
# In this case only interested in the first row
|
||||
row = dataset.Tables[0].Rows[0]
|
||||
# For each column
|
||||
for col in table.Columns:
|
||||
val = row.get_Item(col.ColumnName)
|
||||
print("%s = %s" % (col.ColumnName, val))
|
@ -0,0 +1,17 @@
|
||||
"""
|
||||
Sample script to detect Net2Plus modules
|
||||
"""
|
||||
|
||||
from network.net2plus import Net2PlusFinder
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
inst = Net2PlusFinder()
|
||||
print("Please wait for the modules to respond...")
|
||||
devs = inst.find()
|
||||
if devs:
|
||||
for d in devs:
|
||||
print(d)
|
||||
else:
|
||||
print("No devices detected")
|
@ -0,0 +1,17 @@
|
||||
"""
|
||||
Sample script to detect all SQLServer instances
|
||||
"""
|
||||
|
||||
from network.sqlserver import SqlServerFinder
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
inst = SqlServerFinder()
|
||||
print("Please wait for the servers to respond...")
|
||||
srvs = inst.find()
|
||||
if srvs:
|
||||
for s in srvs:
|
||||
print(s)
|
||||
else:
|
||||
print("No servers detected")
|
@ -0,0 +1,22 @@
|
||||
"""
|
||||
Sample script to fetch all access levels
|
||||
"""
|
||||
|
||||
from net2xs import Net2XS
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
# Show all access levels and their details
|
||||
for al in net2.get_py_access_levels():
|
||||
print(al)
|
@ -0,0 +1,21 @@
|
||||
"""
|
||||
Sample script to fetch all cards
|
||||
"""
|
||||
|
||||
from net2xs import Net2XS
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
# Obtain all Net2 cards
|
||||
print(Net2XS.dataset_to_str(net2.get_cards()))
|
@ -0,0 +1,22 @@
|
||||
"""
|
||||
Sample script to fetch all time zones
|
||||
"""
|
||||
|
||||
from net2xs import Net2XS
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
# Show all time zones and their details
|
||||
for tz in net2.get_py_time_zones():
|
||||
print(tz)
|
@ -0,0 +1,47 @@
|
||||
"""
|
||||
Sample script to demonstrate inheritance
|
||||
"""
|
||||
|
||||
from net2xs import Net2XS
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
class MyNet2XS(Net2XS):
|
||||
"""Inherited class for additional functionality
|
||||
"""
|
||||
global Net2XS
|
||||
|
||||
def get_current_user_id(self):
|
||||
"""Return logged on user id
|
||||
"""
|
||||
# Place a lock, for thread safety
|
||||
# (if you don't have threads you kan skip this)
|
||||
with Net2XS._lock:
|
||||
# Basic check if client connection is valid
|
||||
self._check_client()
|
||||
return self._client.CurrentUserID
|
||||
|
||||
def get_client_members(self):
|
||||
"""Return all Net2 client object members using the introspective
|
||||
Python dir function
|
||||
"""
|
||||
with Net2XS._lock:
|
||||
self._check_client()
|
||||
return dir(self._client)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with MyNet2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
# Show current user id
|
||||
print("Current used id:", net2.get_current_user_id())
|
||||
# Show all members
|
||||
print("Net2 client members:", net2.get_client_members())
|
@ -0,0 +1,48 @@
|
||||
"""
|
||||
Sample script to modify a user.
|
||||
It assumes that a John Doe user exists (see add_user sample).
|
||||
"""
|
||||
|
||||
from net2xs import Net2XS
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
# First name
|
||||
FIRST_NAME = "John"
|
||||
# Surname
|
||||
SUR_NAME = "Doe"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
|
||||
# Get user id
|
||||
user_id = net2.get_user_id_by_name((FIRST_NAME, SUR_NAME))
|
||||
|
||||
print("Found user id %d" % (user_id))
|
||||
|
||||
# Found a valid user id
|
||||
if user_id >= 0:
|
||||
|
||||
# Modify expiration date
|
||||
res = net2.modify_user(
|
||||
user_id=user_id,
|
||||
expiry_date=datetime(2022, 12, 31))
|
||||
|
||||
if res:
|
||||
print("Success")
|
||||
else:
|
||||
print("Failure")
|
||||
|
||||
else:
|
||||
print("Failed to find user %s %s" % (FIRST_NAME, SUR_NAME))
|
@ -0,0 +1,48 @@
|
||||
"""
|
||||
Sample script to monitor acu
|
||||
"""
|
||||
|
||||
from time import sleep
|
||||
from net2xs import Net2XS
|
||||
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
# Acu to monitor
|
||||
ACU_ADDRESS = 1219195
|
||||
|
||||
|
||||
def handle_acu_event(sender, event_view):
|
||||
"""Handler for ACU event
|
||||
"""
|
||||
print("-----------------------")
|
||||
print("address=", event_view.Address)
|
||||
print("card=", event_view.CardNumber)
|
||||
print("department=", event_view.Department)
|
||||
print("userid=", event_view.UserId)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
|
||||
# Set function to handle ACU event
|
||||
net2.on_acu_event = handle_acu_event
|
||||
|
||||
res = net2.monitor_acu(ACU_ADDRESS)
|
||||
if not res:
|
||||
print("Failed to monitor ACU %d" % (ACU_ADDRESS))
|
||||
else:
|
||||
print("Monitoring ACU %d; press <ctrl>C to quit" % (ACU_ADDRESS))
|
||||
while True:
|
||||
try:
|
||||
sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
net2.stop_monitoring_acu(ACU_ADDRESS)
|
@ -0,0 +1,60 @@
|
||||
"""
|
||||
Sample script to hold all known doors open for a few seconds.
|
||||
Also shows how to use the logger function.
|
||||
"""
|
||||
import time
|
||||
|
||||
from net2xs import Net2XS
|
||||
from pylog4net import Log4Net
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
def get_doors(net2):
|
||||
"""Obtain a list of all known doors
|
||||
"""
|
||||
res = []
|
||||
dataset = net2.get_doors()
|
||||
if dataset and dataset.Tables.Count > 0:
|
||||
for row in dataset.Tables[0].Rows:
|
||||
res.append(row.Address)
|
||||
return res
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Create logger object
|
||||
logger = Log4Net.get_logger('open_all_doors')
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
# Get list off door addresses
|
||||
doors = get_doors(net2)
|
||||
# Open each door
|
||||
for door in doors:
|
||||
if not net2.hold_door_open(door):
|
||||
logger.Error(
|
||||
"Failed to hold door %d open: %s." %
|
||||
(door, net2.last_error_message))
|
||||
else:
|
||||
logger.Info("Set door %d open." % (door))
|
||||
|
||||
logger.Info("Now all doors are open...")
|
||||
time.sleep(3)
|
||||
|
||||
# Close each door
|
||||
for door in doors:
|
||||
if not net2.close_door(door):
|
||||
logger.Error(
|
||||
"Failed to close door %d: %s." %
|
||||
(door, net2.last_error_message))
|
||||
else:
|
||||
logger.Info("Set door %d closed." % (door))
|
||||
|
||||
logger.Info("Now all doors are closed again")
|
@ -0,0 +1,21 @@
|
||||
"""
|
||||
Sample script to fetch a dataset with all active users
|
||||
"""
|
||||
|
||||
from net2xs import Net2XS
|
||||
|
||||
# Operator id 0 is System Engineer
|
||||
OPERATOR_ID = 0
|
||||
# Default Net2 password
|
||||
OPERATOR_PWD = "net2"
|
||||
# When running on the machine where Net2 is installed
|
||||
NET2_SERVER = "localhost"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Net2XS(NET2_SERVER) as net2:
|
||||
# Authenticate
|
||||
net2.authenticate(OPERATOR_ID, OPERATOR_PWD)
|
||||
# Obtain all Net2 users
|
||||
print(Net2XS.dataset_to_str(net2.get_users()))
|
@ -0,0 +1,21 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Running as py2exe
|
||||
if hasattr(sys, 'frozen'):
|
||||
DEBUG = False
|
||||
BASE_DIR = os.path.split(sys.executable)[0]
|
||||
# Running as interpreter
|
||||
else:
|
||||
DEBUG = True
|
||||
BASE_DIR = '.'
|
||||
|
||||
# Config file to use for dotnet stuff
|
||||
CONFIG_FILE = os.path.join(
|
||||
BASE_DIR, 'Net2Scripting.exe.config')
|
||||
|
||||
# Generic lib dir
|
||||
LIB_DIR = os.path.join(BASE_DIR, 'libs')
|
||||
|
||||
# Version
|
||||
VERSION = '4.0'
|
@ -0,0 +1,35 @@
|
||||
import os
|
||||
|
||||
from distutils.core import setup
|
||||
from glob import glob
|
||||
import py2exe
|
||||
|
||||
# Data files (resources)
|
||||
data_files = [('.', ['Net2Scripting.exe.config']),
|
||||
('.', glob(r'../runtime/*.*')),
|
||||
('libs/log4netdll', glob(r'libs/log4netdll/*.*')),
|
||||
('libs/paxton', glob(r'libs/paxton/*.*')),
|
||||
('resources', glob(r'../resources/*.*')),
|
||||
('docs', glob(r'../docs/*.*')),
|
||||
('samples', glob(r'samples/*.py'))]
|
||||
|
||||
# Source files (python modules)
|
||||
source_files = [os.path.splitext(os.path.basename(f))[0] for f in glob('*.py')]
|
||||
|
||||
setup(
|
||||
# basic console exe
|
||||
console=[{'script': 'Net2Scripting.py',
|
||||
'icon_resources': [(1, '../resources/Net2Scripting.ico')]}],
|
||||
py_modules=source_files,
|
||||
data_files=data_files,
|
||||
# zipfile=None,
|
||||
# options={'py2exe': {'bundle_files': 1, 'compressed': True}}
|
||||
options={
|
||||
'py2exe': {
|
||||
'dist_dir': '../dist',
|
||||
},
|
||||
'build': {
|
||||
'build_base': '../build'
|
||||
}
|
||||
}
|
||||
)
|
Loading…
Reference in New Issue