Merge branch 'develop' into feature_powerview

This commit is contained in:
akuker 2022-01-07 20:43:28 -06:00
commit d8594a5b03
34 changed files with 5083 additions and 6096 deletions

View File

@ -142,12 +142,14 @@ function installRaScsiScreen() {
SCREEN_HEIGHT="32" SCREEN_HEIGHT="32"
fi fi
echo "" if [ -z "$TOKEN" ]; then
echo "Is RaSCSI using token-based authentication? [y/N]" echo ""
read -r REPLY echo "Did you protect your RaSCSI installation with a token password? [y/N]"
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then read -r REPLY
echo -n "Enter the passphrase that you configured: " if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
read -r TOKEN echo -n "Enter the password that you configured with RaSCSI at the time of installation: "
read -r TOKEN
fi
fi fi
stopRaScsiScreen stopRaScsiScreen
@ -178,7 +180,7 @@ function installRaScsiScreen() {
if [ ! -z "$TOKEN" ]; then if [ ! -z "$TOKEN" ]; then
sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT --password=$TOKEN" "$SYSTEMD_PATH/monitor_rascsi.service" sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT --password=$TOKEN" "$SYSTEMD_PATH/monitor_rascsi.service"
sudo chmod 600 "$SYSTEMD_PATH/monitor_rascsi.service" sudo chmod 600 "$SYSTEMD_PATH/monitor_rascsi.service"
echo "Granted access to the OLED Monitor with the token passphrase that you configured for RaSCSI." echo "Granted access to the OLED Monitor with the password that you configured for RaSCSI."
else else
sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT" "$SYSTEMD_PATH/monitor_rascsi.service" sudo sed -i "8 i ExecStart=$BASE/src/oled_monitor/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT" "$SYSTEMD_PATH/monitor_rascsi.service"
fi fi
@ -277,14 +279,14 @@ function backupRaScsiService() {
fi fi
} }
# Modifies and installs the rascsi service # Offers the choice of enabling token-based authentication for RaSCSI
function enableRaScsiService() { function configureTokenAuth() {
echo "" echo ""
echo "Do you want to enable token-based access control for RaSCSI? [y/N]" echo "Do you want to protect your RaSCSI installation with a password? [y/N]"
read REPLY read REPLY
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
echo -n "Enter the passphrase that you want to use: " echo -n "Enter the password that you want to use: "
read -r TOKEN read -r TOKEN
if [ -f "$HOME/.rascsi_secret" ]; then if [ -f "$HOME/.rascsi_secret" ]; then
sudo rm "$HOME/.rascsi_secret" sudo rm "$HOME/.rascsi_secret"
@ -293,10 +295,17 @@ function enableRaScsiService() {
echo "$TOKEN" > "$HOME/.rascsi_secret" echo "$TOKEN" > "$HOME/.rascsi_secret"
sudo chown root:root "$HOME/.rascsi_secret" sudo chown root:root "$HOME/.rascsi_secret"
sudo chmod 600 "$HOME/.rascsi_secret" sudo chmod 600 "$HOME/.rascsi_secret"
echo ""
echo "Configured RaSCSI to use $HOME/.rascsi_secret for authentication. This file is readable by root only."
echo "Make note of your password: you will need it to use rasctl and other RaSCSI clients."
fi
}
# Modifies and installs the rascsi service
function enableRaScsiService() {
if [ ! -z "$TOKEN" ]; then
sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH -P $HOME/.rascsi_secret@" "$SYSTEMD_PATH/rascsi.service" sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH -P $HOME/.rascsi_secret@" "$SYSTEMD_PATH/rascsi.service"
sudo chmod 600 "$SYSTEMD_PATH/rascsi.service" sudo chmod 600 "$SYSTEMD_PATH/rascsi.service"
echo "Configured to use $HOME/.rascsi_secret to secure RaSCSI. This file is readable by root only."
echo "Make note of your passphrase; you will need it to use rasctl, and other RaSCSI clients."
else else
sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH@" "$SYSTEMD_PATH/rascsi.service" sudo sed -i "s@^ExecStart.*@& -F $VIRTUAL_DRIVER_PATH@" "$SYSTEMD_PATH/rascsi.service"
fi fi
@ -318,7 +327,7 @@ function installWebInterfaceService() {
if [ ! -z "$TOKEN" ]; then if [ ! -z "$TOKEN" ]; then
sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh --password=$TOKEN" "$SYSTEMD_PATH/rascsi-web.service" sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh --password=$TOKEN" "$SYSTEMD_PATH/rascsi-web.service"
sudo chmod 600 "$SYSTEMD_PATH/rascsi-web.service" sudo chmod 600 "$SYSTEMD_PATH/rascsi-web.service"
echo "Granted access to the Web Interface with the token passphrase that you configured for RaSCSI." echo "Granted access to the Web Interface with the token password that you configured for RaSCSI."
else else
sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh" "$SYSTEMD_PATH/rascsi-web.service" sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh" "$SYSTEMD_PATH/rascsi-web.service"
fi fi
@ -724,7 +733,7 @@ function installMacproxy {
( sudo apt-get update && sudo apt-get install python3 python3-venv --assume-yes ) </dev/null ( sudo apt-get update && sudo apt-get install python3 python3-venv --assume-yes ) </dev/null
MACPROXY_VER="21.12.2" MACPROXY_VER="21.12.3"
MACPROXY_PATH="$HOME/macproxy-$MACPROXY_VER" MACPROXY_PATH="$HOME/macproxy-$MACPROXY_VER"
if [ -d "$MACPROXY_PATH" ]; then if [ -d "$MACPROXY_PATH" ]; then
echo "The $MACPROXY_PATH directory already exists. Delete it to proceed with the installation." echo "The $MACPROXY_PATH directory already exists. Delete it to proceed with the installation."
@ -784,11 +793,13 @@ function runChoice() {
echo "- Install additional packages with apt-get" echo "- Install additional packages with apt-get"
echo "- Add and modify systemd services" echo "- Add and modify systemd services"
echo "- Modify and enable Apache2 and Nginx web services" echo "- Modify and enable Apache2 and Nginx web services"
echo "- Create directories and change permissions" echo "- Create files and directories"
echo "- Change permissions of files and directories"
echo "- Modify user groups and permissions" echo "- Modify user groups and permissions"
echo "- Install binaries to /usr/local/bin" echo "- Install binaries to /usr/local/bin"
echo "- Install manpages to /usr/local/man" echo "- Install manpages to /usr/local/man"
sudoCheck sudoCheck
configureTokenAuth
stopOldWebInterface stopOldWebInterface
updateRaScsiGit updateRaScsiGit
createImagesDir createImagesDir
@ -800,9 +811,13 @@ function runChoice() {
backupRaScsiService backupRaScsiService
installRaScsi installRaScsi
enableRaScsiService enableRaScsiService
startRaScsiScreen if [ -f "$SYSTEMD_PATH/monitor_rascsi.service" ]; then
echo "Detected monitor_rascsi.service; will run the installation steps for the OLED monitor."
installRaScsiScreen
fi
installRaScsiWebInterface installRaScsiWebInterface
installWebInterfaceService installWebInterfaceService
showRaScsiScreenStatus
showRaScsiStatus showRaScsiStatus
showRaScsiWebStatus showRaScsiWebStatus
notifyBackup notifyBackup
@ -813,11 +828,13 @@ function runChoice() {
echo "This script will make the following changes to your system:" echo "This script will make the following changes to your system:"
echo "- Install additional packages with apt-get" echo "- Install additional packages with apt-get"
echo "- Add and modify systemd services" echo "- Add and modify systemd services"
echo "- Create directories and change permissions" echo "- Create files ans directories"
echo "- Change permissions of files and directories"
echo "- Modify user groups and permissions" echo "- Modify user groups and permissions"
echo "- Install binaries to /usr/local/bin" echo "- Install binaries to /usr/local/bin"
echo "- Install manpages to /usr/local/man" echo "- Install manpages to /usr/local/man"
sudoCheck sudoCheck
configureTokenAuth
updateRaScsiGit updateRaScsiGit
createImagesDir createImagesDir
installPackages installPackages
@ -827,7 +844,11 @@ function runChoice() {
backupRaScsiService backupRaScsiService
installRaScsi installRaScsi
enableRaScsiService enableRaScsiService
startRaScsiScreen if [ -f "$SYSTEMD_PATH/monitor_rascsi.service" ]; then
echo "Detected monitor_rascsi.service; will run the installation steps for the OLED monitor."
installRaScsiScreen
fi
showRaScsiScreenStatus
showRaScsiStatus showRaScsiStatus
notifyBackup notifyBackup
echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE:-FULLSPEC}) - Complete!" echo "Installing / Updating RaSCSI Service (${CONNECT_TYPE:-FULLSPEC}) - Complete!"

View File

@ -15,7 +15,14 @@ ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as # Python code to execute, usually for sys.path manipulation such as
# pygtk.require(). # pygtk.require().
#init-hook= init-hook=
# venv hook for pylint
# Requires pylint-venv package:
# $ pip install pylint-venv
try: import pylint_venv
except ImportError: pass
else: pylint_venv.inithook()
# Use multiple processes to speed up Pylint. # Use multiple processes to speed up Pylint.
jobs=1 jobs=1

View File

@ -31,8 +31,8 @@
THE SOFTWARE. THE SOFTWARE.
""" """
import argparse import argparse
import sys
from time import sleep from time import sleep
from sys import argv
from collections import deque from collections import deque
from board import I2C from board import I2C
from adafruit_ssd1306 import SSD1306_I2C from adafruit_ssd1306 import SSD1306_I2C
@ -95,20 +95,22 @@ I2C = I2C()
# 128x32 display with hardware I2C: # 128x32 display with hardware I2C:
OLED = SSD1306_I2C(WIDTH, HEIGHT, I2C, addr=0x3C, reset=OLED_RESET) OLED = SSD1306_I2C(WIDTH, HEIGHT, I2C, addr=0x3C, reset=OLED_RESET)
OLED.rotation = ROTATION
print("Running with the following display:") print("Running with the following display:")
print(OLED) print(OLED)
print() print()
print("Will update the OLED display every " + str(DELAY_TIME_MS) + "ms (approximately)") print("Will update the OLED display every " + str(DELAY_TIME_MS) + "ms (approximately)")
# Clear display. # Show a startup splash bitmap image before starting the main loop
OLED.rotation = ROTATION # Convert the image to mode '1' for 1-bit color (monochrome)
OLED.fill(0) # Make sure the splash bitmap image is in the same dir as this script
IMAGE = Image.open(f"splash_start_{HEIGHT}.bmp").convert("1")
OLED.image(IMAGE)
OLED.show() OLED.show()
# Create blank image for drawing. # Keep the pretty splash on screen for a number of seconds
# Make sure to create image with mode '1' for 1-bit color. sleep(4)
IMAGE = Image.new("1", (OLED.width, OLED.height))
# Get drawing object to draw on image. # Get drawing object to draw on image.
DRAW = ImageDraw.Draw(IMAGE) DRAW = ImageDraw.Draw(IMAGE)
@ -138,11 +140,6 @@ LINE_SPACING = 8
# Some other nice fonts to try: http://www.dafont.com/bitmap.php # Some other nice fonts to try: http://www.dafont.com/bitmap.php
FONT = ImageFont.truetype('type_writer.ttf', FONT_SIZE) FONT = ImageFont.truetype('type_writer.ttf', FONT_SIZE)
# Load a bitmap image for start and stop splash screens and convert to monocrome
# Make sure the splash bitmap image is in the same dir as this script
SPLASH_START = Image.open(f"splash_start_{HEIGHT}.bmp").convert("1")
SPLASH_STOP = Image.open(f"splash_stop_{HEIGHT}.bmp").convert("1")
IP_ADDR, HOSTNAME = get_ip_and_host() IP_ADDR, HOSTNAME = get_ip_and_host()
@ -190,28 +187,7 @@ def formatted_output():
return output return output
def start_splash():
"""
Displays a splash screen for the startup sequence
"""
OLED.image(SPLASH_START)
OLED.show()
sleep(4)
def stop_splash():
"""
Displays a splash screen for the shutdown sequence
"""
OLED.image(SPLASH_STOP)
OLED.show()
# Show a startup splash bitmap image before starting the main loop
start_splash()
with GracefulInterruptHandler() as handler: with GracefulInterruptHandler() as handler:
"""
The main loop of displaying attached device info, and other info
"""
while True: while True:
# The reference snapshot of attached devices that will be compared against each cycle # The reference snapshot of attached devices that will be compared against each cycle
@ -225,10 +201,10 @@ with GracefulInterruptHandler() as handler:
while snapshot == ref_snapshot: while snapshot == ref_snapshot:
# Draw a black filled box to clear the image. # Draw a black filled box to clear the image.
DRAW.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0) DRAW.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
y_pos = TOP Y_POS = TOP
for output_line in active_output: for output_line in active_output:
DRAW.text((X_POS, y_pos), output_line, font=FONT, fill=255) DRAW.text((X_POS, Y_POS), output_line, font=FONT, fill=255)
y_pos += LINE_SPACING Y_POS += LINE_SPACING
# Shift the index of the array by one to get a scrolling effect # Shift the index of the array by one to get a scrolling effect
if len(active_output) > LINES: if len(active_output) > LINES:
@ -242,6 +218,8 @@ with GracefulInterruptHandler() as handler:
snapshot = formatted_output() snapshot = formatted_output()
if handler.interrupted: if handler.interrupted:
# Catch interrupt signals and show a shutdown splash bitmap image # Catch interrupt signals and blank out the screen
stop_splash() DRAW.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
exit("Shutting down the OLED display...") OLED.image(IMAGE)
OLED.show()
sys.exit("Shutting down the OLED display...")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -66,9 +66,13 @@ fi
# Test for two known broken venv states # Test for two known broken venv states
if test -e venv; then if test -e venv; then
GOOD_VENV=true GOOD_VENV=true
! test -e venv/bin/activate && GOOD_VENV=false if ! test -e venv/bin/activate; then
pip3 list 1> /dev/null GOOD_VENV=false
test $? -eq 1 && GOOD_VENV=false else
source venv/bin/activate
pip3 list 1> /dev/null
test $? -eq 1 && GOOD_VENV=false
fi
if ! "$GOOD_VENV"; then if ! "$GOOD_VENV"; then
echo "Deleting bad python venv" echo "Deleting bad python venv"
sudo rm -rf venv sudo rm -rf venv

View File

@ -5,11 +5,14 @@
*.layout *.layout
*.log *.log
*.vcd *.vcd
*.json
*.html
rascsi rascsi
scsimon scsimon
rasctl rasctl
sasidump sasidump
rasdump rasdump
scisparse
obj obj
bin bin
/rascsi_interface.pb.cpp /rascsi_interface.pb.cpp

View File

@ -24,8 +24,13 @@ else
CXXFLAGS += -O3 -Wall -Werror -DNDEBUG CXXFLAGS += -O3 -Wall -Werror -DNDEBUG
BUILD_TYPE = Release BUILD_TYPE = Release
endif endif
ifeq ("$(shell uname -s)","Linux")
# -Wno-psabi might not work on non-Linux platforms
CXXFLAGS += -Wno-psabi
endif
CFLAGS += -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP CFLAGS += -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
CXXFLAGS += -std=c++17 -Wno-psabi -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP CXXFLAGS += -std=c++17 -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
## EXTRA_FLAGS : Can be used to pass special purpose flags ## EXTRA_FLAGS : Can be used to pass special purpose flags
CFLAGS += $(EXTRA_FLAGS) CFLAGS += $(EXTRA_FLAGS)
@ -105,9 +110,8 @@ SRC_SCSIMON = \
scsimon.cpp \ scsimon.cpp \
scsi.cpp \ scsi.cpp \
gpiobus.cpp \ gpiobus.cpp \
filepath.cpp \
fileio.cpp \
rascsi_version.cpp rascsi_version.cpp
SRC_SCSIMON += $(shell find ./monitor -name '*.cpp')
SRC_RASCTL = \ SRC_RASCTL = \
rasctl.cpp\ rasctl.cpp\
@ -135,8 +139,8 @@ SRC_SASIDUMP = \
fileio.cpp\ fileio.cpp\
rascsi_version.cpp rascsi_version.cpp
vpath %.h ./ ./controllers ./devices vpath %.h ./ ./controllers ./devices ./monitor
vpath %.cpp ./ ./controllers ./devices vpath %.cpp ./ ./controllers ./devices ./monitor
vpath %.o ./$(OBJDIR) vpath %.o ./$(OBJDIR)
vpath ./$(BINDIR) vpath ./$(BINDIR)
@ -193,6 +197,16 @@ $(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR)
$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR) $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread
# Phony rules for building individual utilities
.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON)
$(RASCSI) : $(BINDIR)/$(RASCSI)
$(RASCTL) : $(BINDIR)/$(RASCTL)
$(RASDUMP) : $(BINDIR)/$(RASDUMP)
$(SASIDUMP): $(BINDIR)/$(SASIDUMP)
$(SCSIMON) : $(BINDIR)/$(SCSIMON)
## clean : Remove all of the object files, intermediate ## clean : Remove all of the object files, intermediate
## compiler files and executable files ## compiler files and executable files
.PHONY: clean .PHONY: clean

View File

@ -1385,6 +1385,7 @@ BOOL GPIOBUS::WaitSignal(int pin, BOOL ast)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void GPIOBUS::DisableIRQ() void GPIOBUS::DisableIRQ()
{ {
#ifdef __linux__
if (rpitype == 4) { if (rpitype == 4) {
// RPI4 is disabled by GICC // RPI4 is disabled by GICC
giccpmr = gicc[GICC_PMR]; giccpmr = gicc[GICC_PMR];
@ -1399,6 +1400,9 @@ void GPIOBUS::DisableIRQ()
irptenb = irpctl[IRPT_ENB_IRQ_1]; irptenb = irpctl[IRPT_ENB_IRQ_1];
irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf; irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf;
} }
#else
(void)0;
#endif
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -19,72 +19,114 @@ using namespace std;
Localizer::Localizer() Localizer::Localizer()
{ {
// Supported locales, always lower case // Supported locales, always lower case
supported_languages = { "en", "de", "sv" }; supported_languages = { "en", "de", "sv", "fr", "es" };
// Positional string arguments are %1, %2, %3 // Positional string arguments are %1, %2, %3
Add(ERROR_AUTHENTICATION, "en", "Authentication failed"); Add(ERROR_AUTHENTICATION, "en", "Authentication failed");
Add(ERROR_AUTHENTICATION, "de", "Authentifizierung fehlgeschlagen"); Add(ERROR_AUTHENTICATION, "de", "Authentifizierung fehlgeschlagen");
Add(ERROR_AUTHENTICATION, "sv", "Autentisering misslyckades"); Add(ERROR_AUTHENTICATION, "sv", "Autentisering misslyckades");
Add(ERROR_AUTHENTICATION, "fr", "Authentification éronnée");
Add(ERROR_AUTHENTICATION, "es", "Fallo de autentificación");
Add(ERROR_OPERATION, "en", "Unknown operation"); Add(ERROR_OPERATION, "en", "Unknown operation");
Add(ERROR_OPERATION, "de", "Unbekannte Operation"); Add(ERROR_OPERATION, "de", "Unbekannte Operation");
Add(ERROR_OPERATION, "sv", "Okänd operation"); Add(ERROR_OPERATION, "sv", "Okänd operation");
Add(ERROR_OPERATION, "fr", "Opération inconnue");
Add(ERROR_OPERATION, "es", "Operación desconocida");
Add(ERROR_LOG_LEVEL, "en", "Invalid log level %1"); Add(ERROR_LOG_LEVEL, "en", "Invalid log level %1");
Add(ERROR_LOG_LEVEL, "de", "Ungültiger Log-Level %1"); Add(ERROR_LOG_LEVEL, "de", "Ungültiger Log-Level %1");
Add(ERROR_LOG_LEVEL, "sv", "Ogiltig loggnivå %1"); Add(ERROR_LOG_LEVEL, "sv", "Ogiltig loggnivå %1");
Add(ERROR_LOG_LEVEL, "fr", "Niveau de journalisation invalide %1");
Add(ERROR_LOG_LEVEL, "es", "Nivel de registro %1 no válido");
Add(ERROR_MISSING_DEVICE_ID, "en", "Missing device ID"); Add(ERROR_MISSING_DEVICE_ID, "en", "Missing device ID");
Add(ERROR_MISSING_DEVICE_ID, "de", "Fehlende Geräte-ID"); Add(ERROR_MISSING_DEVICE_ID, "de", "Fehlende Geräte-ID");
Add(ERROR_MISSING_DEVICE_ID, "sv", "Enhetens ID saknas"); Add(ERROR_MISSING_DEVICE_ID, "sv", "Enhetens ID saknas");
Add(ERROR_MISSING_DEVICE_ID, "fr", "ID de périphérique manquante");
Add(ERROR_MISSING_DEVICE_ID, "es", "Falta el ID del dispositivo");
Add(ERROR_MISSING_FILENAME, "en", "Missing filename"); Add(ERROR_MISSING_FILENAME, "en", "Missing filename");
Add(ERROR_MISSING_FILENAME, "de", "Fehlender Dateiname"); Add(ERROR_MISSING_FILENAME, "de", "Fehlender Dateiname");
Add(ERROR_MISSING_FILENAME, "sv", "Filnamn saknas"); Add(ERROR_MISSING_FILENAME, "sv", "Filnamn saknas");
Add(ERROR_MISSING_FILENAME, "fr", "Nom de fichier manquant");
Add(ERROR_MISSING_FILENAME, "es", "Falta el nombre del archivo");
Add(ERROR_IMAGE_IN_USE, "en", "Image file '%1' is already being used by ID %2, unit %3"); Add(ERROR_IMAGE_IN_USE, "en", "Image file '%1' is already being used by ID %2, unit %3");
Add(ERROR_IMAGE_IN_USE, "de", "Image-Datei '%1' wird bereits von ID %2, Einheit %3 benutzt"); Add(ERROR_IMAGE_IN_USE, "de", "Image-Datei '%1' wird bereits von ID %2, Einheit %3 benutzt");
Add(ERROR_IMAGE_IN_USE, "sv", "Skivbildfilen '%1' används redan av ID %2, LUN %3"); Add(ERROR_IMAGE_IN_USE, "sv", "Skivbildfilen '%1' används redan av ID %2, LUN %3");
Add(ERROR_IMAGE_IN_USE, "fr", "Le fichier d'image '%1' est déjà utilisé par l'ID %2, unité %3");
Add(ERROR_IMAGE_IN_USE, "es", "El archivo de imagen '%1' ya está siendo utilizado por el ID %2, unidad %3");
Add(ERROR_RESERVED_ID, "en", "Device ID %1 is reserved"); Add(ERROR_RESERVED_ID, "en", "Device ID %1 is reserved");
Add(ERROR_RESERVED_ID, "de", "Geräte-ID %1 ist reserviert"); Add(ERROR_RESERVED_ID, "de", "Geräte-ID %1 ist reserviert");
Add(ERROR_RESERVED_ID, "sv", "Enhets-ID %1 är reserverat"); Add(ERROR_RESERVED_ID, "sv", "Enhets-ID %1 är reserverat");
Add(ERROR_RESERVED_ID, "fr", "ID de périphérique %1 réservée");
Add(ERROR_RESERVED_ID, "es", "El ID de dispositivo %1 está reservado");
Add(ERROR_NON_EXISTING_DEVICE, "en", "Command for non-existing ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "en", "Command for non-existing ID %1");
Add(ERROR_NON_EXISTING_DEVICE, "de", "Kommando für nicht existente ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "de", "Kommando für nicht existente ID %1");
Add(ERROR_NON_EXISTING_DEVICE, "sv", "Kommando för avsaknat ID %1"); Add(ERROR_NON_EXISTING_DEVICE, "sv", "Kommando för avsaknat ID %1");
Add(ERROR_NON_EXISTING_DEVICE, "fr", "Commande pour ID %1 non-existant");
Add(ERROR_NON_EXISTING_DEVICE, "es", "Comando para ID %1 no existente");
Add(ERROR_NON_EXISTING_UNIT, "en", "Command for non-existing ID %1, unit %2"); Add(ERROR_NON_EXISTING_UNIT, "en", "Command for non-existing ID %1, unit %2");
Add(ERROR_NON_EXISTING_UNIT, "de", "Kommando für nicht existente ID %1, Einheit %2"); Add(ERROR_NON_EXISTING_UNIT, "de", "Kommando für nicht existente ID %1, Einheit %2");
Add(ERROR_NON_EXISTING_UNIT, "sv", "Kommando för avsaknat ID %1, LUN %2"); Add(ERROR_NON_EXISTING_UNIT, "sv", "Kommando för avsaknat ID %1, LUN %2");
Add(ERROR_NON_EXISTING_UNIT, "fr", "Command pour ID %1, unité %2 non-existant");
Add(ERROR_NON_EXISTING_UNIT, "es", "Comando para ID %1 inexistente, unidad %2");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "en", "Unknown device type %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "en", "Unknown device type %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "de", "Unbekannter Gerätetyp %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "de", "Unbekannter Gerätetyp %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "sv", "Obekant enhetstyp: %1"); Add(ERROR_UNKNOWN_DEVICE_TYPE, "sv", "Obekant enhetstyp: %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "fr", "Type de périphérique inconnu %1");
Add(ERROR_UNKNOWN_DEVICE_TYPE, "es", "Tipo de dispositivo desconocido %1");
Add(ERROR_MISSING_DEVICE_TYPE, "en", "Device type required for unknown extension of file '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "en", "Device type required for unknown extension of file '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "de", "Gerätetyp erforderlich für unbekannte Extension der Datei '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "de", "Gerätetyp erforderlich für unbekannte Extension der Datei '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "sv", "Man måste ange enhetstyp för obekant filändelse '%1'"); Add(ERROR_MISSING_DEVICE_TYPE, "sv", "Man måste ange enhetstyp för obekant filändelse '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "fr", "Type de périphérique requis pour extension inconnue du fichier '%1'");
Add(ERROR_MISSING_DEVICE_TYPE, "es", "Tipo de dispositivo requerido para la extensión desconocida del archivo '%1'");
Add(ERROR_DUPLICATE_ID, "en", "Duplicate ID %1, unit %2"); Add(ERROR_DUPLICATE_ID, "en", "Duplicate ID %1, unit %2");
Add(ERROR_DUPLICATE_ID, "de", "Doppelte ID %1, Einheit %2"); Add(ERROR_DUPLICATE_ID, "de", "Doppelte ID %1, Einheit %2");
Add(ERROR_DUPLICATE_ID, "sv", "Duplikat ID %1, LUN %2"); Add(ERROR_DUPLICATE_ID, "sv", "Duplikat ID %1, LUN %2");
Add(ERROR_DUPLICATE_ID, "fr", "ID %1, unité %2 dupliquée");
Add(ERROR_DUPLICATE_ID, "es", "ID duplicado %1, unidad %2");
Add(ERROR_SASI_SCSI, "en", "SASI and SCSI can't be used at the same time"); Add(ERROR_SASI_SCSI, "en", "SASI and SCSI can't be used at the same time");
Add(ERROR_SASI_SCSI, "de", "SASI und SCSI können nicht gleichzeitig verwendet werden"); Add(ERROR_SASI_SCSI, "de", "SASI und SCSI können nicht gleichzeitig verwendet werden");
Add(ERROR_SASI_SCSI, "sv", "SASI och SCSI kan ej användas samtidigt"); Add(ERROR_SASI_SCSI, "sv", "SASI och SCSI kan ej användas samtidigt");
Add(ERROR_SASI_SCSI, "fr", "SASI et SCSI ne peuvent être utilisés en même temps");
Add(ERROR_SASI_SCSI, "es", "SASI y SCSI no pueden utilizarse al mismo tiempo");
Add(ERROR_EJECT_REQUIRED, "en", "Existing medium must first be ejected"); Add(ERROR_EJECT_REQUIRED, "en", "Existing medium must first be ejected");
Add(ERROR_EJECT_REQUIRED, "de", "Das vorhandene Medium muss erst ausgeworfen werden"); Add(ERROR_EJECT_REQUIRED, "de", "Das vorhandene Medium muss erst ausgeworfen werden");
Add(ERROR_EJECT_REQUIRED, "sv", "Nuvarande skiva måste utmatas först"); Add(ERROR_EJECT_REQUIRED, "sv", "Nuvarande skiva måste utmatas först");
Add(ERROR_EJECT_REQUIRED, "fr", "Media déjà existant doit d'abord être éjecté");
Add(ERROR_EJECT_REQUIRED, "es", "El medio existente debe ser expulsado primero");
Add(ERROR_DEVICE_NAME_UPDATE, "en", "Once set the device name cannot be changed anymore"); Add(ERROR_DEVICE_NAME_UPDATE, "en", "Once set the device name cannot be changed anymore");
Add(ERROR_DEVICE_NAME_UPDATE, "de", "Ein bereits gesetzter Gerätename kann nicht mehr geändert werden"); Add(ERROR_DEVICE_NAME_UPDATE, "de", "Ein bereits gesetzter Gerätename kann nicht mehr geändert werden");
Add(ERROR_DEVICE_NAME_UPDATE, "sv", "Enhetsnamn kan ej ändras efter att ha fastställts en gång"); Add(ERROR_DEVICE_NAME_UPDATE, "sv", "Enhetsnamn kan ej ändras efter att ha fastställts en gång");
Add(ERROR_DEVICE_NAME_UPDATE, "fr", "Une fois défini, le nom de périphérique ne peut plus être changé");
Add(ERROR_DEVICE_NAME_UPDATE, "es", "Una vez establecido el nombre del dispositivo ya no se puede cambiar");
Add(ERROR_SHUTDOWN_MODE_MISSING, "en", "Missing shutdown mode"); Add(ERROR_SHUTDOWN_MODE_MISSING, "en", "Missing shutdown mode");
Add(ERROR_SHUTDOWN_MODE_MISSING, "de", "Fehlender Shutdown-Modus"); Add(ERROR_SHUTDOWN_MODE_MISSING, "de", "Fehlender Shutdown-Modus");
Add(ERROR_SHUTDOWN_MODE_MISSING, "sv", "Avstängningsläge saknas"); Add(ERROR_SHUTDOWN_MODE_MISSING, "sv", "Avstängningsläge saknas");
Add(ERROR_SHUTDOWN_MODE_MISSING, "fr", "Mode d'extinction manquant");
Add(ERROR_SHUTDOWN_MODE_MISSING, "es", "Falta el modo de apagado");
Add(ERROR_SHUTDOWN_MODE_INVALID, "en", "Invalid shutdown mode '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "en", "Invalid shutdown mode '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "de", "Ungültiger Shutdown-Modus '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "de", "Ungültiger Shutdown-Modus '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "sv", "Ogiltigt avstängsningsläge: '%1'"); Add(ERROR_SHUTDOWN_MODE_INVALID, "sv", "Ogiltigt avstängsningsläge: '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "fr", "Mode d'extinction invalide '%1'");
Add(ERROR_SHUTDOWN_MODE_INVALID, "es", "Modo de apagado inválido '%1'");
Add(ERROR_SHUTDOWN_PERMISSION, "en", "Missing root permission for shutdown or reboot"); Add(ERROR_SHUTDOWN_PERMISSION, "en", "Missing root permission for shutdown or reboot");
Add(ERROR_SHUTDOWN_PERMISSION, "de", "Fehlende Root-Berechtigung für Shutdown oder Neustart"); Add(ERROR_SHUTDOWN_PERMISSION, "de", "Fehlende Root-Berechtigung für Shutdown oder Neustart");
Add(ERROR_SHUTDOWN_PERMISSION, "sv", "Root-rättigheter saknas för att kunna stänga av eller starta om systemet"); Add(ERROR_SHUTDOWN_PERMISSION, "sv", "Root-rättigheter saknas för att kunna stänga av eller starta om systemet");
Add(ERROR_SHUTDOWN_PERMISSION, "fr", "Permissions root manquantes pour extinction ou redémarrage");
Add(ERROR_SHUTDOWN_PERMISSION, "es", "Falta el permiso de root para el apagado o el reinicio");
Add(ERROR_FILE_OPEN, "en", "Invalid or non-existing file '%1': %2"); Add(ERROR_FILE_OPEN, "en", "Invalid or non-existing file '%1': %2");
Add(ERROR_FILE_OPEN, "de", "Ungültige oder fehlende Datei '%1': %2"); Add(ERROR_FILE_OPEN, "de", "Ungültige oder fehlende Datei '%1': %2");
Add(ERROR_FILE_OPEN, "sv", "Ogiltig eller saknad fil '%1': %2"); Add(ERROR_FILE_OPEN, "sv", "Ogiltig eller saknad fil '%1': %2");
Add(ERROR_FILE_OPEN, "fr", "Fichier invalide ou non-existant '%1': %2");
Add(ERROR_FILE_OPEN, "es", "Archivo inválido o inexistente '%1': %2");
Add(ERROR_BLOCK_SIZE, "en", "Invalid block size %1 bytes"); Add(ERROR_BLOCK_SIZE, "en", "Invalid block size %1 bytes");
Add(ERROR_BLOCK_SIZE, "de", "Ungültige Blockgröße %1 Bytes"); Add(ERROR_BLOCK_SIZE, "de", "Ungültige Blockgröße %1 Bytes");
Add(ERROR_BLOCK_SIZE, "sv", "Ogiltig blockstorlek: %1 byte"); Add(ERROR_BLOCK_SIZE, "sv", "Ogiltig blockstorlek: %1 byte");
Add(ERROR_BLOCK_SIZE, "fr", "Taille de bloc invalide %1 octets");
Add(ERROR_BLOCK_SIZE, "es", "Tamaño de bloque inválido %1 bytes");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "en", "Block size for device type %1 is not configurable"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "en", "Block size for device type %1 is not configurable");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "de", "Blockgröße für Gerätetyp %1 ist nicht konfigurierbar"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "de", "Blockgröße für Gerätetyp %1 ist nicht konfigurierbar");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "sv", "Enhetstypen %1 kan inte använda andra blockstorlekar"); Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "sv", "Enhetstypen %1 kan inte använda andra blockstorlekar");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "fr", "Taille de block pour le type de périphérique %1 non configurable");
Add(ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, "es", "El tamaño del bloque para el tipo de dispositivo %1 no es configurable");
} }
void Localizer::Add(LocalizationKey key, const string& locale, const string& value) void Localizer::Add(LocalizationKey key, const string& locale, const string& value)

View File

@ -0,0 +1,38 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "scsi.h"
#include "data_sample.h"
const char *GetPhaseStr(const data_capture *sample)
{
return BUS::GetPhaseStrRaw(GetPhase(sample));
}
BUS::phase_t GetPhase(const data_capture *sample)
{
// Selection Phase
if (GetSel(sample))
{
return BUS::selection;
}
// Bus busy phase
if (!GetBsy(sample))
{
return BUS::busfree;
}
// Get target phase from bus signal line
DWORD mci = GetMsg(sample) ? 0x04 : 0x00;
mci |= GetCd(sample) ? 0x02 : 0x00;
mci |= GetIo(sample) ? 0x01 : 0x00;
return BUS::GetPhase(mci);
}

View File

@ -0,0 +1,49 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
#include "gpiobus.h"
typedef struct data_capture
{
DWORD data;
uint64_t timestamp;
} data_capture_t;
#define GET_PIN(SAMPLE, PIN) ((bool)((SAMPLE->data >> PIN) & 1))
inline bool GetBsy(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_BSY); }
inline bool GetSel(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_SEL); }
inline bool GetAtn(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ATN); }
inline bool GetAck(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_ACK); }
inline bool GetRst(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_RST); }
inline bool GetMsg(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_MSG); }
inline bool GetCd(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_CD); }
inline bool GetIo(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_IO); }
inline bool GetReq(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_REQ); }
inline bool GetDp(const data_capture *sample) { return BUS::GetPinRaw(sample->data, PIN_DP); }
inline BYTE GetData(const data_capture *sample)
{
DWORD data = sample->data;
return (BYTE)((data >> (PIN_DT0 - 0)) & (1 << 0)) |
((data >> (PIN_DT1 - 1)) & (1 << 1)) |
((data >> (PIN_DT2 - 2)) & (1 << 2)) |
((data >> (PIN_DT3 - 3)) & (1 << 3)) |
((data >> (PIN_DT4 - 4)) & (1 << 4)) |
((data >> (PIN_DT5 - 5)) & (1 << 5)) |
((data >> (PIN_DT6 - 6)) & (1 << 6)) |
((data >> (PIN_DT7 - 7)) & (1 << 7));
}
const char *GetPhaseStr(const data_capture *sample);
BUS::phase_t GetPhase(const data_capture *sample);

View File

@ -0,0 +1,205 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
//---------------------------------------------------------------------------
#include <stdio.h>
#include <iostream>
#include <fstream>
#include "os.h"
#include "log.h"
#include "sm_reports.h"
#include "rascsi_version.h"
using namespace std;
const static string html_header = R"(
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
h1 {
text-align: center;
}
h2 {
text-align: center;
}
h3 {
text-align: center;
}
pre {
text-align: center;
}
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
}
.active, .collapsible:hover {
background-color: #555;
}
.content {
padding: 0;
display: none;
overflow: hidden;
background-color: #f1f1f1;
}
</style>
</head>
)";
static void print_copyright_info(ofstream& html_fp)
{
html_fp << "<table>" << endl \
<< "<h1>RaSCSI scsimon Capture Tool</h1>" << endl \
<< "<pre>Version " << rascsi_get_version_string() \
<< __DATE__ << " " << __TIME__ << endl \
<< "Copyright (C) 2016-2020 GIMONS" << endl \
<< "Copyright (C) 2020-2021 Contributors to the RaSCSI project" << endl \
<< "</pre></table>" << endl \
<< "<br>" << endl;
}
static const string html_footer = R"(
</table>
<script>
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
</script>
</html>
)";
static void print_html_data(ofstream& html_fp, const data_capture *data_capture_array, DWORD capture_count)
{
const data_capture *data;
bool prev_data_valid = false;
bool curr_data_valid;
DWORD selected_id = 0;
BUS::phase_t prev_phase = BUS::busfree;
bool close_row = false;
int data_space_count = 0;
bool collapsible_div_active = false;
bool button_active = false;
html_fp << "<table>" << endl;
for (DWORD idx = 0; idx < capture_count; idx++)
{
data = &data_capture_array[idx];
curr_data_valid = GetAck(data) && GetReq(data);
BUS::phase_t phase = GetPhase(data);
if (phase == BUS::selection && !GetBsy(data))
{
selected_id = GetData(data);
}
if (prev_phase != phase)
{
if (close_row)
{
if (collapsible_div_active)
{
html_fp << "</code></div>";
}
else if (button_active)
{
html_fp << "</code></button>";
}
html_fp << "</td>";
if (data_space_count < 1)
{
html_fp << "<td>--</td>";
}
else
{
html_fp << "<td>wc: " << std::dec << "(0x" << std::hex << data_space_count << ")</td>";
}
html_fp << "</tr>" << endl;
data_space_count = 0;
}
html_fp << "<tr>";
close_row = true; // Close the row the next time around
html_fp << "<td>" << (double)data->timestamp / 100000 << "</td>";
html_fp << "<td>" << GetPhaseStr(data) << "</td>";
html_fp << "<td>" << std::hex << selected_id << "</td>";
html_fp << "<td>";
}
if (curr_data_valid && !prev_data_valid)
{
if (data_space_count == 0)
{
button_active = true;
html_fp << "<button type=\"button\" class=\"collapsible\"><code>";
}
if ((data_space_count % 16) == 0)
{
html_fp << std::hex << data_space_count << ": ";
}
html_fp << fmt::format("{0:02X}", GetData(data));
data_space_count++;
if ((data_space_count % 4) == 0)
{
html_fp << " ";
}
if (data_space_count == 16)
{
html_fp << "</code></button><div class=\"content\"><code>" << endl;
collapsible_div_active = true;
button_active = false;
}
if (((data_space_count % 16) == 0) && (data_space_count > 17))
{
html_fp << "<br>" << endl;
}
}
prev_data_valid = curr_data_valid;
prev_phase = phase;
}
}
void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, DWORD capture_count)
{
LOGINFO("Creating HTML report file (%s)", filename);
ofstream html_ofstream;
html_ofstream.open(filename, ios::out);
html_ofstream << html_header;
print_copyright_info(html_ofstream);
print_html_data(html_ofstream, data_capture_array, capture_count);
html_ofstream << html_footer;
html_ofstream.close();
}

View File

@ -0,0 +1,98 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
//---------------------------------------------------------------------------
#include "sm_reports.h"
#include "log.h"
#include "spdlog/spdlog.h"
#include "string.h"
#include <iostream>
#include <fstream>
using namespace std;
const char timestamp_label[] = "\"timestamp\":\"0x";
const char data_label[] = "\"data\":\"0x";
DWORD scsimon_read_json(const char *json_filename, data_capture *data_capture_array, DWORD max_sz)
{
char str_buf[1024];
FILE *fp = fopen(json_filename, "r");
DWORD sample_count = 0;
while (fgets(str_buf, sizeof(str_buf), fp))
{
char timestamp[1024];
char data[1024];
uint64_t timestamp_uint;
uint32_t data_uint;
char *ptr;
char *timestamp_str;
char *data_str;
timestamp_str = strstr(str_buf, timestamp_label);
if (!timestamp_str)
continue;
strncpy(timestamp, &timestamp_str[strlen(timestamp_label)], 16);
timestamp[16] = '\0';
timestamp_uint = strtoull(timestamp, &ptr, 16);
data_str = strstr(str_buf, data_label);
if (!data_str)
continue;
strncpy(data, &data_str[strlen(data_label)], 8);
data[8] = '\0';
data_uint = strtoul(data, &ptr, 16);
// printf("Time: %016llX Data: %08X\n", timestamp_uint, data_uint);
data_capture_array[sample_count].timestamp = timestamp_uint;
data_capture_array[sample_count].data = data_uint;
sample_count++;
if (sample_count >= max_sz)
{
LOGWARN("File exceeds maximum buffer size. Some data may be missing.");
LOGWARN("Try re-running the tool with a larger buffer size");
break;
}
}
fclose(fp);
return sample_count;
}
//---------------------------------------------------------------------------
//
// Generate JSON Output File
//
//---------------------------------------------------------------------------
void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, DWORD capture_count)
{
LOGTRACE("Creating JSON file (%s)", filename);
ofstream json_ofstream;
json_ofstream.open(filename, ios::out);
json_ofstream << "[" << endl;
DWORD i = 0;
while (i < capture_count)
{
json_ofstream << fmt::format("{{\"id\": \"{0:d}\", \"timestamp\":\"{1:#016x}\", \"data\":\"{2:#08x}\"}}", i, data_capture_array[i].timestamp, data_capture_array[i].data);
if (i != (capture_count - 1))
{
json_ofstream << ",";
}
json_ofstream << endl;
i++;
}
json_ofstream << "]" << endl;
json_ofstream.close();
}

View File

@ -0,0 +1,17 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "data_sample.h"
DWORD scsimon_read_json(const char *json_filename, data_capture *data_capture_array, DWORD max_sz);
void scsimon_generate_html(const char *filename, const data_capture *data_capture_array, DWORD capture_count);
void scsimon_generate_json(const char *filename, const data_capture *data_capture_array, DWORD capture_count);
void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, DWORD capture_count);

View File

@ -0,0 +1,187 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
//
// Copyright (C) 2020-2021 akuker
//
// [ SCSI Bus Monitor ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "log.h"
#include "spdlog/spdlog.h"
#include <sstream>
#include <iostream>
#include <fstream>
#include "data_sample.h"
#include "sm_reports.h"
#include "gpiobus.h"
using namespace std;
//---------------------------------------------------------------------------
//
// Constant declarations
//
//---------------------------------------------------------------------------
// Symbol definition for the VCD file
// These are just arbitrary symbols. They can be anything allowed by the VCD file format,
// as long as they're consistently used.
const char SYMBOL_PIN_DAT = '#';
const char SYMBOL_PIN_ATN = '+';
const char SYMBOL_PIN_RST = '$';
const char SYMBOL_PIN_ACK = '%';
const char SYMBOL_PIN_REQ = '^';
const char SYMBOL_PIN_MSG = '&';
const char SYMBOL_PIN_CD = '*';
const char SYMBOL_PIN_IO = '(';
const char SYMBOL_PIN_BSY = ')';
const char SYMBOL_PIN_SEL = '-';
const char SYMBOL_PIN_PHASE = '=';
// We'll use position 0 in the prev_value array to store the previous phase
#define PIN_PHASE 0
//---------------------------------------------------------------------------
//
// Variable declarations
//
//---------------------------------------------------------------------------
static BYTE prev_value[32] = {0xFF};
extern double ns_per_loop;
static BOOL get_pin_value(DWORD data, int pin)
{
return (data >> pin) & 1;
}
static BYTE get_data_field(DWORD data)
{
DWORD data_out =
((data >> (PIN_DT0 - 0)) & (1 << 7)) |
((data >> (PIN_DT1 - 1)) & (1 << 6)) |
((data >> (PIN_DT2 - 2)) & (1 << 5)) |
((data >> (PIN_DT3 - 3)) & (1 << 4)) |
((data >> (PIN_DT4 - 4)) & (1 << 3)) |
((data >> (PIN_DT5 - 5)) & (1 << 2)) |
((data >> (PIN_DT6 - 6)) & (1 << 1)) |
((data >> (PIN_DT7 - 7)) & (1 << 0));
return (BYTE)data_out;
}
static void vcd_output_if_changed_phase(ofstream& fp, DWORD data, int pin, char symbol)
{
BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fp << "s" << GPIOBUS::GetPhaseStrRaw(new_value) << " " << symbol << endl;
}
}
static void vcd_output_if_changed_bool(ofstream& fp, DWORD data, int pin, char symbol)
{
BOOL new_value = get_pin_value(data, pin);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fp << new_value << symbol << endl;
}
}
static void vcd_output_if_changed_byte(ofstream& fp, DWORD data, int pin, char symbol)
{
BYTE new_value = get_data_field(data);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fp << "b"
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT7))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT6))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT5))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT4))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT3))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT2))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT1))
<< fmt::format("{0:b}", get_pin_value(data, PIN_DT0))
<< " " << symbol << endl;
}
}
void scsimon_generate_value_change_dump(const char *filename, const data_capture *data_capture_array, DWORD capture_count)
{
LOGTRACE("Creating Value Change Dump file (%s)", filename);
ofstream vcd_ofstream;
vcd_ofstream.open(filename, ios::out);
// Get the current time
time_t rawtime;
time(&rawtime);
struct tm *timeinfo = localtime(&rawtime);
char timestamp[256];
strftime(timestamp, sizeof(timestamp), "%d-%m-%Y %H-%M-%S", timeinfo);
vcd_ofstream
<< "$date" << endl
<< timestamp << endl
<< "$end" << endl
<< "$version" << endl
<< " VCD generator tool version info text." << endl
<< "$end" << endl
<< "$comment" << endl
<< " Tool build date:" << __TIMESTAMP__ << endl
<< "$end" << endl
<< "$timescale 1 ns $end" << endl
<< "$scope module logic $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_BSY << " BSY $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_SEL << " SEL $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_CD << " CD $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_IO << " IO $end"<< endl
<< "$var wire 1 " << SYMBOL_PIN_MSG << " MSG $end"<< endl
<< "$var wire 1 " << SYMBOL_PIN_REQ << " REQ $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_ACK << " ACK $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_ATN << " ATN $end" << endl
<< "$var wire 1 " << SYMBOL_PIN_RST << " RST $end" << endl
<< "$var wire 8 " << SYMBOL_PIN_DAT << " data $end" << endl
<< "$var string 1 " << SYMBOL_PIN_PHASE << " phase $end" << endl
<< "$upscope $end" << endl
<< "$enddefinitions $end" << endl;
// Initial values - default to zeros
vcd_ofstream
<< "$dumpvars" << endl
<< "0" << SYMBOL_PIN_BSY << endl
<< "0" << SYMBOL_PIN_SEL << endl
<< "0" << SYMBOL_PIN_CD << endl
<< "0" << SYMBOL_PIN_IO << endl
<< "0" << SYMBOL_PIN_MSG << endl
<< "0" << SYMBOL_PIN_REQ << endl
<< "0" << SYMBOL_PIN_ACK << endl
<< "0" << SYMBOL_PIN_ATN << endl
<< "0" << SYMBOL_PIN_RST << endl
<< "b00000000 " << SYMBOL_PIN_DAT << endl
<< "$end" << endl;
DWORD i = 0;
while (i < capture_count)
{
vcd_ofstream << "#" << (uint64_t)(data_capture_array[i].timestamp * ns_per_loop) << endl;
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_CD, SYMBOL_PIN_CD);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_IO, SYMBOL_PIN_IO);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_MSG, SYMBOL_PIN_MSG);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_REQ, SYMBOL_PIN_REQ);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ACK, SYMBOL_PIN_ACK);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_ATN, SYMBOL_PIN_ATN);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_RST, SYMBOL_PIN_RST);
vcd_output_if_changed_byte(vcd_ofstream, data_capture_array[i].data, PIN_DT0, SYMBOL_PIN_DAT);
vcd_output_if_changed_phase(vcd_ofstream, data_capture_array[i].data, PIN_PHASE, SYMBOL_PIN_PHASE);
i++;
}
vcd_ofstream.close();
}

View File

@ -58,7 +58,9 @@
#include <pwd.h> #include <pwd.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#if defined(__linux__)
#include <sys/epoll.h> #include <sys/epoll.h>
#endif
#include <netinet/in.h> #include <netinet/in.h>
#if defined(__linux__) #if defined(__linux__)

View File

@ -12,9 +12,9 @@
#include <cstdio> #include <cstdio>
// The following should be updated for each release // The following should be updated for each release
const int rascsi_major_version = 21; // Last two digits of year const int rascsi_major_version = 22; // Last two digits of year
const int rascsi_minor_version = 12; // Month const int rascsi_minor_version = 01; // Month
const int rascsi_patch_version = -1; // Patch number - increment for each update const int rascsi_patch_version = 01; // Patch number - increment for each update
static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + null character + "development build" static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + null character + "development build"

View File

@ -10,6 +10,7 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#pragma once #pragma once
#include "os.h"
//=========================================================================== //===========================================================================
// //

View File

@ -9,8 +9,6 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "os.h" #include "os.h"
#include "filepath.h"
#include "fileio.h"
#include "log.h" #include "log.h"
#include "gpiobus.h" #include "gpiobus.h"
#include "rascsi_version.h" #include "rascsi_version.h"
@ -19,7 +17,11 @@
#include <climits> #include <climits>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <getopt.h>
#include "rascsi.h" #include "rascsi.h"
#include <sched.h>
#include "monitor/sm_reports.h"
#include "monitor/data_sample.h"
using namespace std; using namespace std;
@ -28,48 +30,47 @@ using namespace std;
// Constant declarations // Constant declarations
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#define MAX_BUFF_SIZE 1000000
// Symbol definition for the VCD file // Symbol definition for the VCD file
// These are just arbitrary symbols. They can be anything allowed by the VCD file format, // These are just arbitrary symbols. They can be anything allowed by the VCD file format,
// as long as they're consistently used. // as long as they're consistently used.
#define SYMBOL_PIN_DAT '#' #define SYMBOL_PIN_DAT '#'
#define SYMBOL_PIN_ATN '+' #define SYMBOL_PIN_ATN '+'
#define SYMBOL_PIN_RST '$' #define SYMBOL_PIN_RST '$'
#define SYMBOL_PIN_ACK '%' #define SYMBOL_PIN_ACK '%'
#define SYMBOL_PIN_REQ '^' #define SYMBOL_PIN_REQ '^'
#define SYMBOL_PIN_MSG '&' #define SYMBOL_PIN_MSG '&'
#define SYMBOL_PIN_CD '*' #define SYMBOL_PIN_CD '*'
#define SYMBOL_PIN_IO '(' #define SYMBOL_PIN_IO '('
#define SYMBOL_PIN_BSY ')' #define SYMBOL_PIN_BSY ')'
#define SYMBOL_PIN_SEL '-' #define SYMBOL_PIN_SEL '-'
#define SYMBOL_PIN_PHASE '=' #define SYMBOL_PIN_PHASE '='
// We'll use position 0 in the prev_value array to store the previous phase
#define PIN_PHASE 0
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Variable declarations // Variable declarations
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static BYTE prev_value[32] = {0xFF}; static volatile bool running; // Running flag
static volatile bool running; // Running flag GPIOBUS *bus; // GPIO Bus
GPIOBUS *bus; // GPIO Bus
typedef struct data_capture{
DWORD data;
uint64_t timestamp;
} data_capture_t;
data_capture data_buffer[MAX_BUFF_SIZE]; DWORD buff_size = 1000000;
data_capture *data_buffer;
DWORD data_idx = 0; DWORD data_idx = 0;
double ns_per_loop; double ns_per_loop;
bool print_help = false;
bool import_data = false;
// We don't really need to support 256 character file names - this causes // We don't really need to support 256 character file names - this causes
// all kinds of compiler warnings when the log filename can be up to 256 // all kinds of compiler warnings when the log filename can be up to 256
// characters. _MAX_FNAME/2 is just an arbitrary value. // characters. _MAX_FNAME/2 is just an arbitrary value.
char log_file_name[_MAX_FNAME/2] = "log.vcd"; char file_base_name[_MAX_FNAME / 2] = "log";
char vcd_file_name[_MAX_FNAME];
char json_file_name[_MAX_FNAME];
char html_file_name[_MAX_FNAME];
char input_file_name[_MAX_FNAME];
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
@ -78,8 +79,84 @@ char log_file_name[_MAX_FNAME/2] = "log.vcd";
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void KillHandler(int sig) void KillHandler(int sig)
{ {
// Stop instruction // Stop instruction
running = false; running = false;
}
void parse_arguments(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "-Hhb:i:")) != -1)
{
switch (opt)
{
// The three options below are kind of a compound option with two letters
case 'h':
case 'H':
print_help = true;
break;
case 'b':
buff_size = atoi(optarg);
break;
case 'i':
strncpy(input_file_name, optarg, sizeof(input_file_name)-1);
import_data = true;
break;
case 1:
strncpy(file_base_name, optarg, sizeof(file_base_name) - 5);
break;
default:
cout << "default: " << optarg << endl;
break;
}
}
/* Process any remaining command line arguments (not options). */
if (optind < argc)
{
while (optind < argc)
strncpy(file_base_name, argv[optind++], sizeof(file_base_name)-1);
}
strcpy(vcd_file_name, file_base_name);
strcat(vcd_file_name, ".vcd");
strcpy(json_file_name, file_base_name);
strcat(json_file_name, ".json");
strcpy(html_file_name, file_base_name);
strcat(html_file_name, ".html");
}
//---------------------------------------------------------------------------
//
// Copyright text
//
//---------------------------------------------------------------------------
void print_copyright_text(int argc, char *argv[])
{
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) ");
LOGINFO("version %s (%s, %s)",
rascsi_get_version_string(),
__DATE__,
__TIME__);
LOGINFO("Powered by XM6 TypeG Technology ");
LOGINFO("Copyright (C) 2016-2020 GIMONS");
LOGINFO("Copyright (C) 2020-2021 Contributors to the RaSCSI project");
LOGINFO(" ");
}
//---------------------------------------------------------------------------
//
// Help text
//
//---------------------------------------------------------------------------
void print_help_text(int argc, char *argv[])
{
LOGINFO("%s -i [input file json] -b [buffer size] [output file]", argv[0]);
LOGINFO(" -i [input file json] - scsimon will parse the json file instead of capturing new data");
LOGINFO(" If -i option is not specified, scsimon will read the gpio pins");
LOGINFO(" -b [buffer size] - Override the default buffer size of %d.", buff_size);
LOGINFO(" [output file] - Base name of the output files. The file extension (ex: .json)");
LOGINFO(" will be appended to this file name");
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -87,30 +164,23 @@ void KillHandler(int sig)
// Banner Output // Banner Output
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void Banner(int argc, char* argv[]) void Banner(int argc, char *argv[])
{ {
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) "); if (import_data)
LOGINFO("version %s (%s, %s)\n", {
rascsi_get_version_string(), LOGINFO("Reading input file: %s", input_file_name);
__DATE__, }
__TIME__);
LOGINFO("Powered by XM6 TypeG Technology ");
LOGINFO("Copyright (C) 2016-2020 GIMONS");
LOGINFO("Copyright (C) 2020-2021 Contributors to the RaSCSI project");
LOGINFO("Connect type : %s", CONNECT_DESC);
LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", log_file_name);
if ((argc > 1 && strcmp(argv[1], "-h") == 0) ||
(argc > 1 && strcmp(argv[1], "--help") == 0)){
LOGINFO("Usage: %s [log filename]...", argv[0]);
exit(0);
}
else else
{ {
LOGINFO(" "); LOGINFO("Reading live data from the GPIO pins");
LOGINFO("Now collecting data.... Press CTRL-C to stop.") LOGINFO(" Connection type : %s", CONNECT_DESC);
LOGINFO(" ");
} }
LOGINFO(" Data buffer size: %u", buff_size);
LOGINFO(" ");
LOGINFO("Generating output files:");
LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", vcd_file_name);
LOGINFO(" %s - JSON file with raw data", json_file_name);
LOGINFO(" %s - HTML file with summary of commands", html_file_name);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -120,246 +190,101 @@ void Banner(int argc, char* argv[])
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool Init() bool Init()
{ {
// Interrupt handler settings // Interrupt handler settings
if (signal(SIGINT, KillHandler) == SIG_ERR) { if (signal(SIGINT, KillHandler) == SIG_ERR)
return FALSE; {
} return FALSE;
if (signal(SIGHUP, KillHandler) == SIG_ERR) { }
return FALSE; if (signal(SIGHUP, KillHandler) == SIG_ERR)
} {
if (signal(SIGTERM, KillHandler) == SIG_ERR) { return FALSE;
return FALSE; }
} if (signal(SIGTERM, KillHandler) == SIG_ERR)
{
return FALSE;
}
// GPIO Initialization // GPIO Initialization
bus = new GPIOBUS(); bus = new GPIOBUS();
if (!bus->Init()) { if (!bus->Init())
{
LOGERROR("Unable to intiailize the GPIO bus. Exiting...."); LOGERROR("Unable to intiailize the GPIO bus. Exiting....");
return false; return false;
}
// Bus Reset
bus->Reset();
// Other
running = false;
return true;
}
BOOL get_pin_value(DWORD data, int pin)
{
return (data >> pin) & 1;
}
BYTE get_data_field(DWORD data)
{
DWORD data_out =
((data >> (PIN_DT0 - 0)) & (1 << 7)) |
((data >> (PIN_DT1 - 1)) & (1 << 6)) |
((data >> (PIN_DT2 - 2)) & (1 << 5)) |
((data >> (PIN_DT3 - 3)) & (1 << 4)) |
((data >> (PIN_DT4 - 4)) & (1 << 3)) |
((data >> (PIN_DT5 - 5)) & (1 << 2)) |
((data >> (PIN_DT6 - 6)) & (1 << 1)) |
((data >> (PIN_DT7 - 7)) & (1 << 0));
return (BYTE)data_out;
}
void vcd_output_if_changed_phase(FILE *fp, DWORD data, int pin, char symbol)
{
BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "s%s %c\n", GPIOBUS::GetPhaseStrRaw(new_value), symbol);
} }
}
void vcd_output_if_changed_bool(FILE *fp, DWORD data, int pin, char symbol) // Bus Reset
{ bus->Reset();
BOOL new_value = get_pin_value(data,pin);
if (prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "%d%c\n", new_value, symbol);
}
}
void vcd_output_if_changed_byte(FILE *fp, DWORD data, int pin, char symbol) // Other
{ running = false;
BYTE new_value = get_data_field(data);
if(prev_value[pin] != new_value)
{
prev_value[pin] = new_value;
fprintf(fp, "b%d%d%d%d%d%d%d%d %c\n",
get_pin_value(data,PIN_DT7),
get_pin_value(data,PIN_DT6),
get_pin_value(data,PIN_DT5),
get_pin_value(data,PIN_DT4),
get_pin_value(data,PIN_DT3),
get_pin_value(data,PIN_DT2),
get_pin_value(data,PIN_DT1),
get_pin_value(data,PIN_DT0), symbol);
}
}
void create_value_change_dump() return true;
{
LOGINFO("Creating Value Change Dump file (%s)", log_file_name);
FILE *fp = fopen(log_file_name,"w");
// Get the current time
time_t rawtime;
time(&rawtime);
struct tm *timeinfo = localtime(&rawtime);
char timestamp[256];
strftime (timestamp,sizeof(timestamp),"%d-%m-%Y %H-%M-%S",timeinfo);
fprintf(fp, "$date\n");
fprintf(fp, "%s\n", timestamp);
fprintf(fp, "$end\n");
fprintf(fp, "$version\n");
fprintf(fp, " VCD generator tool version info text.\n");
fprintf(fp, "$end\n");
fprintf(fp, "$comment\n");
fprintf(fp, " Tool build date:%s\n", __TIMESTAMP__);
fprintf(fp, "$end\n");
fprintf(fp, "$timescale 1 ns $end\n");
fprintf(fp, "$scope module logic $end\n");
fprintf(fp, "$var wire 1 %c BSY $end\n", SYMBOL_PIN_BSY);
fprintf(fp, "$var wire 1 %c SEL $end\n", SYMBOL_PIN_SEL);
fprintf(fp, "$var wire 1 %c CD $end\n", SYMBOL_PIN_CD);
fprintf(fp, "$var wire 1 %c IO $end\n", SYMBOL_PIN_IO);
fprintf(fp, "$var wire 1 %c MSG $end\n", SYMBOL_PIN_MSG);
fprintf(fp, "$var wire 1 %c REQ $end\n", SYMBOL_PIN_REQ);
fprintf(fp, "$var wire 1 %c ACK $end\n", SYMBOL_PIN_ACK);
fprintf(fp, "$var wire 1 %c ATN $end\n", SYMBOL_PIN_ATN);
fprintf(fp, "$var wire 1 %c RST $end\n", SYMBOL_PIN_RST);
fprintf(fp, "$var wire 8 %c data $end\n", SYMBOL_PIN_DAT);
fprintf(fp, "$var string 1 %c phase $end\n", SYMBOL_PIN_PHASE);
fprintf(fp, "$upscope $end\n");
fprintf(fp, "$enddefinitions $end\n");
// Initial values - default to zeros
fprintf(fp, "$dumpvars\n");
fprintf(fp, "0%c\n", SYMBOL_PIN_BSY);
fprintf(fp, "0%c\n", SYMBOL_PIN_SEL);
fprintf(fp, "0%c\n", SYMBOL_PIN_CD);
fprintf(fp, "0%c\n", SYMBOL_PIN_IO);
fprintf(fp, "0%c\n", SYMBOL_PIN_MSG);
fprintf(fp, "0%c\n", SYMBOL_PIN_REQ);
fprintf(fp, "0%c\n", SYMBOL_PIN_ACK);
fprintf(fp, "0%c\n", SYMBOL_PIN_ATN);
fprintf(fp, "0%c\n", SYMBOL_PIN_RST);
fprintf(fp, "b00000000 %c\n", SYMBOL_PIN_DAT);
fprintf(fp, "$end\n");
DWORD i = 0;
while (i < data_idx)
{
ostringstream s;
s << (uint64_t)(data_buffer[i].timestamp*ns_per_loop);
fprintf(fp, "#%s\n",s.str().c_str());
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_CD, SYMBOL_PIN_CD);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_IO, SYMBOL_PIN_IO);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_MSG, SYMBOL_PIN_MSG);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_REQ, SYMBOL_PIN_REQ);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ACK, SYMBOL_PIN_ACK);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ATN, SYMBOL_PIN_ATN);
vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_RST, SYMBOL_PIN_RST);
vcd_output_if_changed_byte(fp, data_buffer[i].data, PIN_DT0, SYMBOL_PIN_DAT);
vcd_output_if_changed_phase(fp, data_buffer[i].data, PIN_PHASE, SYMBOL_PIN_PHASE);
i++;
}
fclose(fp);
} }
void Cleanup() void Cleanup()
{ {
LOGINFO("Stopping data collection...."); if (!import_data)
create_value_change_dump(); {
LOGINFO("Stopping data collection....");
}
LOGINFO(" ");
LOGINFO("Generating %s...", vcd_file_name);
scsimon_generate_value_change_dump(vcd_file_name, data_buffer, data_idx);
LOGINFO("Generating %s...", json_file_name);
scsimon_generate_json(json_file_name, data_buffer, data_idx);
LOGINFO("Generating %s...", html_file_name);
scsimon_generate_html(html_file_name, data_buffer, data_idx);
// Cleanup the Bus if (bus)
bus->Cleanup(); {
// Cleanup the Bus
delete bus; bus->Cleanup();
delete bus;
}
} }
void Reset() void Reset()
{ {
// Reset the bus // Reset the bus
bus->Reset(); bus->Reset();
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Pin the thread to a specific CPU // Pin the thread to a specific CPU (Only applies to Linux)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#ifdef __linux__
void FixCpu(int cpu) void FixCpu(int cpu)
{ {
// Get the number of CPUs // Get the number of CPUs
cpu_set_t cpuset; cpu_set_t cpuset;
CPU_ZERO(&cpuset); CPU_ZERO(&cpuset);
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset); sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
int cpus = CPU_COUNT(&cpuset); int cpus = CPU_COUNT(&cpuset);
// Set the thread affinity // Set the thread affinity
if (cpu < cpus) { if (cpu < cpus)
CPU_ZERO(&cpuset); {
CPU_SET(cpu, &cpuset); CPU_ZERO(&cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); CPU_SET(cpu, &cpuset);
} sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
} }
#endif
#ifdef DEBUG #ifdef DEBUG
static DWORD high_bits = 0x0; static DWORD high_bits = 0x0;
static DWORD low_bits = 0xFFFFFFFF; static DWORD low_bits = 0xFFFFFFFF;
#endif #endif
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Main processing // Main processing
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int main(int argc, char* argv[]) int main(int argc, char *argv[])
{ {
ostringstream s;
#ifdef DEBUG
DWORD prev_high = high_bits;
DWORD prev_low = low_bits;
#endif
DWORD prev_sample = 0xFFFFFFFF;
DWORD this_sample = 0;
struct sched_param schparam;
timeval start_time;
timeval stop_time;
uint64_t loop_count = 0;
timeval time_diff;
uint64_t elapsed_us;
int str_len;
// If there is an argument specified and it is NOT -h or --help
if((argc > 1) && (strcmp(argv[1], "-h")) && (strcmp(argv[1], "--help"))){
str_len = strlen(argv[1]);
if ((str_len >= 1) && (str_len < _MAX_FNAME))
{
strncpy(log_file_name, argv[1], sizeof(log_file_name));
// Append .vcd if its not already there
if((str_len < 4) || strcasecmp(log_file_name + (str_len - 4), ".vcd")) {
strcat(log_file_name, ".vcd");
}
}
else
{
printf("Invalid log name specified. Using log.vcd");
}
}
#ifdef DEBUG #ifdef DEBUG
spdlog::set_level(spdlog::level::trace); spdlog::set_level(spdlog::level::trace);
@ -367,53 +292,98 @@ int main(int argc, char* argv[])
spdlog::set_level(spdlog::level::info); spdlog::set_level(spdlog::level::info);
#endif #endif
spdlog::set_pattern("%^[%l]%$ %v"); spdlog::set_pattern("%^[%l]%$ %v");
// Output the Banner
Banner(argc, argv);
memset(data_buffer,0,sizeof(data_buffer));
// Initialize print_copyright_text(argc, argv);
int ret = 0; parse_arguments(argc, argv);
if (!Init()) {
ret = EPERM;
goto init_exit;
}
// Reset #ifdef DEBUG
Reset(); DWORD prev_high = high_bits;
DWORD prev_low = low_bits;
#endif
ostringstream s;
DWORD prev_sample = 0xFFFFFFFF;
DWORD this_sample = 0;
timeval start_time;
timeval stop_time;
uint64_t loop_count = 0;
timeval time_diff;
uint64_t elapsed_us;
if (print_help)
{
print_help_text(argc, argv);
exit(0);
}
// Output the Banner
Banner(argc, argv);
data_buffer = (data_capture *)malloc(sizeof(data_capture_t) * buff_size);
bzero(data_buffer, sizeof(data_capture_t) * buff_size);
if (import_data)
{
data_idx = scsimon_read_json(input_file_name, data_buffer, buff_size);
if (data_idx > 0)
{
LOGDEBUG("Read %d samples from %s", data_idx, input_file_name);
Cleanup();
}
exit(0);
}
LOGINFO(" ");
LOGINFO("Now collecting data.... Press CTRL-C to stop.")
LOGINFO(" ");
// Initialize
int ret = 0;
if (!Init())
{
ret = EPERM;
goto init_exit;
}
// Reset
Reset();
#ifdef __linux__
// Set the affinity to a specific processor core // Set the affinity to a specific processor core
FixCpu(3); FixCpu(3);
// Scheduling policy setting (highest priority) // Scheduling policy setting (highest priority)
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); struct sched_param schparam;
sched_setscheduler(0, SCHED_FIFO, &schparam); schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
#endif
// Start execution // Start execution
running = true; running = true;
bus->SetACT(FALSE); bus->SetACT(FALSE);
(void)gettimeofday(&start_time, NULL); (void)gettimeofday(&start_time, NULL);
LOGDEBUG("ALL_SCSI_PINS %08X\n",ALL_SCSI_PINS); LOGDEBUG("ALL_SCSI_PINS %08X\n", ALL_SCSI_PINS);
// Main Loop // Main Loop
while (running) { while (running)
// Work initialization {
this_sample = (bus->Aquire() & ALL_SCSI_PINS); // Work initialization
this_sample = (bus->Aquire() & ALL_SCSI_PINS);
loop_count++; loop_count++;
if (loop_count > LLONG_MAX -1) if (loop_count > LLONG_MAX - 1)
{ {
LOGINFO("Maximum amount of time has elapsed. SCSIMON is terminating."); LOGINFO("Maximum amount of time has elapsed. SCSIMON is terminating.");
running=false; running = false;
} }
if (data_idx >= (MAX_BUFF_SIZE-2)) if (data_idx >= (buff_size - 2))
{ {
LOGINFO("Internal data buffer is full. SCSIMON is terminating."); LOGINFO("Internal data buffer is full. SCSIMON is terminating.");
running=false; running = false;
} }
if (this_sample != prev_sample) if (this_sample != prev_sample)
{ {
#ifdef DEBUG #ifdef DEBUG
// This is intended to be a debug check to see if every pin is set // This is intended to be a debug check to see if every pin is set
@ -422,27 +392,28 @@ int main(int argc, char* argv[])
low_bits &= this_sample; low_bits &= this_sample;
if ((high_bits != prev_high) || (low_bits != prev_low)) if ((high_bits != prev_high) || (low_bits != prev_low))
{ {
LOGDEBUG(" %08X %08X\n",high_bits, low_bits); LOGDEBUG(" %08X %08X\n", high_bits, low_bits);
} }
prev_high = high_bits; prev_high = high_bits;
prev_low = low_bits; prev_low = low_bits;
if((data_idx % 1000) == 0){ if ((data_idx % 1000) == 0)
s.str(""); {
s << "Collected " << data_idx << " samples..."; s.str("");
LOGDEBUG("%s", s.str().c_str()); s << "Collected " << data_idx << " samples...";
LOGDEBUG("%s", s.str().c_str());
} }
#endif #endif
data_buffer[data_idx].data = this_sample; data_buffer[data_idx].data = this_sample;
data_buffer[data_idx].timestamp = loop_count; data_buffer[data_idx].timestamp = loop_count;
data_idx++; data_idx++;
prev_sample = this_sample; prev_sample = this_sample;
} }
continue; continue;
} }
// Collect one last sample, otherwise it looks like the end of the data was cut off // Collect one last sample, otherwise it looks like the end of the data was cut off
if (data_idx < MAX_BUFF_SIZE) if (data_idx < buff_size)
{ {
data_buffer[data_idx].data = this_sample; data_buffer[data_idx].data = this_sample;
data_buffer[data_idx].timestamp = loop_count; data_buffer[data_idx].timestamp = loop_count;
@ -453,7 +424,7 @@ int main(int argc, char* argv[])
timersub(&stop_time, &start_time, &time_diff); timersub(&stop_time, &start_time, &time_diff);
elapsed_us = ((time_diff.tv_sec*1000000) + time_diff.tv_usec); elapsed_us = ((time_diff.tv_sec * 1000000) + time_diff.tv_usec);
s.str(""); s.str("");
s << "Elapsed time: " << elapsed_us << " microseconds (" << elapsed_us / 1000000 << " seconds)"; s << "Elapsed time: " << elapsed_us << " microseconds (" << elapsed_us / 1000000 << " seconds)";
LOGINFO("%s", s.str().c_str()); LOGINFO("%s", s.str().c_str());
@ -461,14 +432,14 @@ int main(int argc, char* argv[])
s << "Collected " << data_idx << " changes"; s << "Collected " << data_idx << " changes";
LOGINFO("%s", s.str().c_str()); LOGINFO("%s", s.str().c_str());
// Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps. // Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps.
ns_per_loop = (elapsed_us * 1000) / (double)loop_count; ns_per_loop = (elapsed_us * 1000) / (double)loop_count;
s.str(""); s.str("");
s << "Read the SCSI bus " << loop_count << " times with an average of " << ns_per_loop << " ns for each read"; s << "Read the SCSI bus " << loop_count << " times with an average of " << ns_per_loop << " ns for each read";
LOGINFO("%s", s.str().c_str()); LOGINFO("%s", s.str().c_str());
Cleanup(); Cleanup();
init_exit: init_exit:
exit(ret); exit(ret);
} }

View File

@ -24,9 +24,12 @@ A separate mocking solution will be needed for this interface.
It is recommended to run pylint against new code to protect against bugs It is recommended to run pylint against new code to protect against bugs
and keep the code readable and maintainable. and keep the code readable and maintainable.
The local pylint configuration lives in .pylintrc The local pylint configuration lives in .pylintrc
In order for pylint to recognize venv libraries, the pylint-venv package is required.
``` ```
sudo apt install pylint3 sudo apt install pylint3
sudo pip install pylint-venv
source venv/bin/activate
pylint3 python_source_file.py pylint3 python_source_file.py
``` ```
@ -53,25 +56,59 @@ We use the Flask-Babel library and Flask/Jinja2 extension for i18n.
It uses the 'pybabel' command line tool for extracting and compiling localizations. It uses the 'pybabel' command line tool for extracting and compiling localizations.
Activate the Python venv in src/web/ to use it: Activate the Python venv in src/web/ to use it:
```
$ cd src/web/ $ cd src/web/
$ source venv/bin/activate $ source venv/bin/activate
$ pybabel --help $ pybabel --help
```
To create a new localization, it needs to be added to accept_languages in To create a new localization, it needs to be added to the LANGAUGES constant in
the get_locale() method, and also to localizer.cpp in the RaSCSI C++ code. web/settings.py. To localize messages coming from the RaSCSI backend, update also code in
raspberrypi/localizer.cpp in the RaSCSI C++ code.
Once this is done, follow the steps in the [Flask-Babel documentation](https://flask-babel.tkte.ch/#translating-applications) Once this is done, follow the steps in the [Flask-Babel documentation](https://flask-babel.tkte.ch/#translating-applications)
to generate the messages.po for the new language. to generate the messages.po for the new language.
Updating an existing messages.po is also covered above. Updating the strings in an existing messages.po is also covered above.
When you are ready to contribute new or updated localizations, use the same Gitflow Workflow as used for any code contributions to submit PRs against the develop branch. When you are ready to contribute new or updated localizations, use the same Gitflow Workflow as used for any code contributions to submit PRs against the develop branch.
### Working with PO files
See the [GNU gettext documentation](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) for an introduction to the PO file format.
We make heavy use of __python-format__ for formatting, for instance:
```
#: file_cmds.py:353
#, python-format
msgid "%(file_name)s downloaded to %(save_dir)s"
msgstr "Laddade ner %(file_name)s till %(save_dir)s"
```
There are also a few instances of formatting in JavaScript:
```
#: templates/index.html:381
msgid "Server responded with code: {{statusCode}}"
msgstr "Servern svarade med kod: {{statusCode}}"
```
And with html tags:
```
#: templates/index.html:304
#, python-format
msgid ""
"Emulates a SCSI DaynaPORT Ethernet Adapter. <a href=\"%(url)s\">Host "
"drivers and configuration required</a>."
msgstr ""
"Emulerar en SCSI DaynaPORT ethernet-adapter. <a href=\"%(url)s\">Kräver "
"drivrutiner och inställningar</a>."
```
### (Optional) See translation stats for a localization ### (Optional) See translation stats for a localization
Install the gettext package and use msgfmt to see the translation progress. Install the gettext package and use msgfmt to see the translation progress.
```
$ sudo apt install gettext $ sudo apt install gettext
$ cd src/web/ $ cd src/web/
$ msgfmt --statistics translations/sv/LC_MESSAGES/messages.po $ msgfmt --statistics translations/sv/LC_MESSAGES/messages.po
215 translated messages, 1 untranslated message. 215 translated messages, 1 untranslated message.
```

View File

@ -418,5 +418,17 @@
"file_type": null, "file_type": null,
"description": "Boots DECstations and VAXstations. Use only with workstations of this vintage.", "description": "Boots DECstations and VAXstations. Use only with workstations of this vintage.",
"url": "" "url": ""
},
{
"device_type": "SCCD",
"vendor": "MATSHITA",
"product": "CD-ROM CR-8005 ",
"revision": "1.0k",
"block_size": 2048,
"size": null,
"name": "Apple CD 600e",
"file_type": null,
"description": "Emulates Apple CD ROM drive for use with Macintosh computers.",
"url": ""
} }
] ]

View File

@ -66,7 +66,8 @@ def list_images():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -123,7 +124,8 @@ def create_new_image(file_name, file_type, size):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.CREATE_IMAGE command.operation = proto.PbOperation.CREATE_IMAGE
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
command.params["file"] = file_name + "." + file_type command.params["file"] = file_name + "." + file_type
command.params["size"] = str(size) command.params["size"] = str(size)
@ -144,7 +146,8 @@ def delete_image(file_name):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DELETE_IMAGE command.operation = proto.PbOperation.DELETE_IMAGE
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
command.params["file"] = file_name command.params["file"] = file_name
@ -163,7 +166,8 @@ def rename_image(file_name, new_file_name):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.RENAME_IMAGE command.operation = proto.PbOperation.RENAME_IMAGE
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
command.params["from"] = file_name command.params["from"] = file_name
command.params["to"] = new_file_name command.params["to"] = new_file_name
@ -275,7 +279,7 @@ def download_file_to_iso(url, *iso_args):
tmp_full_path = tmp_dir + file_name tmp_full_path = tmp_dir + file_name
iso_filename = f"{server_info['image_dir']}/{file_name}.iso" iso_filename = f"{server_info['image_dir']}/{file_name}.iso"
req_proc = download_to_dir(url, tmp_dir) req_proc = download_to_dir(url, tmp_dir, file_name)
if not req_proc["status"]: if not req_proc["status"]:
return {"status": False, "msg": req_proc["msg"]} return {"status": False, "msg": req_proc["msg"]}
@ -300,18 +304,16 @@ def download_file_to_iso(url, *iso_args):
delete_file(tmp_full_path) delete_file(tmp_full_path)
try: try:
iso_proc = ( run(
run( [
[ "genisoimage",
"genisoimage", *iso_args,
*iso_args, "-o",
"-o", iso_filename,
iso_filename, tmp_dir,
tmp_dir, ],
], capture_output=True,
capture_output=True, check=True,
check=True,
)
) )
except CalledProcessError as error: except CalledProcessError as error:
logging.warning("Executed shell command: %s", " ".join(error.cmd)) logging.warning("Executed shell command: %s", " ".join(error.cmd))
@ -320,18 +322,20 @@ def download_file_to_iso(url, *iso_args):
return { return {
"status": True, "status": True,
"msg": _(u"Created CD-ROM ISO image with arguments \"%(value)s\"", value=" ".join(iso_args)), "msg": _(
u"Created CD-ROM ISO image with arguments \"%(value)s\"",
value=" ".join(iso_args),
),
"file_name": iso_filename, "file_name": iso_filename,
} }
def download_to_dir(url, save_dir): def download_to_dir(url, save_dir, file_name):
""" """
Takes (str) url, (str) save_dir Takes (str) url, (str) save_dir, (str) file_name
Returns (dict) with (bool) status and (str) msg Returns (dict) with (bool) status and (str) msg
""" """
import requests import requests
file_name = PurePath(url).name
logging.info("Making a request to download %s", url) logging.info("Making a request to download %s", url)
try: try:
@ -393,7 +397,10 @@ def write_config(file_name):
json_file, json_file,
indent=4 indent=4
) )
return {"status": True, "msg": _(u"Saved configuration file to %(file_name)s", file_name=file_name)} return {
"status": True,
"msg": _(u"Saved configuration file to %(file_name)s", file_name=file_name),
}
except (IOError, ValueError, EOFError, TypeError) as error: except (IOError, ValueError, EOFError, TypeError) as error:
logging.error(str(error)) logging.error(str(error))
delete_file(file_name) delete_file(file_name)

View File

@ -2,11 +2,11 @@
Module for commands sent to the RaSCSI backend service. Module for commands sent to the RaSCSI backend service.
""" """
from settings import REMOVABLE_DEVICE_TYPES
from socket_cmds import send_pb_command
from flask import current_app, session from flask import current_app, session
from flask_babel import _ from flask_babel import _
import rascsi_interface_pb2 as proto import rascsi_interface_pb2 as proto
from settings import REMOVABLE_DEVICE_TYPES
from socket_cmds import send_pb_command
def get_server_info(): def get_server_info():
@ -25,7 +25,8 @@ def get_server_info():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.SERVER_INFO command.operation = proto.PbOperation.SERVER_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -84,7 +85,8 @@ def get_reserved_ids():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.RESERVED_IDS_INFO command.operation = proto.PbOperation.RESERVED_IDS_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -106,7 +108,8 @@ def get_network_info():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO command.operation = proto.PbOperation.NETWORK_INTERFACES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -125,7 +128,8 @@ def get_device_types():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICE_TYPES_INFO command.operation = proto.PbOperation.DEVICE_TYPES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -148,7 +152,8 @@ def get_image_files_info():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -177,7 +182,8 @@ def attach_image(scsi_id, **kwargs):
""" """
command = proto.PbCommand() command = proto.PbCommand()
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
devices = proto.PbDeviceDefinition() devices = proto.PbDeviceDefinition()
devices.id = int(scsi_id) devices.id = int(scsi_id)
@ -253,7 +259,8 @@ def detach_by_id(scsi_id, unit=None):
command.operation = proto.PbOperation.DETACH command.operation = proto.PbOperation.DETACH
command.devices.append(devices) command.devices.append(devices)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -269,7 +276,8 @@ def detach_all():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DETACH_ALL command.operation = proto.PbOperation.DETACH_ALL
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -292,7 +300,8 @@ def eject_by_id(scsi_id, unit=None):
command.operation = proto.PbOperation.EJECT command.operation = proto.PbOperation.EJECT
command.devices.append(devices) command.devices.append(devices)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -312,7 +321,8 @@ def list_devices(scsi_id=None, unit=None):
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.DEVICES_INFO command.operation = proto.PbOperation.DEVICES_INFO
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
# If method is called with scsi_id parameter, return the info on those devices # If method is called with scsi_id parameter, return the info on those devices
# Otherwise, return the info on all attached devices # Otherwise, return the info on all attached devices
@ -390,7 +400,8 @@ def reserve_scsi_ids(reserved_scsi_ids):
command.operation = proto.PbOperation.RESERVE_IDS command.operation = proto.PbOperation.RESERVE_IDS
command.params["ids"] = ",".join(reserved_scsi_ids) command.params["ids"] = ",".join(reserved_scsi_ids)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -408,7 +419,8 @@ def set_log_level(log_level):
command.operation = proto.PbOperation.LOG_LEVEL command.operation = proto.PbOperation.LOG_LEVEL
command.params["level"] = str(log_level) command.params["level"] = str(log_level)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -426,7 +438,8 @@ def shutdown_pi(mode):
command.operation = proto.PbOperation.SHUT_DOWN command.operation = proto.PbOperation.SHUT_DOWN
command.params["mode"] = str(mode) command.params["mode"] = str(mode)
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()
@ -443,7 +456,8 @@ def is_token_auth():
command = proto.PbCommand() command = proto.PbCommand()
command.operation = proto.PbOperation.CHECK_AUTHENTICATION command.operation = proto.PbOperation.CHECK_AUTHENTICATION
command.params["token"] = current_app.config["TOKEN"] command.params["token"] = current_app.config["TOKEN"]
command.params["locale"] = session["language"] if "language" in session.keys():
command.params["locale"] = session["language"]
data = send_pb_command(command.SerializeToString()) data = send_pb_command(command.SerializeToString())
result = proto.PbResult() result = proto.PbResult()

View File

@ -32,4 +32,4 @@ RESERVATIONS = ["" for x in range(0, 8)]
AUTH_GROUP = "rascsi" AUTH_GROUP = "rascsi"
# The language locales supported by RaSCSI # The language locales supported by RaSCSI
LANGUAGES = ["en", "de", "sv"] LANGUAGES = ["en", "de", "sv", "fr", "es"]

View File

@ -3,9 +3,9 @@ Module for sending and receiving data over a socket connection with the RaSCSI b
""" """
import logging import logging
from time import sleep
from flask import abort from flask import abort
from flask_babel import _ from flask_babel import _
from time import sleep
def send_pb_command(payload): def send_pb_command(payload):
""" """

View File

@ -34,9 +34,13 @@ fi
# Test for two known broken venv states # Test for two known broken venv states
if test -e venv; then if test -e venv; then
GOOD_VENV=true GOOD_VENV=true
! test -e venv/bin/activate && GOOD_VENV=false if ! test -e venv/bin/activate; then
pip3 list 1> /dev/null GOOD_VENV=false
test $? -eq 1 && GOOD_VENV=false else
source venv/bin/activate
pip3 list 1> /dev/null
test $? -eq 1 && GOOD_VENV=false
fi
if ! "$GOOD_VENV"; then if ! "$GOOD_VENV"; then
echo "Deleting bad python venv" echo "Deleting bad python venv"
sudo rm -rf venv sudo rm -rf venv

File diff suppressed because it is too large Load Diff

View File

@ -372,7 +372,25 @@
forceChunking: true, forceChunking: true,
url: '/files/upload', url: '/files/upload',
maxFilesize: {{ max_file_size }}, // MB maxFilesize: {{ max_file_size }}, // MB
chunkSize: 1000000 // bytes chunkSize: 1000000, // bytes
dictDefaultMessage: "{{ _("Drop files here to upload") }}",
dictFallbackMessage: "{{ _("Your browser does not support drag'n'drop file uploads.") }}",
dictFallbackText: "{{ _("Please use the fallback form below to upload your files like in the olden days.") }}",
dictFileTooBig: "{{ _("File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB.") }}",
dictInvalidFileType: "{{ _("You can't upload files of this type.") }}",
dictResponseError: "{{ _("Server responded with code: {{statusCode}}") }}",
dictCancelUpload:" {{ _("Cancel upload") }}",
dictUploadCanceled: "{{ _("Upload canceled.") }}",
dictCancelUploadConfirmation: "{{ _("Are you sure you want to cancel this upload?") }}",
dictRemoveFile: "{{ _("Remove file") }}",
dictMaxFilesExceeded: "{{ _("You can not upload any more files.") }}",
dictFileSizeUnits: {
tb: "{{ _("TB") }}",
gb: "{{ _("GB") }}",
mb: "{{ _("MB") }}",
kb: "{{ _("KB") }}",
b: "{{ _("b") }}"
}
} }
</script> </script>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: RaSCSI 68kmla Edition\n" "Project-Id-Version: RaSCSI 68kmla Edition\n"
"Report-Msgid-Bugs-To: https://github.com/akuker/RASCSI/issues\n" "Report-Msgid-Bugs-To: https://github.com/akuker/RASCSI/issues\n"
"POT-Creation-Date: 2021-12-26 21:59-0800\n" "POT-Creation-Date: 2021-12-28 14:39-0800\n"
"PO-Revision-Date: 2021-12-24 16:16-0800\n" "PO-Revision-Date: 2021-12-24 16:16-0800\n"
"Last-Translator: Daniel Markstedt <markstedt@gmail.com>\n" "Last-Translator: Daniel Markstedt <markstedt@gmail.com>\n"
"Language: sv\n" "Language: sv\n"
@ -41,53 +41,53 @@ msgstr "Kunde ej flytta filen till %(target_path)s"
#: file_cmds.py:323 #: file_cmds.py:323
#, python-format #, python-format
msgid "Created CD-ROM ISO image with arguments \"%(value)s\"" msgid "Created CD-ROM ISO image with arguments \"%(value)s\""
msgstr "Skapade en CD-ROM ISO-fil med argumenten \"%(value)s\"" msgstr "Skapade en cd-rom ISO-fil med argumentet \"%(value)s\""
#: file_cmds.py:353 #: file_cmds.py:354
#, python-format #, python-format
msgid "%(file_name)s downloaded to %(save_dir)s" msgid "%(file_name)s downloaded to %(save_dir)s"
msgstr "Laddade ner %(file_name)s till %(save_dir)s" msgstr "Laddade ner %(file_name)s till %(save_dir)s"
#: file_cmds.py:396 #: file_cmds.py:399
#, python-format #, python-format
msgid "Saved configuration file to %(file_name)s" msgid "Saved configuration file to %(file_name)s"
msgstr "Sparade konfigurationsfilen som %(file_name)s" msgstr "Sparade konfigurationsfilen som %(file_name)s"
#: file_cmds.py:406 #: file_cmds.py:410
#, python-format #, python-format
msgid "Could not write to file: %(file_name)s" msgid "Could not write to file: %(file_name)s"
msgstr "Kunde ej skriva till filen %(file_name)s" msgstr "Kunde ej skriva till filen %(file_name)s"
#: file_cmds.py:464 #: file_cmds.py:468
msgid "Invalid configuration file format" msgid "Invalid configuration file format"
msgstr "Ogiltigt konfigurationsfilformat" msgstr "Ogiltigt konfigurationsfilformat"
#: file_cmds.py:467 #: file_cmds.py:471
#, python-format #, python-format
msgid "Loaded configurations from: %(file_name)s" msgid "Loaded configurations from: %(file_name)s"
msgstr "Laddade konfigurationer från %(file_name)s" msgstr "Laddade konfigurationer från %(file_name)s"
#: file_cmds.py:476 #: file_cmds.py:480
#, python-format #, python-format
msgid "Could not read configuration file: %(file_name)s" msgid "Could not read configuration file: %(file_name)s"
msgstr "Kunde ej läsa konfigurationer från filen %(file_name)s" msgstr "Kunde ej läsa konfigurationer från filen %(file_name)s"
#: file_cmds.py:493 #: file_cmds.py:497
#, python-format #, python-format
msgid "Created properties file: %(file_path)s" msgid "Created properties file: %(file_path)s"
msgstr "Skapade egenskapsfilen %(file_path)s" msgstr "Skapade egenskapsfilen %(file_path)s"
#: file_cmds.py:504 #: file_cmds.py:508
#, python-format #, python-format
msgid "Could not write to properties file: %(file_path)s" msgid "Could not write to properties file: %(file_path)s"
msgstr "Kunde ej spara egenskaper till filen %(file_path)s" msgstr "Kunde ej spara egenskaper till filen %(file_path)s"
#: file_cmds.py:520 #: file_cmds.py:524
#, python-format #, python-format
msgid "Read properties from file: %(file_path)s" msgid "Read properties from file: %(file_path)s"
msgstr "Läste egenskaper från filen %(file_path)s" msgstr "Läste egenskaper från filen %(file_path)s"
#: file_cmds.py:530 #: file_cmds.py:534
#, python-format #, python-format
msgid "Could not read properties from file: %(file_path)s" msgid "Could not read properties from file: %(file_path)s"
msgstr "Kunde ej läsa egenskaper från filen %(file_path)s" msgstr "Kunde ej läsa egenskaper från filen %(file_path)s"
@ -113,7 +113,7 @@ msgid ""
" crashed." " crashed."
msgstr "" msgstr ""
"RaSCSIs webbgränssnitt kunde inte ansluta till RaSCSI på " "RaSCSIs webbgränssnitt kunde inte ansluta till RaSCSI på "
"%(host)s:%(port)s med felmeddelande %(error_msg)s. RaSCSI-processen är " "%(host)s:%(port)s med felmeddelandet %(error_msg)s. RaSCSI-processen är "
"antingen avslagen eller har krashat." "antingen avslagen eller har krashat."
#: socket_cmds.py:79 #: socket_cmds.py:79
@ -132,7 +132,7 @@ msgstr ""
"RaSCSIs webbgränssnitt fick en ogiltig respons från RaSCSI. Gå tillbaks " "RaSCSIs webbgränssnitt fick en ogiltig respons från RaSCSI. Gå tillbaks "
"och försök igen. Om samma fel upprepas så rapportera en bugg." "och försök igen. Om samma fel upprepas så rapportera en bugg."
#: web.py:97 #: web.py:119
msgid "" msgid ""
"RaSCSI is password protected. Start the Web Interface with the --password" "RaSCSI is password protected. Start the Web Interface with the --password"
" parameter." " parameter."
@ -140,71 +140,71 @@ msgstr ""
"RaSCSI är lösenordsskyddat. Start webbgränssnittet med parametern " "RaSCSI är lösenordsskyddat. Start webbgränssnittet med parametern "
"--password ." "--password ."
#: web.py:191 #: web.py:220
#, python-format #, python-format
msgid "Could not read drive properties from %(properties_file)s" msgid "Could not read drive properties from %(properties_file)s"
msgstr "Kunde ej läsa egenskaper från %(properties_file)s" msgstr "Kunde ej läsa egenskaper från %(properties_file)s"
#: web.py:255 #: web.py:290
#, python-format #, python-format
msgid "You must log in with credentials for a user in the '%(group)s' group" msgid "You must log in with credentials for a user in the '%(group)s' group"
msgstr "Du måste logga in som en användare som tillhör %(group)s-gruppen" msgstr "Du måste logga in som en användare som tillhör %(group)s-gruppen"
#: web.py:308 web.py:843 #: web.py:348 web.py:887
#, python-format #, python-format
msgid "Image file created: %(file_name)s" msgid "Image file created: %(file_name)s"
msgstr "Skapade skivbildfil: %(file_name)s" msgstr "Skapade skivbildsfil %(file_name)s"
#: web.py:433 #: web.py:473
msgid "An error occurred when fetching logs." msgid "An error occurred when fetching logs."
msgstr "Ett fel inträffade när vi skaffade loggar." msgstr "Ett fel inträffade när vi skaffade loggar."
#: web.py:448 #: web.py:488
#, python-format #, python-format
msgid "Log level set to %(value)s" msgid "Log level set to %(value)s"
msgstr "Ställde in loggnivån till %(value)s" msgstr "Bytte loggnivån till %(value)s"
#: web.py:467 #: web.py:507
#, python-format #, python-format
msgid "Please follow the instructions at %(url)s" msgid "Please follow the instructions at %(url)s"
msgstr "Följ instruktionerna på %(url)s" msgstr "Följ instruktionerna på %(url)s"
#: web.py:471 #: web.py:511
msgid "Configure IPv4 forwarding before using a wireless network device." msgid "Configure IPv4 forwarding before using a wireless network device."
msgstr "" msgstr ""
"Ställ in IPv4-vidarebefodring innan du använder en trådlös " "Ställ in IPv4-vidarebefodring innan du använder ett trådlöst "
"nätverksadapter." "nätverksgränssnitt."
#: web.py:475 #: web.py:515
msgid "Configure NAT before using a wireless network device." msgid "Configure NAT before using a wireless network device."
msgstr "Ställ in NAT innan du använder en trådlös nätverksadapter." msgstr "Ställ in NAT innan du använder ett trådlöst nätverksgränssnitt."
#: web.py:480 web.py:484 #: web.py:520 web.py:524
msgid "Configure the network bridge before using a wired network device." msgid "Configure the network bridge before using a wired network device."
msgstr "Ställ in nätverksbryggan innan du använder ett nätverksgränssnitt." msgstr "Ställ in nätverksbryggan innan du använder ett nätverksgränssnitt."
#: web.py:497 #: web.py:537
#, python-format #, python-format
msgid "Attached DaynaPORT to SCSI ID %(id_number)s" msgid "Attached DaynaPORT to SCSI ID %(id_number)s"
msgstr "Anslöt DaynaPORT till SCSI-id %(id_number)s" msgstr "Anslöt DaynaPORT till SCSI-id %(id_number)s"
#: web.py:545 #: web.py:585
#, python-format #, python-format
msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s" msgid "Attached %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr "Anslöt %(file_name)s till SCSI-id %(id_number)s LUN %(unit_number)s" msgstr "Anslöt %(file_name)s till SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:548 #: web.py:588
#, python-format #, python-format
msgid "" msgid ""
"The image file size %(file_size)s bytes is not a multiple of " "The image file size %(file_size)s bytes is not a multiple of "
"%(block_size)s. RaSCSI will ignore the trailing data. The image may be " "%(block_size)s. RaSCSI will ignore the trailing data. The image may be "
"corrupted, so proceed with caution." "corrupted, so proceed with caution."
msgstr "" msgstr ""
"Filstorleken %(file_size)s bytes är inte en multipel av %(block_size)s. " "Filstorleken %(file_size)s byte är inte en multipel av %(block_size)s. "
"RaSCSI ignorerar den överflödiga datan. Skivbilden är möjligen förstörd, " "RaSCSI ignorerar den överflödiga datan. Skivbilden är möjligen förstörd, "
"så var försiktig när du använder den." "så var försiktig när du använder den."
#: web.py:554 #: web.py:594
#, python-format #, python-format
msgid "" msgid ""
"Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN " "Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN "
@ -213,133 +213,127 @@ msgstr ""
"Kunde inte ansluta %(file_name)s till SCSI-id %(id_number)s LUN " "Kunde inte ansluta %(file_name)s till SCSI-id %(id_number)s LUN "
"%(unit_number)s" "%(unit_number)s"
#: web.py:568 #: web.py:608
msgid "Detached all SCSI devices" msgid "Detached all SCSI devices"
msgstr "Kopplade ifrån alla SCSI-enheter" msgstr "Kopplade ifrån alla SCSI-enheter"
#: web.py:585 #: web.py:625
#, python-format #, python-format
msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s" msgid "Detached SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr "Kopplade ifrån SCSI-id %(id_number)s LUN %(unit_number)s" msgstr "Kopplade ifrån SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:589 #: web.py:629
#, python-format #, python-format
msgid "" msgid "Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s"
"Failed to detach %(file_name)s from SCSI ID %(id_number)s LUN "
"%(unit_number)s"
msgstr "" msgstr ""
"Kunde ej koppla ifrån %(file_name)s från SCSI-id %(id_number)s LUN " "Kunde ej koppla ifrån SCSI-id %(id_number)s LUN %(unit_number)s"
"%(unit_number)s"
#: web.py:606 #: web.py:646
#, python-format #, python-format
msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s" msgid "Ejected SCSI ID %(id_number)s LUN %(unit_number)s"
msgstr "Utmatade SCSI-id %(id_number)s LUN %(unit_number)s" msgstr "Utmatade SCSI-id %(id_number)s LUN %(unit_number)s"
#: web.py:610 #: web.py:650
#, python-format #, python-format
msgid "" msgid "Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s"
"Failed to eject %(file_name)s from SCSI ID %(id_number)s LUN "
"%(unit_number)s"
msgstr "" msgstr ""
"Kunde ej utmata %(file_name)s från SCSI-id %(id_number)s LUN " "Kunde ej mata ut skiva från SCSI-id %(id_number)s LUN %(unit_number)s"
"%(unit_number)s"
#: web.py:633 #: web.py:673
msgid "DEVICE INFO" msgid "DEVICE INFO"
msgstr "ENHETSDATA" msgstr "ENHETSDATA"
#: web.py:635 #: web.py:675
#, python-format #, python-format
msgid "SCSI ID: %(id_number)s" msgid "SCSI ID: %(id_number)s"
msgstr "SCSI-id: %(id_number)s" msgstr "SCSI-id: %(id_number)s"
#: web.py:636 #: web.py:676
#, python-format #, python-format
msgid "LUN: %(unit_number)s" msgid "LUN: %(unit_number)s"
msgstr "LUN: %(unit_number)s" msgstr "LUN: %(unit_number)s"
#: web.py:637 #: web.py:677
#, python-format #, python-format
msgid "Type: %(device_type)s" msgid "Type: %(device_type)s"
msgstr "Typ: %(device_type)s" msgstr "Typ: %(device_type)s"
#: web.py:638 #: web.py:678
#, python-format #, python-format
msgid "Status: %(device_status)s" msgid "Status: %(device_status)s"
msgstr "Status: %(device_status)s" msgstr "Status: %(device_status)s"
#: web.py:639 #: web.py:679
#, python-format #, python-format
msgid "File: %(image_file)s" msgid "File: %(image_file)s"
msgstr "Fil: %(image_file)s" msgstr "Fil: %(image_file)s"
#: web.py:640 #: web.py:680
#, python-format #, python-format
msgid "Parameters: %(value)s" msgid "Parameters: %(value)s"
msgstr "Parametrar: %(value)s" msgstr "Parametrar: %(value)s"
#: web.py:641 #: web.py:681
#, python-format #, python-format
msgid "Vendor: %(value)s" msgid "Vendor: %(value)s"
msgstr "Tillverkare: %(value)s" msgstr "Tillverkare: %(value)s"
#: web.py:642 #: web.py:682
#, python-format #, python-format
msgid "Product: %(value)s" msgid "Product: %(value)s"
msgstr "Produkt: %(value)s" msgstr "Produkt: %(value)s"
#: web.py:643 #: web.py:683
#, python-format #, python-format
msgid "Revision: %(revision_number)s" msgid "Revision: %(revision_number)s"
msgstr "Revision: %(revision_number)s" msgstr "Revision: %(revision_number)s"
#: web.py:644 #: web.py:684
#, python-format #, python-format
msgid "Block Size: %(value)s bytes" msgid "Block Size: %(value)s bytes"
msgstr "Blockstorlek: %(value)s bytes" msgstr "Blockstorlek: %(value)s byte"
#: web.py:645 #: web.py:685
#, python-format #, python-format
msgid "Image Size: %(value)s bytes" msgid "Image Size: %(value)s bytes"
msgstr "Skivbildsstorlek: %(value)s bytes" msgstr "Skivbildsstorlek: %(value)s byte"
#: web.py:664 #: web.py:704
#, python-format #, python-format
msgid "Reserved SCSI ID %(id_number)s" msgid "Reserved SCSI ID %(id_number)s"
msgstr "Reserverat SCSI-id %(id_number)s" msgstr "Reserverat SCSI-id %(id_number)s"
#: web.py:667 #: web.py:707
#, python-format #, python-format
msgid "Failed to reserve SCSI ID %(id_number)s" msgid "Failed to reserve SCSI ID %(id_number)s"
msgstr "Kunde ej reservera SCSI-id %(id_number)s" msgstr "Kunde ej reservera SCSI-id %(id_number)s"
#: web.py:683 #: web.py:723
#, python-format #, python-format
msgid "Released the reservation for SCSI ID %(id_number)s" msgid "Released the reservation for SCSI ID %(id_number)s"
msgstr "Avreserverade SCSI-id %(id_number)s" msgstr "Avreserverade SCSI-id %(id_number)s"
#: web.py:686 #: web.py:726
#, python-format #, python-format
msgid "Failed to release the reservation for SCSI ID %(id_number)s" msgid "Failed to release the reservation for SCSI ID %(id_number)s"
msgstr "Kunde ej avreservera SCSI-id %(id_number)s" msgstr "Kunde ej avreservera SCSI-id %(id_number)s"
#: web.py:724 #: web.py:764
#, python-format #, python-format
msgid "Saved image as: %(file_name)s" msgid "Saved image as: %(file_name)s"
msgstr "Sparade bildfilen som %(file_name)s" msgstr "Sparade bildfilen som %(file_name)s"
#: web.py:726 #: web.py:766
#, python-format #, python-format
msgid "Failed to create CD-ROM image from %(url)s" msgid "Failed to create CD-ROM image from %(url)s"
msgstr "Kunde ej skapa CD-ROM-bildfil från %(url)s" msgstr "Kunde ej skapa CD-ROM-bildfil från %(url)s"
#: web.py:732 #: web.py:772
#, python-format #, python-format
msgid "Attached to SCSI ID %(id_number)s" msgid "Attached to SCSI ID %(id_number)s"
msgstr "Anslöt till SCSI-id %(id_number)s" msgstr "Anslöt till SCSI-id %(id_number)s"
#: web.py:735 #: web.py:775
#, python-format #, python-format
msgid "" msgid ""
"Failed to attach image to SCSI ID %(id_number)s. Try attaching it " "Failed to attach image to SCSI ID %(id_number)s. Try attaching it "
@ -348,55 +342,60 @@ msgstr ""
"Kunde ej ansluta bildfilen till SCSI-id %(id_number)s. Försök ansluta den" "Kunde ej ansluta bildfilen till SCSI-id %(id_number)s. Försök ansluta den"
" manuellt." " manuellt."
#: web.py:754 web.py:771 #: web.py:794 web.py:811
#, python-format #, python-format
msgid "Failed to download file from %(url)s" msgid "Failed to download file from %(url)s"
msgstr "Kunde ej ladda ner filen från %(url)s" msgstr "Kunde ej ladda ner filen från %(url)s"
#: web.py:802 #: web.py:842
msgid "The file already exists!" msgid "The file already exists!"
msgstr "Filen existerar redan!" msgstr "Filen existerar redan!"
#: web.py:810 #: web.py:850
msgid "Unable to write the file to disk!" msgid "Unable to write the file to disk!"
msgstr "Kunde ej skriva filen till skivan!" msgstr "Kunde ej skriva filen till skivan!"
#: web.py:821 #: web.py:865
msgid "Transferred file corrupted!" msgid "Transferred file corrupted!"
msgstr "Den överförda filen är skadad!" msgstr "Den överförda filen är skadad!"
#: web.py:827 #: web.py:871
msgid "File upload successful!" msgid "File upload successful!"
msgstr "Filen har laddas upp!" msgstr "Filen har laddas upp!"
#: web.py:870 #: web.py:914
#, python-format #, python-format
msgid "Image file deleted: %(file_name)s" msgid "Image file deleted: %(file_name)s"
msgstr "Skivbildfilen %(file_name)s har blivit raderad" msgstr "Filen %(file_name)s har blivit raderad"
#: web.py:900 #: web.py:944
#, python-format #, python-format
msgid "Image file renamed to: %(file_name)s" msgid "Image file renamed to: %(file_name)s"
msgstr "Skivbildfilen har blivit omdöpt till %(file_name)s" msgstr "Filen har blivit omdöpt till %(file_name)s"
#: web.py:937 #: web.py:981
msgid "Aborted unzip: File(s) with the same name already exists." msgid "Aborted unzip: File(s) with the same name already exists."
msgstr "Uppackning stoppad: En eller flera filer med samma namn existerar." msgstr "Uppackning stoppad: En eller flera filer med samma namn existerar redan."
#: web.py:939 #: web.py:983
msgid "Unzipped the following files:" msgid "Unzipped the following files:"
msgstr "Packade up dessa filer:" msgstr "Packade up dessa filer:"
#: web.py:943 #: web.py:987
#, python-format #, python-format
msgid "Properties file(s) have been moved to %(directory)s" msgid "Properties file(s) have been moved to %(directory)s"
msgstr "En eller flera egenskapsfiler har blivit flyttade till %(directory)s" msgstr "En eller flera egenskapsfiler har blivit flyttade till %(directory)s"
#: web.py:946 #: web.py:990
#, python-format #, python-format
msgid "Failed to unzip %(zip_file)s" msgid "Failed to unzip %(zip_file)s"
msgstr "Kunde ej packa up %(zip_file)s" msgstr "Kunde ej packa up %(zip_file)s"
#: web.py:1004
#, python-format
msgid "Changed Web Interface language to %(locale)s"
msgstr "Bytte webbgränssnittets språk till %(locale)s"
#: templates/base.html:4 #: templates/base.html:4
msgid "RaSCSI Control Page" msgid "RaSCSI Control Page"
msgstr "RaSCSI kontrollsida" msgstr "RaSCSI kontrollsida"
@ -514,7 +513,7 @@ msgid "Save as:"
msgstr "Spara som:" msgstr "Spara som:"
#: templates/drives.html:41 templates/drives.html:88 templates/drives.html:131 #: templates/drives.html:41 templates/drives.html:88 templates/drives.html:131
#: templates/index.html:531 #: templates/index.html:549
msgid "Create" msgid "Create"
msgstr "Skapa" msgstr "Skapa"
@ -557,7 +556,7 @@ msgid ""
"Save and load device configurations, stored as json files in " "Save and load device configurations, stored as json files in "
"<tt>%(config_dir)s</tt>" "<tt>%(config_dir)s</tt>"
msgstr "" msgstr ""
"Spara och ladda enhetskonfigurationer. Sparas som json-format i " "Spara och ladda enhetskonfigurationer. Sparas i json-format vid "
"<tt>%(config_dir)s</tt>" "<tt>%(config_dir)s</tt>"
#: templates/index.html:11 #: templates/index.html:11
@ -626,7 +625,7 @@ msgstr "Anslut"
#: templates/index.html:98 #: templates/index.html:98
msgid "Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!" msgid "Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!"
msgstr "Mata ut skiva? VARNING: På Mac OS, mata ut skivan i Finder istället!" msgstr "Mata ut skiva? VARNING: På Mac OS bör du mata ut skivan i Finder istället!"
#: templates/index.html:101 #: templates/index.html:101
msgid "Eject" msgid "Eject"
@ -678,8 +677,8 @@ msgid ""
"Manage image files in the active RaSCSI image directory: " "Manage image files in the active RaSCSI image directory: "
"<tt>%(directory)s</tt> with a scan depth of %(scan_depth)s." "<tt>%(directory)s</tt> with a scan depth of %(scan_depth)s."
msgstr "" msgstr ""
"Hantera filer i den aktiva skivbildsfilskatalogen: <tt>%(directory)s</tt>" "Hantera filer i den aktiva skivbildskatalogen: <tt>%(directory)s</tt> med"
" med hierarkiskt djup %(scan_depth)s." " hierarkiskt djup %(scan_depth)s."
#: templates/index.html:156 #: templates/index.html:156
#, python-format #, python-format
@ -721,9 +720,9 @@ msgstr "Packa upp"
#: templates/index.html:204 templates/index.html:218 #: templates/index.html:204 templates/index.html:218
msgid "Unzipping a single file..." msgid "Unzipping a single file..."
msgstr "Packar upp en enda fil..." msgstr "Packar upp endast en fil..."
#: templates/index.html:233 templates/index.html:530 #: templates/index.html:233 templates/index.html:390 templates/index.html:548
msgid "MB" msgid "MB"
msgstr "MB" msgstr "MB"
@ -763,7 +762,7 @@ msgid ""
"Emulates a SCSI DaynaPORT Ethernet Adapter. <a href=\"%(url)s\">Host " "Emulates a SCSI DaynaPORT Ethernet Adapter. <a href=\"%(url)s\">Host "
"drivers and configuration required</a>." "drivers and configuration required</a>."
msgstr "" msgstr ""
"Emulerar en SCSI DaynaPORT ethernet-adapter. <a href=\"%(url)s\">Kräver " "Emulerar ett SCSI DaynaPORT ethernet-gränssnitt. <a href=\"%(url)s\">Kräver "
"drivrutiner och inställningar</a>." "drivrutiner och inställningar</a>."
#: templates/index.html:306 #: templates/index.html:306
@ -799,14 +798,14 @@ msgstr "Gränssnitt:"
msgid "Static IP (optional):" msgid "Static IP (optional):"
msgstr "Statisk adress (tillval):" msgstr "Statisk adress (tillval):"
#: templates/index.html:329 templates/index.html:455 #: templates/index.html:329 templates/index.html:473
msgid "SCSI ID:" msgid "SCSI ID:"
msgstr "SCSI-id:" msgstr "SCSI-id:"
#: templates/index.html:343 #: templates/index.html:343
#, python-format #, python-format
msgid "Macproxy is running at %(ip_addr)s (default port 5000)" msgid "Macproxy is running at %(ip_addr)s (default port 5000)"
msgstr "Macproxy är tillgängligt på %(ip_addr)s (förvald port 5000)" msgstr "Macproxy är tillgängligt på %(ip_addr)s (vanligtvis port 5000)"
#: templates/index.html:345 #: templates/index.html:345
#, python-format #, python-format
@ -815,7 +814,7 @@ msgid ""
"vintage browser. It's not just for Macs!" "vintage browser. It's not just for Macs!"
msgstr "" msgstr ""
"Installera <a href=\"%(url)s\">Macproxy</a> och surfa på nätet med gamla " "Installera <a href=\"%(url)s\">Macproxy</a> och surfa på nätet med gamla "
"webbläsare. Den är inte bara för Macar!" "webbläsare. Den är inte bara till för Macar!"
#: templates/index.html:351 #: templates/index.html:351
msgid "Upload File" msgid "Upload File"
@ -838,56 +837,122 @@ msgid ""
msgstr "" msgstr ""
"Om RaSCSI inte känner igen en filtyp kan du försöka döpa om " "Om RaSCSI inte känner igen en filtyp kan du försöka döpa om "
"hårddiskbildfiler till '.hds', cd-bildfiler till '.iso', och utmatbara " "hårddiskbildfiler till '.hds', cd-bildfiler till '.iso', och utmatbara "
"bildfiler till '.hdr' innan du laddar upp den." "bildfiler till '.hdr' innan du laddar upp dem."
#: templates/index.html:356 #: templates/index.html:356
#, python-format #, python-format
msgid "Recognized file types: %(valid_file_suffix)s" msgid "Recognized file types: %(valid_file_suffix)s"
msgstr "Kända filtyper: %(valid_file_suffix)s" msgstr "Kända filtyper: %(valid_file_suffix)s"
#: templates/index.html:376
msgid "Drop files here to upload"
msgstr "Släpp filer här för att ladda upp"
#: templates/index.html:377
msgid "Your browser does not support drag'n'drop file uploads."
msgstr "Din webbläsare stöder ej filuppladdning via dra och släpp."
#: templates/index.html:378
msgid ""
"Please use the fallback form below to upload your files like in the olden"
" days."
msgstr ""
"Använd reservformuläret nedan för att ladda upp dina filer på gammaldags "
"vis."
#: templates/index.html:379
msgid "File is too big: {{filesize}}MB. Max filesize: {{maxFilesize}}MB."
msgstr ""
"Filen är för stor: {{filesize}}MB. Största möjliga storlek: "
"{{maxFilesize}}MB."
#: templates/index.html:380
msgid "You can't upload files of this type."
msgstr "Du kan ej ladda upp filer av den här typen."
#: templates/index.html:381
msgid "Server responded with code: {{statusCode}}"
msgstr "Servern svarade med kod: {{statusCode}}"
#: templates/index.html:382
msgid "Cancel upload"
msgstr "Avbryt uppladdning"
#: templates/index.html:383 #: templates/index.html:383
msgid "Download File to Images" msgid "Upload canceled."
msgstr "Ladda ner fil till skivbildsfilskatalogen" msgstr "Uppladdningen avbröts."
#: templates/index.html:384
msgid "Are you sure you want to cancel this upload?"
msgstr "Är du säker på att du vill avbryta uppladdningen?"
#: templates/index.html:385
msgid "Remove file"
msgstr "Radera fil"
#: templates/index.html:386 #: templates/index.html:386
msgid "You can not upload any more files."
msgstr "Du kan inte ladda upp några fler filer."
#: templates/index.html:388
msgid "TB"
msgstr "TB"
#: templates/index.html:389
msgid "GB"
msgstr "GB"
#: templates/index.html:391
msgid "KB"
msgstr "KB"
#: templates/index.html:392
msgid "b"
msgstr "b"
#: templates/index.html:401
msgid "Download File to Images"
msgstr "Ladda ner fil till skivbildskatalogen"
#: templates/index.html:404
#, python-format #, python-format
msgid "Given a URL, download that file to the <tt>%(directory)s</tt> directory." msgid "Given a URL, download that file to the <tt>%(directory)s</tt> directory."
msgstr "Ta ett url och ladda ner en fil till katalogen <tt>%(directory)s</tt>" msgstr "Ta en webbadress och ladda ner en fil till katalogen <tt>%(directory)s</tt>"
#: templates/index.html:394 templates/index.html:420 templates/index.html:464 #: templates/index.html:412 templates/index.html:438 templates/index.html:482
msgid "URL:" msgid "URL:"
msgstr "Url:" msgstr "Webbadress:"
#: templates/index.html:395 templates/index.html:421 templates/index.html:465 #: templates/index.html:413 templates/index.html:439 templates/index.html:483
msgid "URL" msgid "URL"
msgstr "Url" msgstr "Webbadress"
#: templates/index.html:396 templates/index.html:422 #: templates/index.html:414 templates/index.html:440
msgid "Download" msgid "Download"
msgstr "Ladda ner" msgstr "Ladda ner"
#: templates/index.html:396 #: templates/index.html:414
msgid "Downloading File to Images..." msgid "Downloading File to Images..."
msgstr "Laddar ner filen till skivbildsfilskatalogen..." msgstr "Laddar ner filen till skivbildskatalogen..."
#: templates/index.html:406 #: templates/index.html:424
msgid "Download File to AppleShare" msgid "Download File to AppleShare"
msgstr "Ladda ner fil till AppleShare" msgstr "Ladda ner fil till AppleShare"
#: templates/index.html:409 #: templates/index.html:427
#, python-format #, python-format
msgid "" msgid ""
"Given a URL, download that file to the <tt>%(directory)s</tt> directory " "Given a URL, download that file to the <tt>%(directory)s</tt> directory "
"and share it over AFP." "and share it over AFP."
msgstr "" msgstr ""
"Ta ett url och ladda ner en fil till katalogen <tt>%(directory)s</tt> och" "Ta en webbadress och ladda ner en fil till katalogen <tt>%(directory)s</tt> och"
" fildela den över AFP." " fildela den över AFP."
#: templates/index.html:410 #: templates/index.html:428
msgid "Manage the files you download here through AppleShare on your vintage Mac." msgid "Manage the files you download here through AppleShare on your vintage Mac."
msgstr "Hantera dessa filer via AppleShare på en klassisk Mac." msgstr "Hantera dessa filer via AppleShare på din gamla Mac."
#: templates/index.html:411 #: templates/index.html:429
#, python-format #, python-format
msgid "" msgid ""
"Requires <a href=\"%(url)s\">Netatalk</a> to be installed and configured " "Requires <a href=\"%(url)s\">Netatalk</a> to be installed and configured "
@ -896,25 +961,25 @@ msgstr ""
"Kräver att <a href=\"%(url)s\">Netatalk</a> är installerat och inställt " "Kräver att <a href=\"%(url)s\">Netatalk</a> är installerat och inställt "
"på lämpligt vis för ditt nätverk." "på lämpligt vis för ditt nätverk."
#: templates/index.html:422 #: templates/index.html:440
msgid "Downloading File to AppleShare..." msgid "Downloading File to AppleShare..."
msgstr "Laddar ner fil till AppleShare..." msgstr "Laddar ner fil till AppleShare..."
#: templates/index.html:429 #: templates/index.html:447
msgid "The AppleShare server is running. No active connections." msgid "The AppleShare server is running. No active connections."
msgstr "AppleShare-servern är aktiv. Inga klienter är anslutna." msgstr "AppleShare-servern är aktiv. Inga klienter är anslutna."
#: templates/index.html:431 #: templates/index.html:449
#, python-format #, python-format
msgid "%(value)d active AFP connection" msgid "%(value)d active AFP connection"
msgstr "%(value)d aktiv AFP-klient" msgstr "%(value)d aktiv AFP-klient"
#: templates/index.html:433 #: templates/index.html:451
#, python-format #, python-format
msgid "%(value)d active AFP connections" msgid "%(value)d active AFP connections"
msgstr "%(value)d aktiva AFP-klienter" msgstr "%(value)d aktiva AFP-klienter"
#: templates/index.html:436 #: templates/index.html:454
#, python-format #, python-format
msgid "" msgid ""
"Install <a href=\"%(url)s\">Netatalk</a> to use the AppleShare File " "Install <a href=\"%(url)s\">Netatalk</a> to use the AppleShare File "
@ -923,56 +988,56 @@ msgstr ""
"Installera <a href=\"%(url)s\">Netatalk</a> innan du kan använda " "Installera <a href=\"%(url)s\">Netatalk</a> innan du kan använda "
"AppleShare-fildelning." "AppleShare-fildelning."
#: templates/index.html:443 #: templates/index.html:461
msgid "Download File and Create CD-ROM image" msgid "Download File and Create CD-ROM image"
msgstr "Ladda ner fil och skapa en cd-bildfil" msgstr "Ladda ner fil och skapa en cd-bildfil"
#: templates/index.html:446 #: templates/index.html:464
msgid "" msgid ""
"Create an ISO file system CD-ROM image with the downloaded file, and " "Create an ISO file system CD-ROM image with the downloaded file, and "
"mount it on the given SCSI ID." "mount it on the given SCSI ID."
msgstr "" msgstr ""
"Skapar en cd-bildfil med ISO-filsystem som innehåller den nedladdade " "Skapar en cd-bildfil med ISO-filsystem som innehåller den nedladdade "
"filen. Sedan ansluts den till det angivna SCSI-idt." "filen. Sedan ansluts den till det angivna SCSI-id:t."
#: templates/index.html:447 #: templates/index.html:465
msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX." msgid "HFS is for Mac OS, Joliet for Windows, and Rock Ridge for POSIX."
msgstr "HFS är för Mac OS, Joliet för Windows, samt Rock Ridge för POSIX." msgstr "HFS är för Mac OS, Joliet för Windows, samt Rock Ridge för POSIX."
#: templates/index.html:448 #: templates/index.html:466
#, python-format #, python-format
msgid "On Mac OS, a <a href=\"%(url)s\">compatible CD-ROM driver</a> is required." msgid "On Mac OS, a <a href=\"%(url)s\">compatible CD-ROM driver</a> is required."
msgstr "På Mac OS krävs <a href=\"%(url)s\">kompatibla cd-drivrutiner</a>." msgstr "På Mac OS krävs <a href=\"%(url)s\">kompatibla cd-drivrutiner</a>."
#: templates/index.html:449 #: templates/index.html:467
msgid "" msgid ""
"If the downloaded file is a zip archive, we will attempt to unzip it and " "If the downloaded file is a zip archive, we will attempt to unzip it and "
"store the resulting files." "store the resulting files."
msgstr "" msgstr ""
"Om den nedladdade filen är en zip-fil så försöker vi packa up den och " "Om den nedladdade filen är en zip-fil så försöker vi packa up den och "
"spara de uppackade filerna på cd-bildfilen" "spara de uppackade filerna på cd-bildfilen."
#: templates/index.html:466 templates/index.html:511 #: templates/index.html:484 templates/index.html:529
msgid "Type:" msgid "Type:"
msgstr "Typ:" msgstr "Typ:"
#: templates/index.html:487 #: templates/index.html:505
msgid "Download and Mount CD-ROM image" msgid "Download and Mount CD-ROM image"
msgstr "Ladda ner och mata in cd-bildfil" msgstr "Ladda ner och mata in cd-bildfil"
#: templates/index.html:487 #: templates/index.html:505
msgid "Downloading File and generating CD-ROM image..." msgid "Downloading File and generating CD-ROM image..."
msgstr "Laddar ner fil och tillverkar cd-bildfil..." msgstr "Laddar ner fil och tillverkar cd-bildfil..."
#: templates/index.html:497 #: templates/index.html:515
msgid "Create Empty Disk Image File" msgid "Create Empty Disk Image File"
msgstr "Skapa en tom skivbilsdfil" msgstr "Skapa en tom skivbilsdfil"
#: templates/index.html:500 #: templates/index.html:518
msgid "The Generic image type is recommended for most computer platforms." msgid "The Generic image type is recommended for most computer platforms."
msgstr "Bildfilsformatet 'Generic' är rekommederad för de flesta datorsystem." msgstr "Bildfilsformatet 'Generic' är rekommederad för de flesta datorsystem."
#: templates/index.html:501 #: templates/index.html:519
msgid "" msgid ""
"APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI " "APPLE GENUINE (.hda) and NEC GENUINE (.hdn) image types will make RaSCSI "
"behave as a particular drive type that are recognized by Mac and PC98 " "behave as a particular drive type that are recognized by Mac and PC98 "
@ -982,52 +1047,52 @@ msgstr ""
"RaSCSI beter sig som en typ av hårddisk som Macar och PC98-datorer känner" "RaSCSI beter sig som en typ av hårddisk som Macar och PC98-datorer känner"
" igen." " igen."
#: templates/index.html:502 #: templates/index.html:520
msgid "" msgid ""
"SASI images should only be used on the original Sharp X68000, or other " "SASI images should only be used on the original Sharp X68000, or other "
"legacy systems that utilize this pre-SCSI standard." "legacy systems that utilize this pre-SCSI standard."
msgstr "" msgstr ""
"Bildfilsformatet SASI bör endast användas för den första Sharp " "Bildfilsformatet SASI bör endast användas med tidiga Sharp "
"X68000-modellen eller andra riktigt gamla system som använder denna " "X68000-modeller, eller andra riktigt gamla system som använder denna "
"föregångare till SCSI." "föregångare till SCSI."
#: templates/index.html:509 #: templates/index.html:527
msgid "File Name:" msgid "File Name:"
msgstr "Filnamn:" msgstr "Filnamn:"
#: templates/index.html:510 #: templates/index.html:528
msgid "File Name" msgid "File Name"
msgstr "Filnamn" msgstr "Filnamn"
#: templates/index.html:514 #: templates/index.html:532
msgid "SCSI Hard Disk image (Generic) [.hds]" msgid "SCSI Hard Disk image (Generic) [.hds]"
msgstr "SCSI-hårddisk (Generic) [.hds]" msgstr "SCSI-hårddisk (Generic) [.hds]"
#: templates/index.html:517 #: templates/index.html:535
msgid "SCSI Hard Disk image (APPLE GENUINE) [.hda]" msgid "SCSI Hard Disk image (APPLE GENUINE) [.hda]"
msgstr "SCSI-hårddisk (APPLE GENUINE) [.hda]" msgstr "SCSI-hårddisk (APPLE GENUINE) [.hda]"
#: templates/index.html:520 #: templates/index.html:538
msgid "SCSI Hard Disk image (NEC GENUINE) [.hdn]" msgid "SCSI Hard Disk image (NEC GENUINE) [.hdn]"
msgstr "SCSI-hårddisk (NEC GENUINE) [.hdn]" msgstr "SCSI-hårddisk (NEC GENUINE) [.hdn]"
#: templates/index.html:523 #: templates/index.html:541
msgid "SCSI Removable Media Disk image (Generic) [.hdr]" msgid "SCSI Removable Media Disk image (Generic) [.hdr]"
msgstr "SCSI utmatbart medium (Generic) [.hdr]" msgstr "SCSI utmatbart medium (Generic) [.hdr]"
#: templates/index.html:526 #: templates/index.html:544
msgid "SASI Hard Disk image (Legacy) [.hdf]" msgid "SASI Hard Disk image (Legacy) [.hdf]"
msgstr "SASI-hårddisk (föråldrat) [.hdf]" msgstr "SASI-hårddisk (föråldrat) [.hdf]"
#: templates/index.html:529 #: templates/index.html:547
msgid "Size:" msgid "Size:"
msgstr "Storlek:" msgstr "Storlek:"
#: templates/index.html:541 #: templates/index.html:559
msgid "Create Named Drive" msgid "Create Named Drive"
msgstr "Skapa benämnd skiva" msgstr "Skapa benämnd skiva"
#: templates/index.html:544 #: templates/index.html:562
msgid "" msgid ""
"Create pairs of images and properties files from a list of real-life " "Create pairs of images and properties files from a list of real-life "
"drives." "drives."
@ -1035,7 +1100,7 @@ msgstr ""
"Skapar ett par av skivbilds- och egenskapsfiler från en lista av verkliga" "Skapar ett par av skivbilds- och egenskapsfiler från en lista av verkliga"
" enheter." " enheter."
#: templates/index.html:545 #: templates/index.html:563
msgid "" msgid ""
"This will make RaSCSI use certain vendor strings and block sizes that may" "This will make RaSCSI use certain vendor strings and block sizes that may"
" improve compatibility with certain systems." " improve compatibility with certain systems."
@ -1043,86 +1108,103 @@ msgstr ""
"På så vis kommer RaSCSI använda vissa tillverkarattribut och " "På så vis kommer RaSCSI använda vissa tillverkarattribut och "
"blockstorlekar som kan hjälpa till med kompatibilitet." "blockstorlekar som kan hjälpa till med kompatibilitet."
#: templates/index.html:548 #: templates/index.html:566
msgid "Create a named disk image that mimics real-life drives" msgid "Create a named disk image that mimics real-life drives"
msgstr "Skapa en benämnd skivbildfil som låstas vara en riktig enhet" msgstr "Skapa en benämnd skivbildfil som låstas vara en riktig enhet"
#: templates/index.html:554 #: templates/index.html:572
msgid "Logging" msgid "Logging"
msgstr "Loggar" msgstr "Loggar"
#: templates/index.html:557 #: templates/index.html:575
msgid "Fetch a certain number of lines of system logs with the given scope." msgid "Fetch a certain number of lines of system logs with the given scope."
msgstr "Skaffar ett visst antal loggar för en viss systemprocess." msgstr "Skaffar ett visst antal loggar för en viss systemprocess."
#: templates/index.html:564 #: templates/index.html:582
msgid "Log Lines:" msgid "Log Lines:"
msgstr "Antal loggar:" msgstr "Antal loggar:"
#: templates/index.html:566 #: templates/index.html:584
msgid "Scope:" msgid "Scope:"
msgstr "Process:" msgstr "Process:"
#: templates/index.html:578 #: templates/index.html:596
msgid "Show Logs" msgid "Show Logs"
msgstr "Skaffa loggar" msgstr "Skaffa loggar"
#: templates/index.html:588 #: templates/index.html:606
msgid "Server Log Level" msgid "Server Log Level"
msgstr "Serverns loggnivå" msgstr "Serverns loggnivå"
#: templates/index.html:591 #: templates/index.html:609
msgid "Change the log level of the RaSCSI backend process." msgid "Change the log level of the RaSCSI backend process."
msgstr "Ändra loggnivån för RaSCSI-servern" msgstr "Byt loggnivån för RaSCSI-servern"
#: templates/index.html:592 #: templates/index.html:610
msgid "The current dropdown selection indicates the active log level." msgid "The current dropdown selection indicates the active log level."
msgstr "Det nuvarande valet i rullgardinsmenyn påvisar aktiv loggnivå." msgstr "Det nuvarande valet i rullgardinsmenyn påvisar aktiv loggnivå."
#: templates/index.html:599 #: templates/index.html:617
msgid "Log Level:" msgid "Log Level:"
msgstr "Loggnivå:" msgstr "Loggnivå:"
#: templates/index.html:607 #: templates/index.html:625
msgid "Set Log Level" msgid "Set Log Level"
msgstr "Ställ in loggnivå" msgstr "Byt loggnivå"
#: templates/index.html:617 #: templates/index.html:635
msgid "Language"
msgstr "Språk"
#: templates/index.html:638
msgid "Change the Web Interface language."
msgstr "Byt webbgränssnittets språk."
#: templates/index.html:645
msgid "Language:"
msgstr "Språk:"
#: templates/index.html:653
msgid "Change Language"
msgstr "Byt språk"
#: templates/index.html:663
msgid "Raspberry Pi Operations" msgid "Raspberry Pi Operations"
msgstr "Raspberry Pi-kommandon" msgstr "Raspberry Pi-kommandon"
#: templates/index.html:620 #: templates/index.html:666
msgid "Reboot or shut down the Raspberry Pi that RaSCSI is running on." msgid "Reboot or shut down the Raspberry Pi that RaSCSI is running on."
msgstr "Starta om eller stäng av Raspberry Pi-systemet som RaSCSI körs på." msgstr "Starta om eller stäng av Raspberry Pi-systemet som RaSCSI körs på."
#: templates/index.html:621 #: templates/index.html:667
msgid "" msgid ""
"IMPORTANT: Always shut down the Pi before turning off the power. Failing " "IMPORTANT: Always shut down the Pi before turning off the power. Failing "
"to do so may lead to data loss." "to do so may lead to data loss."
msgstr "" msgstr ""
"VIKTIGT: Stäng alltid av Pi-systemet innan du stänger av strömmen. Det " "VIKTIGT: Stäng alltid av Pi-systemet innan du kopplar ur strömmen. På "
"finns risk för dataförlust." "så vis undviker du risken att förlora data."
#: templates/index.html:627 #: templates/index.html:673
msgid "Reboot the Raspberry Pi?" msgid "Reboot the Raspberry Pi?"
msgstr "Vill du starta om din Raspberry Pi?" msgstr "Vill du starta om din Raspberry Pi?"
#: templates/index.html:627 #: templates/index.html:673
msgid "Rebooting the Raspberry Pi..." msgid "Rebooting the Raspberry Pi..."
msgstr "Startar om Raspberry Pi..." msgstr "Startar om Raspberry Pi..."
#: templates/index.html:628 #: templates/index.html:674
msgid "Reboot Raspberry Pi" msgid "Reboot Raspberry Pi"
msgstr "Starta om Raspberry Pi" msgstr "Starta om Raspberry Pi"
#: templates/index.html:632 #: templates/index.html:678
msgid "Shut down the Raspberry Pi?" msgid "Shut down the Raspberry Pi?"
msgstr "Vill du stänga av din Raspberry Pi?" msgstr "Vill du stänga av din Raspberry Pi?"
#: templates/index.html:632 #: templates/index.html:678
msgid "Shutting down the Raspberry Pi..." msgid "Shutting down the Raspberry Pi..."
msgstr "Stänger av Raspberry Pi..." msgstr "Stänger av Raspberry Pi..."
#: templates/index.html:633 #: templates/index.html:679
msgid "Shut Down Raspberry Pi" msgid "Shut Down Raspberry Pi"
msgstr "Stäng av Raspberry Pi" msgstr "Stäng av Raspberry Pi"

View File

@ -4,9 +4,9 @@ Module for the Flask app rendering and endpoints
import logging import logging
import argparse import argparse
from sys import argv
from pathlib import Path from pathlib import Path
from functools import wraps from functools import wraps
from hashlib import md5
from flask import ( from flask import (
Flask, Flask,
@ -93,10 +93,13 @@ def get_locale():
try: try:
language = session["language"] language = session["language"]
except KeyError: except KeyError:
language = None language = ""
if language is not None: logging.warning("The default locale could not be detected. Falling back to English.")
if language:
return language return language
return request.accept_languages.best_match(LANGUAGES) # Hardcoded fallback to "en" when the user agent does not send an accept-language header
language = request.accept_languages.best_match(LANGUAGES) or "en"
return language
def get_supported_locales(): def get_supported_locales():
@ -115,7 +118,13 @@ def index():
Sets up data structures for and renders the index page Sets up data structures for and renders the index page
""" """
if not is_token_auth()["status"] and not APP.config["TOKEN"]: if not is_token_auth()["status"] and not APP.config["TOKEN"]:
abort(403, _(u"RaSCSI is password protected. Start the Web Interface with the --password parameter.")) abort(
403,
_(
u"RaSCSI is password protected. "
u"Start the Web Interface with the --password parameter."
),
)
locales = get_supported_locales() locales = get_supported_locales()
server_info = get_server_info() server_info = get_server_info()
@ -211,7 +220,13 @@ def drive_list():
return redirect(url_for("index")) return redirect(url_for("index"))
conf = process["conf"] conf = process["conf"]
else: else:
flash(_("Could not read drive properties from %(properties_file)s", properties_file=drive_properties), "error") flash(
_(
"Could not read drive properties from %(properties_file)s",
properties_file=drive_properties,
),
"error",
)
return redirect(url_for("index")) return redirect(url_for("index"))
hd_conf = [] hd_conf = []
@ -275,7 +290,13 @@ def login():
if authenticate(str(username), str(password)): if authenticate(str(username), str(password)):
session["username"] = request.form["username"] session["username"] = request.form["username"]
return redirect(url_for("index")) return redirect(url_for("index"))
flash(_(u"You must log in with credentials for a user in the '%(group)s' group", group=AUTH_GROUP), "error") flash(
_(
u"You must log in with credentials for a user in the '%(group)s' group",
group=AUTH_GROUP,
),
"error",
)
return redirect(url_for("index")) return redirect(url_for("index"))
@ -413,7 +434,7 @@ def config_load():
flash(process['msg'], "error") flash(process['msg'], "error")
return redirect(url_for("index")) return redirect(url_for("index"))
elif "delete" in request.form: if "delete" in request.form:
process = delete_file(f"{CFG_DIR}/{file_name}") process = delete_file(f"{CFG_DIR}/{file_name}")
if process["status"]: if process["status"]:
flash(process["msg"]) flash(process["msg"])
@ -609,8 +630,8 @@ def detach():
id_number=scsi_id, unit_number=unit)) id_number=scsi_id, unit_number=unit))
return redirect(url_for("index")) return redirect(url_for("index"))
flash(_(u"Failed to detach %(file_name)s from SCSI ID %(id_number)s LUN %(unit_number)s", flash(_(u"Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit), "error") id_number=scsi_id, unit_number=unit), "error")
flash(process["msg"], "error") flash(process["msg"], "error")
return redirect(url_for("index")) return redirect(url_for("index"))
@ -630,8 +651,8 @@ def eject():
id_number=scsi_id, unit_number=unit)) id_number=scsi_id, unit_number=unit))
return redirect(url_for("index")) return redirect(url_for("index"))
flash(_(u"Failed to eject %(file_name)s from SCSI ID %(id_number)s LUN %(unit_number)s", flash(_(u"Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s",
file_name=file_name, id_number=scsi_id, unit_number=unit), "error") id_number=scsi_id, unit_number=unit), "error")
flash(process["msg"], "error") flash(process["msg"], "error")
return redirect(url_for("index")) return redirect(url_for("index"))
@ -769,7 +790,7 @@ def download_img():
""" """
url = request.form.get("url") url = request.form.get("url")
server_info = get_server_info() server_info = get_server_info()
process = download_to_dir(url, server_info["image_dir"]) process = download_to_dir(url, server_info["image_dir"], Path(url).name)
if process["status"]: if process["status"]:
flash(process["msg"]) flash(process["msg"])
return redirect(url_for("index")) return redirect(url_for("index"))
@ -786,7 +807,19 @@ def download_afp():
Downloads a remote file onto the AFP shared dir on the Pi Downloads a remote file onto the AFP shared dir on the Pi
""" """
url = request.form.get("url") url = request.form.get("url")
process = download_to_dir(url, AFP_DIR) file_name = Path(url).name
# Shorten file names longer than 31 chars, the HFS upper limit
# Append a hash of the removed piece of the file name to make it unique
# The format of the hash is a # followed by the first four symbols of the md4 hash
if len(file_name) > 31:
chars_to_cut = len(file_name) - 31
file_name_stem = Path(file_name).stem
file_name_suffix = Path(file_name).suffix
discarded_portion = file_name_stem[:-abs(chars_to_cut)]
stem_remainder = file_name_stem[:(26 - len(file_name_suffix))]
appendix_hash = (md5(discarded_portion.encode("utf-8"))).hexdigest().upper()
file_name = stem_remainder + "#" + appendix_hash[:4] + file_name_suffix
process = download_to_dir(url, AFP_DIR, file_name)
if process["status"]: if process["status"]:
flash(process["msg"]) flash(process["msg"])
return redirect(url_for("index")) return redirect(url_for("index"))
@ -837,10 +870,14 @@ def upload_file():
if current_chunk + 1 == total_chunks: if current_chunk + 1 == total_chunks:
# Validate the resulting file size after writing the last chunk # Validate the resulting file size after writing the last chunk
if path.getsize(save_path) != int(request.form["dztotalfilesize"]): if path.getsize(save_path) != int(request.form["dztotalfilesize"]):
log.error("Finished transferring %s, " log.error(
"but it has a size mismatch with the original file." "Finished transferring %s, "
"Got %s but we expected %s.", "but it has a size mismatch with the original file. "
file_object.filename, path.getsize(save_path), request.form['dztotalfilesize']) "Got %s but we expected %s.",
file_object.filename,
path.getsize(save_path),
request.form['dztotalfilesize'],
)
return make_response(_(u"Transferred file corrupted!"), 500) return make_response(_(u"Transferred file corrupted!"), 500)
log.info("File %s has been uploaded successfully", file_object.filename) log.info("File %s has been uploaded successfully", file_object.filename)
@ -973,6 +1010,9 @@ def unzip():
@APP.route("/language", methods=["POST"]) @APP.route("/language", methods=["POST"])
def change_language(): def change_language():
"""
Changes the session language locale and refreshes the Flask app context
"""
locale = request.form.get("locale") locale = request.form.get("locale")
session["language"] = locale session["language"] = locale
refresh() refresh()
@ -1013,9 +1053,9 @@ if __name__ == "__main__":
action="store", action="store",
help="Token password string for authenticating with RaSCSI", help="Token password string for authenticating with RaSCSI",
) )
args = parser.parse_args() arguments = parser.parse_args()
APP.config["TOKEN"] = args.password APP.config["TOKEN"] = arguments.password
import bjoern import bjoern
print("Serving rascsi-web...") print("Serving rascsi-web...")
bjoern.run(APP, "0.0.0.0", args.port) bjoern.run(APP, "0.0.0.0", arguments.port)